tracelogging 1.2.1

TraceLogging for Rust
Documentation
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

use core::fmt;
use core::str::from_utf8;

use crate::descriptors::EventDataDescriptor;
use crate::descriptors::EventDescriptor;
use crate::enums::Level;
use crate::guid::Guid;
use crate::native::ProviderContext;
use crate::native::ProviderEnableCallback;

#[allow(unused_imports)] // For docs
#[cfg(feature = "macros")]
use crate::define_provider;

#[allow(unused_imports)] // For docs
#[cfg(feature = "macros")]
use crate::write_event;

/// A connection to ETW for writing TraceLogging (manifest-free) events.
///
/// # Overview
///
/// 1. Use [`define_provider!`] to create a static provider variable.
/// 2. Call [`Provider::register()`] during component initialization to open the
///    connection to ETW.
/// 3. Use [`write_event!`] as needed to write events.
/// 4. Call [`Provider::unregister()`] during component cleanup to close the connection
///    to ETW.
pub struct Provider {
    context: ProviderContext,
    meta: &'static [u8], // provider metadata
    id: Guid,
}

impl Provider {
    /// Returns the current thread's thread-local activity id.
    /// (Calls
    /// [EventActivityIdControl](https://docs.microsoft.com/windows/win32/api/evntprov/nf-evntprov-eventactivityidcontrol)
    /// with control code `EVENT_ACTIVITY_CTRL_GET_ID`.)
    ///
    /// The thread-local activity id will be used if [`write_event!`] is called with no
    /// `activity_id` option.
    pub fn current_thread_activity_id() -> Guid {
        let mut activity_id = Guid::default();
        ProviderContext::activity_id_control(
            1, // GetId
            &mut activity_id,
        );
        return activity_id;
    }

    /// Sets the current thread's thread-local activity id. Returns the previous value.
    /// (Calls
    /// [EventActivityIdControl](https://docs.microsoft.com/windows/win32/api/evntprov/nf-evntprov-eventactivityidcontrol)
    /// with control code `EVENT_ACTIVITY_CTRL_GET_SET_ID`.)
    ///
    /// The thread-local activity id will be used if [`write_event!`] is called with no
    /// `activity_id` option.
    ///
    /// Important: thread-local activity id should follow scoping rules. If you set the
    /// thread-local activity id in a scope, you should restore the previous value before exiting
    /// the scope.
    pub fn set_current_thread_activity_id(value: &Guid) -> Guid {
        let mut activity_id = *value;
        ProviderContext::activity_id_control(
            4, // GetSetId
            &mut activity_id,
        );
        return activity_id;
    }

    /// Generates and returns a new 128-bit value suitable for use as an activity id.
    /// (Calls
    /// [EventActivityIdControl](https://docs.microsoft.com/windows/win32/api/evntprov/nf-evntprov-eventactivityidcontrol)
    /// with control code `EVENT_ACTIVITY_CREATE_ID`.)
    ///
    /// The returned value is not a true GUID/UUID since it is not globally-unique. The
    /// returned value is locally-unique: it is guaranteed to be different from all other
    /// values generated by `create_activity_id()` on the current machine in the current
    /// boot session.
    ///
    /// Activity ids need to be unique within the trace but do not need to be
    /// globally-unique, so your activity ids can use either a real GUID/UUID or a
    /// locally-unique id generated by create_activity_id(). Use `create_activity_id()` for
    /// locally-unique activity ids or use [Guid::new] for globally-unique activity ids.
    pub fn create_activity_id() -> Guid {
        let mut activity_id = Guid::default();
        ProviderContext::activity_id_control(
            3, // CreateId
            &mut activity_id,
        );
        return activity_id;
    }

    /// Returns a GUID generated from a case-insensitive hash of the specified trace
    /// provider name. The hash uses the same algorithm as many other ETW tools and APIs.
    /// Given the same name, it will always generate the same GUID.
    /// (Same as [Guid::from_name].)
    ///
    /// The result can (and usually should) be used as the provider id.
    ///
    /// **Note:** Do not use guid_from_name to generate activity ids.
    /// ```
    /// use tracelogging as tlg;
    /// assert_eq!(
    ///    tlg::Provider::guid_from_name("MyProvider"),
    ///    tlg::Guid::from_u128(&0xb3864c38_4273_58c5_545b_8b3608343471));
    /// ```
    pub fn guid_from_name(name: &str) -> Guid {
        return Guid::from_name(name);
    }

    /// *Advanced:* Returns this provider's encoded metadata bytes.
    pub const fn raw_meta(&self) -> &[u8] {
        return self.meta;
    }

    /// Returns this provider's name.
    pub fn name(&self) -> &str {
        let mut name_end = 2;
        while self.meta[name_end] != 0 {
            name_end += 1;
        }
        return from_utf8(&self.meta[2..name_end]).unwrap();
    }

    /// Returns this provider's id (GUID).
    pub const fn id(&self) -> &Guid {
        return &self.id;
    }

    /// Returns true if any ETW logging session is listening to this provider for events
    /// with the specified level and keyword.
    ///
    /// This method is only supported on the ETW implementation of Provider.
    /// For similar functionality that works with all implementations, use the
    /// [`provider_enabled!`] macro.
    ///
    /// Note: [`write_event!`] already checks `enabled()`. You only need to make your own
    /// call to `enabled()` if you want to skip something other than [`write_event!`].
    #[inline(always)]
    pub const fn enabled(&self, level: Level, keyword: u64) -> bool {
        return self.context.enabled(level, keyword);
    }

    /// If this provider is not registered, does nothing and returns 0.
    /// Otherwise, unregisters the provider.
    ///
    /// Returns 0 for success or a Win32 error from `EventUnregister` for failure. The
    /// return value is for diagnostic purposes only and should generally be ignored in
    /// retail builds.
    pub fn unregister(&self) -> u32 {
        return self.context.unregister();
    }

    /// Register the provider.
    ///
    /// # Preconditions
    ///
    /// - Provider must not already be registered. Verified at runtime, failure = panic.
    /// - For a given provider object, a call on one thread to the provider's `register`
    ///   method must not occur at the same time as a call to the same provider's
    ///   `register` or `unregister` method on any other thread. Verified at runtime,
    ///   failure = panic.
    ///
    /// # Safety
    ///
    /// - If creating a DLL or creating a provider that might run as part of a DLL, all
    ///   registered providers **must** be unregistered before the DLL unloads.
    ///
    ///   If a provider variable is registered by a DLL and the DLL unloads while the
    ///   provider is still registered, the process may subsequently crash. This occurs
    ///   because `register` enables an ETW callback into the calling DLL and
    ///   `unregister` ensures that the callback is disabled. If the module unloads
    ///   without disabling the callback, the process will crash the next time that ETW
    ///   tries to invoke the callback.
    ///
    ///   The provider cannot unregister itself because the provider is static and Rust
    ///   does not drop static objects.
    ///
    ///   You'll typically register the provider during `DLL_PROCESS_ATTACH` and
    ///   unregister during `DLL_PROCESS_DETACH`.
    pub unsafe fn register(&self) -> u32 {
        return self.register_impl(None, 0);
    }

    /// Register the provider with a custom provider enable callback.
    ///
    /// # Preconditions
    ///
    /// - Provider must not already be registered. Verified at runtime, failure = panic.
    /// - For a given provider object, a call on one thread to the provider's `register`
    ///   method must not occur at the same time as a call to the same provider's
    ///   `register` or `unregister` method on any other thread. Verified at runtime,
    ///   failure = panic.
    ///
    /// # Safety
    ///
    /// - If creating a DLL or creating a provider that might run as part of a DLL, all
    ///   registered providers **must** be unregistered before the DLL unloads.
    ///
    ///   If a provider variable is registered by a DLL and the DLL unloads while the
    ///   provider is still registered, the process may subsequently crash. This occurs
    ///   because `register` enables an ETW callback into the calling DLL and
    ///   `unregister` ensures that the callback is disabled. If the module unloads
    ///   without disabling the callback, the process will crash the next time that ETW
    ///   tries to invoke the callback.
    ///
    ///   The provider cannot unregister itself because the provider is static and Rust
    ///   does not drop static objects.
    ///
    ///   You'll typically register the provider during `DLL_PROCESS_ATTACH` and
    ///   unregister during `DLL_PROCESS_DETACH`.
    pub unsafe fn register_with_callback(
        &self,
        callback_fn: ProviderEnableCallback,
        callback_context: usize,
    ) -> u32 {
        return self.register_impl(Some(callback_fn), callback_context);
    }

    /// Safety:
    ///
    /// 1. Pinning: The only way to construct a provider is `provider_new`.
    ///    `provider_new` is unsafe and declares its safety condition as the provider
    ///    must not be moved-from while registered, so we're covered for any direct
    ///    use of `provider_new`. The `new_provider!` macro calls `provider_new` and
    ///    prohibits moving by storing the provider in an immutable variable, so
    ///    `new_provider!` is meeting its obligations.
    ///
    /// 2. Unregister: Our `register` and `register_with_callback` methods are unsafe and
    ///    declare safety conditions matching the Unregister condition.
    fn register_impl(
        &self,
        callback_fn: Option<ProviderEnableCallback>,
        callback_context: usize,
    ) -> u32 {
        let result = unsafe {
            self.context
                .register(&self.id, callback_fn, callback_context)
        };

        if result == 0 {
            // 2 == EventProviderSetTraits
            self.context.set_information(2, self.meta);
        }

        return result;
    }
}

impl fmt::Debug for Provider {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        return write!(
            f,
            "Provider {{ name: \"{}\", id: {}, enabled: {}, reg_handle: {:x} }}",
            self.name(),
            from_utf8(&self.id.to_utf8_bytes()).unwrap(),
            self.enabled(Level::LogAlways, 0),
            self.context.reg_handle()
        );
    }
}

/// For use by the define_provider macro: creates a new provider.
///
/// # Safety
///
/// - Must not move-out of a provider while it is registered. `define_provider` enforces
///   this by storing the result in an immutable variable.
pub const unsafe fn provider_new(meta: &'static [u8], id: &Guid) -> Provider {
    return Provider {
        context: ProviderContext::new(),
        meta,
        id: *id,
    };
}

/// For use by the write_event macro: Calls EventWriteTransfer.
pub fn provider_write_transfer(
    provider: &Provider,
    descriptor: &EventDescriptor,
    activity_id: Option<&[u8; 16]>,
    related_id: Option<&[u8; 16]>,
    dd: &[EventDataDescriptor],
) -> u32 {
    return provider
        .context
        .write_transfer(descriptor, activity_id, related_id, dd);
}