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
15pub 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
30pub 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
53pub trait Provider {
56 fn write(
58 &self,
59 options: Option<&crate::EventOptions>,
60 descriptor: &EventDescriptor,
61 data: &[EventDataDescriptor<'_>],
62 );
63
64 fn is_enabled(&self, level: u8, keyword: u64) -> bool;
66
67 fn is_event_enabled(&self, event_descriptor: &EventDescriptor) -> bool;
69}
70
71pub 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
117pub struct EtwProvider {
119 #[cfg(target_os = "windows")]
120 handle: REGHANDLE,
121
122 #[cfg(target_os = "windows")]
123 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, 0, 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 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 pub(crate) struct StableProviderData {
243 pub(crate) max_level: AtomicU8,
244 }
245
246 #[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 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 #[cfg(feature = "dev")]
296 {
297 eprintln!("EVENT_CONTROL_CODE_CAPTURE_STATE");
298 }
299 }
300 _ => {
301 #[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 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 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 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#[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#[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 }
474 }
475
476 let result = f();
477 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}