1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
// 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.
///
/// 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.
///
/// # Preconditions
/// - For a given provider object, a call on one thread to the provider's `register`
/// or `unregister` method must not occur at the same time as a call to the
/// provider's `register` or `unregister` method on any other thread. Verified at
/// runtime, failure = panic.
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`
/// or `unregister` method must not occur at the same time as a call to the
/// 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`
/// or `unregister` method must not occur at the same time as a call to the
/// 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<&Guid>,
related_id: Option<&Guid>,
dd: &[EventDataDescriptor],
) -> u32 {
return provider
.context
.write_transfer(descriptor, activity_id, related_id, dd);
}