win_etw_provider/
provider.rs

1use crate::guid::GUID;
2use crate::Level;
3use crate::{Error, EventDataDescriptor};
4use alloc::boxed::Box;
5use core::convert::TryFrom;
6use core::pin::Pin;
7use core::ptr::null;
8use core::sync::atomic::{AtomicU8, Ordering::SeqCst};
9#[cfg(target_os = "windows")]
10use windows_sys::Win32::System::Diagnostics::Etw::{EventProviderSetTraits, REGHANDLE};
11
12#[cfg(target_os = "windows")]
13use win_support::*;
14
15/// Generates a new activity ID.
16///
17/// This function is only implemented on Windows. On other platforms, it will always return `Err`.
18pub fn new_activity_id() -> Result<GUID, Error> {
19    #[cfg(target_os = "windows")]
20    {
21        win_support::new_activity_id()
22    }
23
24    #[cfg(not(target_os = "windows"))]
25    {
26        Err(Error::NotSupported)
27    }
28}
29
30/// Gets the current activity ID.
31///
32/// This function is only implemented on Windows. On other platforms, it will always return `Err`.
33pub fn get_current_thread_activity_id() -> Result<GUID, Error> {
34    #[cfg(target_os = "windows")]
35    {
36        unsafe {
37            let mut guid: windows_sys::core::GUID = core::mem::zeroed();
38            let error = EventActivityIdControl(EVENT_ACTIVITY_CTRL_GET_ID, &mut guid);
39            if error == 0 {
40                Ok(guid.into())
41            } else {
42                Err(Error::WindowsError(error))
43            }
44        }
45    }
46
47    #[cfg(not(target_os = "windows"))]
48    {
49        Err(Error::NotSupported)
50    }
51}
52
53/// Describes the functions needed for an event provider backend. This is an implementation
54/// detail, and should not be used directly by applications.
55pub trait Provider {
56    /// Writes one event.
57    fn write(
58        &self,
59        options: Option<&crate::EventOptions>,
60        descriptor: &EventDescriptor,
61        data: &[EventDataDescriptor<'_>],
62    );
63
64    /// Checks whether the event provider is enabled.
65    fn is_enabled(&self, level: u8, keyword: u64) -> bool;
66
67    /// Checks whether a specific event is enabled.
68    fn is_event_enabled(&self, event_descriptor: &EventDescriptor) -> bool;
69}
70
71/// Implements `Provider` by discarding all events.
72pub struct NullProvider;
73
74impl Provider for NullProvider {
75    fn write(
76        &self,
77        _options: Option<&crate::EventOptions>,
78        _descriptor: &EventDescriptor,
79        _data: &[EventDataDescriptor<'_>],
80    ) {
81    }
82
83    fn is_enabled(&self, _level: u8, _keyword: u64) -> bool {
84        false
85    }
86    fn is_event_enabled(&self, _event_descriptor: &EventDescriptor) -> bool {
87        false
88    }
89}
90
91impl<T: Provider> Provider for Option<T> {
92    fn write(
93        &self,
94        options: Option<&crate::EventOptions>,
95        descriptor: &EventDescriptor,
96        data: &[EventDataDescriptor<'_>],
97    ) {
98        if let Some(p) = self {
99            p.write(options, descriptor, data);
100        }
101    }
102
103    fn is_enabled(&self, level: u8, keyword: u64) -> bool {
104        match self {
105            Some(p) => p.is_enabled(level, keyword),
106            None => false,
107        }
108    }
109    fn is_event_enabled(&self, event_descriptor: &EventDescriptor) -> bool {
110        match self {
111            Some(p) => p.is_event_enabled(event_descriptor),
112            None => false,
113        }
114    }
115}
116
117/// Implements `Provider` by registering with ETW.
118pub struct EtwProvider {
119    #[cfg(target_os = "windows")]
120    handle: REGHANDLE,
121
122    #[cfg(target_os = "windows")]
123    // #[allow(dead_code)] // Needed for lifetime control
124    stable: Pin<Box<StableProviderData>>,
125}
126
127impl Provider for EtwProvider {
128    #[inline(always)]
129    fn write(
130        &self,
131        options: Option<&crate::EventOptions>,
132        descriptor: &EventDescriptor,
133        data: &[EventDataDescriptor<'_>],
134    ) {
135        #[cfg(target_os = "windows")]
136        {
137            unsafe {
138                let mut activity_id_ptr = null();
139                let mut related_activity_id_ptr = null();
140
141                let mut event_descriptor = EVENT_DESCRIPTOR {
142                    Id: descriptor.id,
143                    Version: descriptor.version,
144                    Channel: descriptor.channel,
145                    Level: descriptor.level.0,
146                    Opcode: descriptor.opcode,
147                    Task: descriptor.task,
148                    Keyword: descriptor.keyword,
149                };
150
151                if let Some(options) = options {
152                    if let Some(id) = options.activity_id.as_ref() {
153                        activity_id_ptr = id as *const GUID as *const windows_sys::core::GUID;
154                    }
155                    if let Some(id) = options.related_activity_id.as_ref() {
156                        related_activity_id_ptr =
157                            id as *const GUID as *const windows_sys::core::GUID;
158                    }
159                    if let Some(level) = options.level {
160                        event_descriptor.Level = level.0;
161                    }
162                }
163
164                let error = EventWriteEx(
165                    self.handle,
166                    &event_descriptor,
167                    0, // filter
168                    0, // flags
169                    activity_id_ptr,
170                    related_activity_id_ptr,
171                    data.len() as u32,
172                    data.as_ptr() as *const EVENT_DATA_DESCRIPTOR,
173                );
174                if error != 0 {
175                    write_failed(error)
176                }
177            }
178        }
179    }
180
181    // write_ex
182    // write_transfer
183
184    fn is_enabled(&self, level: u8, keyword: u64) -> bool {
185        #[cfg(target_os = "windows")]
186        {
187            unsafe { EventProviderEnabled(self.handle, level, keyword) }
188        }
189        #[cfg(not(target_os = "windows"))]
190        {
191            false
192        }
193    }
194
195    fn is_event_enabled(&self, event_descriptor: &EventDescriptor) -> bool {
196        #[cfg(target_os = "windows")]
197        {
198            if false {
199                unsafe {
200                    EventEnabled(
201                        self.handle,
202                        event_descriptor as *const _ as *const EVENT_DESCRIPTOR,
203                    )
204                }
205            } else {
206                let max_level = self.stable.as_ref().max_level.load(SeqCst);
207                event_descriptor.level.0 <= max_level
208            }
209        }
210        #[cfg(not(target_os = "windows"))]
211        {
212            false
213        }
214    }
215}
216
217#[inline(never)]
218fn write_failed(_error: u32) {
219    #[cfg(feature = "dev")]
220    {
221        eprintln!("EventWrite failed: {}", _error);
222    }
223}
224
225#[cfg(target_os = "windows")]
226mod win_support {
227    pub use windows_sys::Win32::Foundation::ERROR_SUCCESS;
228    pub use windows_sys::Win32::System::Diagnostics::Etw::{
229        EventActivityIdControl, EventEnabled, EventProviderEnabled, EventRegister,
230        EventSetInformation, EventUnregister, EventWriteEx, ENABLECALLBACK_ENABLED_STATE,
231        EVENT_ACTIVITY_CTRL_CREATE_ID, EVENT_ACTIVITY_CTRL_CREATE_SET_ID,
232        EVENT_ACTIVITY_CTRL_GET_ID, EVENT_ACTIVITY_CTRL_SET_ID, EVENT_CONTROL_CODE_CAPTURE_STATE,
233        EVENT_CONTROL_CODE_DISABLE_PROVIDER, EVENT_CONTROL_CODE_ENABLE_PROVIDER,
234        EVENT_DATA_DESCRIPTOR, EVENT_DESCRIPTOR, EVENT_FILTER_DESCRIPTOR,
235    };
236
237    use super::*;
238
239    /// This data is stored in a Box, so that it has a stable address.
240    /// It is used to coordinate with ETW; ETW runs callbacks that need a stable pointer.
241    /// See `EventRegister` and the "enable callback".
242    pub(crate) struct StableProviderData {
243        pub(crate) max_level: AtomicU8,
244    }
245
246    /// See [PENABLECALLBACK](https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nc-evntprov-penablecallback).
247    #[allow(non_snake_case)]
248    pub(crate) unsafe extern "system" fn enable_callback(
249        _source_id: *const windows_sys::core::GUID,
250        is_enabled_code: ENABLECALLBACK_ENABLED_STATE,
251        level: u8,
252        _match_any_keyword: u64,
253        _match_all_keyword: u64,
254        _filter_data: *const EVENT_FILTER_DESCRIPTOR,
255        context: *mut core::ffi::c_void,
256    ) {
257        // This should never happen.
258        if context.is_null() {
259            return;
260        }
261        let stable_data: &StableProviderData = &*(context as *const _ as *const StableProviderData);
262
263        let _source_id: GUID = if _source_id.is_null() {
264            GUID::default()
265        } else {
266            (*(_source_id as *const GUID)).clone()
267        };
268        #[cfg(feature = "dev")]
269        {
270            eprintln!(
271                "enable_callback: source_id {} is_enabled {}, level {}, any {:#x} all {:#x} filter? {:?}",
272                _source_id, is_enabled_code, level, _match_any_keyword, _match_all_keyword,
273                !_filter_data.is_null()
274            );
275        }
276
277        match is_enabled_code {
278            EVENT_CONTROL_CODE_ENABLE_PROVIDER => {
279                #[cfg(feature = "dev")]
280                {
281                    eprintln!("ETW is ENABLING this provider.  setting level: {}", level);
282                }
283                stable_data.max_level.store(level, SeqCst);
284            }
285            EVENT_CONTROL_CODE_DISABLE_PROVIDER => {
286                #[cfg(feature = "dev")]
287                {
288                    eprintln!("ETW is DISABLING this provider.  setting level: {}", level);
289                }
290                stable_data.max_level.store(level, SeqCst);
291            }
292            EVENT_CONTROL_CODE_CAPTURE_STATE => {
293                // ETW is requesting that the provider log its state information. The meaning of this
294                // is provider-dependent. Currently, this functionality is not exposed to Rust apps.
295                #[cfg(feature = "dev")]
296                {
297                    eprintln!("EVENT_CONTROL_CODE_CAPTURE_STATE");
298                }
299            }
300            _ => {
301                // The control code is unrecognized.
302                #[cfg(feature = "dev")]
303                {
304                    eprintln!(
305                        "enable_callback: control code {} is not recognized",
306                        is_enabled_code
307                    );
308                }
309            }
310        }
311    }
312
313    pub fn new_activity_id() -> Result<GUID, Error> {
314        unsafe {
315            let mut guid: windows_sys::core::GUID = core::mem::zeroed();
316            let error = EventActivityIdControl(EVENT_ACTIVITY_CTRL_CREATE_ID, &mut guid);
317            if error == 0 {
318                Ok(guid.into())
319            } else {
320                Err(Error::WindowsError(error))
321            }
322        }
323    }
324}
325
326impl EtwProvider {
327    /// Registers an event provider with ETW.
328    ///
329    /// The implementation uses `[EventWriteEx](https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwriteex)`.
330    pub fn new(provider_id: &GUID) -> Result<EtwProvider, Error> {
331        #[cfg(target_os = "windows")]
332        {
333            unsafe {
334                let mut stable = Box::pin(StableProviderData {
335                    max_level: AtomicU8::new(0),
336                });
337                let mut handle: REGHANDLE = 0;
338                let stable_ptr: &mut StableProviderData = &mut stable;
339                let error = EventRegister(
340                    provider_id as *const _ as *const windows_sys::core::GUID,
341                    Some(enable_callback),
342                    stable_ptr as *mut StableProviderData as *mut core::ffi::c_void,
343                    &mut handle,
344                );
345                if error != 0 {
346                    Err(Error::WindowsError(error))
347                } else {
348                    Ok(EtwProvider { handle, stable })
349                }
350            }
351        }
352        #[cfg(not(target_os = "windows"))]
353        {
354            Ok(EtwProvider {})
355        }
356    }
357
358    /// See TraceLoggingRegisterEx in traceloggingprovider.h.
359    /// This registers provider metadata.
360    pub fn register_provider_metadata(&mut self, provider_metadata: &[u8]) -> Result<(), Error> {
361        #[cfg(target_os = "windows")]
362        {
363            unsafe {
364                let error = EventSetInformation(
365                    self.handle,
366                    EventProviderSetTraits,
367                    provider_metadata.as_ptr() as *const core::ffi::c_void,
368                    u32::try_from(provider_metadata.len()).unwrap(),
369                );
370                if error != 0 {
371                    Err(Error::WindowsError(error))
372                } else {
373                    #[cfg(feature = "dev")]
374                    {
375                        eprintln!("register_provider_metadata: succeeded");
376                    }
377                    Ok(())
378                }
379            }
380        }
381        #[cfg(not(target_os = "windows"))]
382        {
383            Ok(())
384        }
385    }
386
387    /// Registers provider traits for a provider.
388    ///
389    /// ETW providers should not call this function directly. It is automatically
390    /// called by the provider code that is generated by `win_etw_macros`.
391    ///
392    /// See [Provider Traits](https://docs.microsoft.com/en-us/windows/win32/etw/provider-traits).
393    pub fn set_provider_traits(&mut self, provider_traits: &[u8]) -> Result<(), Error> {
394        #[cfg(target_os = "windows")]
395        {
396            unsafe {
397                let error = EventSetInformation(
398                    self.handle,
399                    EventProviderSetTraits,
400                    provider_traits.as_ptr() as *const core::ffi::c_void,
401                    u32::try_from(provider_traits.len()).unwrap(),
402                );
403                if error != 0 {
404                    #[cfg(feature = "dev")]
405                    {
406                        eprintln!("EventSetInformation failed for provider traits");
407                    }
408                    return Err(Error::WindowsError(error));
409                }
410            }
411            Ok(())
412        }
413        #[cfg(not(target_os = "windows"))]
414        {
415            Ok(())
416        }
417    }
418}
419
420impl Drop for EtwProvider {
421    fn drop(&mut self) {
422        #[cfg(target_os = "windows")]
423        {
424            unsafe {
425                EventUnregister(self.handle);
426            }
427        }
428    }
429}
430
431unsafe impl Send for EtwProvider {}
432unsafe impl Sync for EtwProvider {}
433
434/// Describes parameters for an event. This is an implementation detail, and should not be directly
435/// used by applications.
436#[repr(C)]
437#[allow(missing_docs)]
438pub struct EventDescriptor {
439    pub id: u16,
440    pub version: u8,
441    pub channel: u8,
442    pub level: Level,
443    pub opcode: u8,
444    pub task: u16,
445    pub keyword: u64,
446}
447
448/// Allows an application to enter a nested activity scope. This creates a new activity ID,
449/// sets this activity ID as the current activity ID of the current thread, and then runs the
450/// provided function. After the function finishes, it restores the activity ID of the calling
451/// thread (even if a panic occurs).
452///
453/// See `[EventActivityIdControl](https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventactivityidcontrol)`.
454#[inline(always)]
455pub fn with_activity<F: FnOnce() -> R, R>(f: F) -> R {
456    #[cfg(target_os = "windows")]
457    {
458        let mut previous_activity_id: GUID = Default::default();
459
460        let mut restore = RestoreActivityHolder {
461            previous_activity_id: None,
462        };
463
464        unsafe {
465            let result = EventActivityIdControl(
466                EVENT_ACTIVITY_CTRL_CREATE_SET_ID,
467                &mut previous_activity_id as *mut _ as *mut windows_sys::core::GUID,
468            );
469            if result == ERROR_SUCCESS {
470                restore.previous_activity_id = Some(previous_activity_id);
471            } else {
472                // Failed to create/replace the activity ID. There is not much we can do about this.
473            }
474        }
475
476        let result = f();
477        // RestoreActivityHolder::drop() will run, even if f() panics, and will restore the
478        // activity ID of the current thread.
479        drop(restore);
480        result
481    }
482
483    #[cfg(not(target_os = "windows"))]
484    {
485        f()
486    }
487}
488
489struct RestoreActivityHolder {
490    previous_activity_id: Option<GUID>,
491}
492
493impl Drop for RestoreActivityHolder {
494    fn drop(&mut self) {
495        #[cfg(target_os = "windows")]
496        {
497            unsafe {
498                if let Some(previous_activity_id) = self.previous_activity_id.as_ref() {
499                    EventActivityIdControl(
500                        EVENT_ACTIVITY_CTRL_SET_ID,
501                        previous_activity_id as *const GUID as *const windows_sys::core::GUID
502                            as *mut _,
503                    );
504                }
505            }
506        }
507    }
508}