use core::marker::PhantomPinned;
#[cfg(all(windows, feature = "etw"))]
use core::cell::UnsafeCell;
#[cfg(all(windows, feature = "etw"))]
use core::sync::atomic;
use crate::descriptors::EventDataDescriptor;
use crate::descriptors::EventDescriptor;
use crate::enums::Level;
use crate::guid::Guid;
pub enum NativeImplementation {
Other,
Windows,
}
pub const NATIVE_IMPLEMENTATION: NativeImplementation = if cfg!(all(windows, feature = "etw")) {
NativeImplementation::Windows
} else {
NativeImplementation::Other
};
pub type ProviderEnableCallback = fn(
source_id: &Guid,
event_control_code: u32,
level: Level,
match_any_keyword: u64,
match_all_keyword: u64,
filter_data: usize,
callback_context: usize,
);
#[cfg(all(windows, feature = "etw"))]
type OuterEnableCallback = unsafe extern "system" fn(
source_id: &Guid,
event_control_code: u32,
level: u8,
match_any_keyword: u64,
match_all_keyword: u64,
filter_data: usize,
outer_context: usize, );
pub struct ProviderContext {
_pinned: PhantomPinned,
#[cfg(all(windows, feature = "etw"))]
cell: UnsafeCell<ProviderContextInner>,
}
impl ProviderContext {
pub fn activity_id_control(_control_code: u32, _activity_id: &mut Guid) -> u32 {
let result;
#[cfg(not(all(windows, feature = "etw")))]
{
result = 50; }
#[cfg(all(windows, feature = "etw"))]
{
result = unsafe { EventActivityIdControl(_control_code, _activity_id) };
}
return result;
}
pub const fn new() -> ProviderContext {
return ProviderContext {
_pinned: PhantomPinned,
#[cfg(all(windows, feature = "etw"))]
cell: UnsafeCell::new(ProviderContextInner::new()),
};
}
pub const fn reg_handle(&self) -> u64 {
let result;
#[cfg(not(all(windows, feature = "etw")))]
{
result = 0;
}
#[cfg(all(windows, feature = "etw"))]
{
let inner_ptr: *const ProviderContextInner = self.cell.get();
let inner = unsafe { &*inner_ptr };
result = inner.reg_handle;
}
return result;
}
#[inline(always)]
pub const fn enabled(&self, _level: Level, _keyword: u64) -> bool {
let result;
#[cfg(not(all(windows, feature = "etw")))]
{
result = false;
}
#[cfg(all(windows, feature = "etw"))]
{
let inner_ptr: *const ProviderContextInner = self.cell.get();
let inner = unsafe { &*inner_ptr };
result = (_level.0 as i32) <= inner.level && inner.enabled_keyword(_keyword);
}
return result;
}
pub fn unregister(&self) -> u32 {
let result;
#[cfg(not(all(windows, feature = "etw")))]
{
result = 0;
}
#[cfg(all(windows, feature = "etw"))]
{
let inner_ptr: *mut ProviderContextInner = self.cell.get();
let inner_mut = unsafe { &mut *inner_ptr };
result = inner_mut.unregister();
}
return result;
}
pub unsafe fn register(
&self,
_provider_id: &Guid,
_callback_fn: Option<ProviderEnableCallback>,
_callback_context: usize,
) -> u32 {
let result;
#[cfg(not(all(windows, feature = "etw")))]
{
result = 0;
}
#[cfg(all(windows, feature = "etw"))]
{
result = { &mut *self.cell.get() }.register(
_provider_id,
_callback_fn,
_callback_context);
}
return result;
}
pub fn set_information(&self, _information_class: u32, _information: &[u8]) -> u32 {
let result;
#[cfg(not(all(windows, feature = "etw")))]
{
result = 0;
}
#[cfg(all(windows, feature = "etw"))]
{
result = unsafe {
EventSetInformation(
self.reg_handle(),
_information_class,
_information.as_ptr(),
_information.len() as u32,
)
};
}
return result;
}
pub fn write_transfer(
&self,
_descriptor: &EventDescriptor,
_activity_id: Option<&[u8; 16]>,
_related_id: Option<&[u8; 16]>,
_data: &[EventDataDescriptor],
) -> u32 {
let result;
#[cfg(not(all(windows, feature = "etw")))]
{
result = 0;
}
#[cfg(all(windows, feature = "etw"))]
{
result = unsafe {
EventWriteTransfer(
self.reg_handle(),
_descriptor,
_activity_id,
_related_id,
_data.len() as u32,
_data.as_ptr(),
)
};
}
return result;
}
}
unsafe impl Sync for ProviderContext {}
impl Default for ProviderContext {
fn default() -> Self {
Self::new()
}
}
impl Drop for ProviderContext {
fn drop(&mut self) {
self.unregister();
}
}
#[cfg(all(windows, feature = "etw"))]
struct ProviderContextInner {
level: i32, busy: atomic::AtomicBool,
reg_handle: u64,
keyword_any: u64,
keyword_all: u64,
callback_fn: Option<ProviderEnableCallback>,
callback_context: usize,
}
#[cfg(all(windows, feature = "etw"))]
impl ProviderContextInner {
const fn new() -> Self {
return Self {
level: -1,
busy: atomic::AtomicBool::new(false),
reg_handle: 0,
keyword_any: 0,
keyword_all: 0,
callback_fn: None,
callback_context: 0,
};
}
const fn enabled_keyword(&self, keyword: u64) -> bool {
return keyword == 0
|| ((keyword & self.keyword_any) != 0
&& (keyword & self.keyword_all) == self.keyword_all);
}
fn unregister(&mut self) -> u32 {
let result;
let was_busy = self.busy.swap(true, atomic::Ordering::Acquire);
if was_busy {
result = 0;
} else {
if self.reg_handle == 0 {
result = 0;
} else {
result = unsafe { EventUnregister(self.reg_handle) };
self.level = -1;
self.reg_handle = 0;
}
self.busy.swap(false, atomic::Ordering::Release);
}
return result;
}
fn register(
&mut self,
provider_id: &Guid,
callback_fn: Option<ProviderEnableCallback>,
callback_context: usize,
) -> u32 {
let was_busy = self.busy.swap(true, atomic::Ordering::Acquire);
if was_busy {
panic!("provider.register called simultaneously with another call to register or unregister.");
}
if self.reg_handle != 0 {
self.busy.swap(false, atomic::Ordering::Relaxed);
panic!("provider.register called when provider is already registered");
}
self.callback_fn = callback_fn;
self.callback_context = callback_context;
let self_ptr: *mut Self = self;
let result = unsafe {
EventRegister(
provider_id,
Self::outer_callback,
self_ptr as usize,
&mut self.reg_handle,
)
};
self.busy.swap(false, atomic::Ordering::Release);
return result;
}
#[cfg(all(windows, feature = "etw"))]
fn outer_callback_impl(
&mut self,
source_id: &Guid,
event_control_code: u32,
level: u8,
match_any_keyword: u64,
match_all_keyword: u64,
filter_data: usize,
) {
match event_control_code {
0 => {
self.level = -1;
}
1 => {
self.level = level as i32;
self.keyword_any = match_any_keyword;
self.keyword_all = match_all_keyword;
}
_ => {}
}
if let Some(callback_fn) = self.callback_fn {
callback_fn(
source_id,
event_control_code,
Level(level),
match_any_keyword,
match_all_keyword,
filter_data,
self.callback_context,
);
}
}
unsafe extern "system" fn outer_callback(
source_id: &Guid,
event_control_code: u32,
level: u8,
match_any_keyword: u64,
match_all_keyword: u64,
filter_data: usize,
outer_context: usize,
) {
(*(outer_context as *mut Self)).outer_callback_impl(
source_id,
event_control_code,
level,
match_any_keyword,
match_all_keyword,
filter_data,
);
}
}
#[cfg(all(windows, feature = "etw"))]
extern "system" {
fn EventUnregister(reg_handle: u64) -> u32;
fn EventRegister(
provider_id: &Guid,
outer_callback: OuterEnableCallback,
outer_context: usize,
reg_handle: &mut u64,
) -> u32;
fn EventSetInformation(
reg_handle: u64,
information_class: u32,
information: *const u8,
information_length: u32,
) -> u32;
fn EventWriteTransfer(
reg_handle: u64,
descriptor: &EventDescriptor,
activity_id: Option<&[u8; 16]>,
related_id: Option<&[u8; 16]>,
data_count: u32,
data: *const EventDataDescriptor,
) -> u32;
fn EventActivityIdControl(control_code: u32, activity_id: &mut Guid) -> u32;
}