1use std::ffi::OsString;
5use std::marker::PhantomData;
6use std::path::PathBuf;
7use std::sync::Arc;
8use std::time::Duration;
9
10use widestring::U16CString;
11use windows::core::GUID;
12use windows::Win32::System::Diagnostics::Etw;
13
14use self::private::{PrivateRealTimeTraceTrait, PrivateTraceTrait};
15
16use crate::native::etw_types::{EventTraceProperties, SubscriptionSource};
17use crate::native::evntrace::{
18 close_trace, control_trace, control_trace_by_name, enable_provider, open_trace, process_trace,
19 start_trace, ControlHandle, TraceHandle,
20};
21use crate::native::version_helper;
22use crate::provider::Provider;
23use crate::utils;
24use crate::EventRecord;
25use crate::SchemaLocator;
26
27pub use crate::native::etw_types::DumpFileLoggingMode;
28pub use crate::native::etw_types::LoggingMode;
29
30pub(crate) mod callback_data;
31use callback_data::CallbackData;
32use callback_data::CallbackDataFromFile;
33use callback_data::RealTimeCallbackData;
34
35const KERNEL_LOGGER_NAME: &str = "NT Kernel Logger";
36const SYSTEM_TRACE_CONTROL_GUID: &str = "9e814aad-3204-11d2-9a82-006008a86939";
37const EVENT_TRACE_SYSTEM_LOGGER_MODE: u32 = 0x02000000;
38
39#[derive(Debug)]
41pub enum TraceError {
42 InvalidTraceName,
43 EtwNativeError(crate::native::EvntraceNativeError),
45}
46
47impl From<crate::native::EvntraceNativeError> for TraceError {
48 fn from(err: crate::native::EvntraceNativeError) -> Self {
49 TraceError::EtwNativeError(err)
50 }
51}
52
53type TraceResult<T> = Result<T, TraceError>;
54
55#[derive(Debug, Copy, Clone)]
61pub struct TraceProperties {
62 pub buffer_size: u32,
64 pub min_buffer: u32,
66 pub max_buffer: u32,
68 pub flush_timer: Duration,
72 pub log_file_mode: LoggingMode,
74}
75
76impl Default for TraceProperties {
77 fn default() -> Self {
78 TraceProperties {
80 buffer_size: 32,
81 min_buffer: 0,
82 max_buffer: 0,
83 flush_timer: Duration::from_secs(1),
84 log_file_mode: LoggingMode::EVENT_TRACE_REAL_TIME_MODE
85 | LoggingMode::EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING,
86 }
87 }
88}
89
90pub trait TraceTrait: private::PrivateTraceTrait + Sized {
92 fn trace_handle(&self) -> TraceHandle;
94
95 fn events_handled(&self) -> usize;
97
98 fn process(&mut self) -> TraceResult<()> {
105 process_trace(self.trace_handle()).map_err(|e| e.into())
106 }
107
108 fn process_from_handle(handle: TraceHandle) -> TraceResult<()> {
112 process_trace(handle).map_err(|e| e.into())
113 }
114
115 fn stop(mut self) -> TraceResult<()> {
120 self.non_consuming_stop()
121 }
122}
123
124pub trait RealTimeTraceTrait: TraceTrait + private::PrivateRealTimeTraceTrait {
126 fn trace_guid() -> GUID;
128
129 fn trace_name(&self) -> OsString;
131}
132
133impl TraceTrait for UserTrace {
134 fn trace_handle(&self) -> TraceHandle {
135 self.trace_handle
136 }
137
138 fn events_handled(&self) -> usize {
139 self.callback_data.events_handled()
140 }
141}
142
143impl RealTimeTraceTrait for UserTrace {
144 fn trace_guid() -> GUID {
145 GUID::new().unwrap_or(GUID::zeroed())
146 }
147
148 fn trace_name(&self) -> OsString {
149 self.properties.name()
150 }
151}
152
153impl TraceTrait for KernelTrace {
155 fn trace_handle(&self) -> TraceHandle {
156 self.trace_handle
157 }
158
159 fn events_handled(&self) -> usize {
160 self.callback_data.events_handled()
161 }
162}
163
164impl RealTimeTraceTrait for KernelTrace {
165 fn trace_guid() -> GUID {
166 if version_helper::is_win8_or_greater() {
167 GUID::new().unwrap_or(GUID::zeroed())
168 } else {
169 GUID::from(SYSTEM_TRACE_CONTROL_GUID)
170 }
171 }
172
173 fn trace_name(&self) -> OsString {
174 self.properties.name()
175 }
176}
177
178impl TraceTrait for FileTrace {
179 fn trace_handle(&self) -> TraceHandle {
180 self.trace_handle
181 }
182
183 fn events_handled(&self) -> usize {
184 self.callback_data.events_handled()
185 }
186}
187
188#[derive(Debug)]
192#[allow(clippy::redundant_allocation)] pub struct UserTrace {
194 properties: EventTraceProperties,
195 control_handle: ControlHandle,
196 trace_handle: TraceHandle,
197 callback_data: Box<Arc<CallbackData>>,
201}
202
203#[derive(Debug)]
207#[allow(clippy::redundant_allocation)] pub struct KernelTrace {
209 properties: EventTraceProperties,
210 control_handle: ControlHandle,
211 trace_handle: TraceHandle,
212 callback_data: Box<Arc<CallbackData>>,
216}
217
218#[derive(Debug)]
222#[allow(clippy::redundant_allocation)] pub struct FileTrace {
224 trace_handle: TraceHandle,
225 callback_data: Box<Arc<CallbackData>>,
229}
230
231#[derive(Clone, Default)]
233pub struct DumpFileParams {
234 pub file_path: PathBuf,
235 pub file_logging_mode: DumpFileLoggingMode,
237 pub max_size: Option<u32>,
239}
240
241pub struct TraceBuilder<T: RealTimeTraceTrait> {
245 name: String,
246 etl_dump_file: Option<DumpFileParams>,
247 properties: TraceProperties,
248 rt_callback_data: RealTimeCallbackData,
249 trace_kind: PhantomData<T>,
250}
251
252pub struct FileTraceBuilder {
253 etl_file_path: PathBuf,
254 callback: crate::EtwCallback,
255}
256
257impl UserTrace {
258 pub fn new() -> TraceBuilder<UserTrace> {
260 let name = format!("n4r1b-trace-{}", utils::rand_string());
261 TraceBuilder {
262 name,
263 etl_dump_file: None,
264 rt_callback_data: RealTimeCallbackData::new(),
265 properties: TraceProperties::default(),
266 trace_kind: PhantomData,
267 }
268 }
269
270 pub fn stop(mut self) -> TraceResult<()> {
275 self.non_consuming_stop()
276 }
277}
278
279impl KernelTrace {
280 pub fn new() -> TraceBuilder<KernelTrace> {
282 let builder = TraceBuilder {
283 name: String::new(),
284 etl_dump_file: None,
285 rt_callback_data: RealTimeCallbackData::new(),
286 properties: TraceProperties::default(),
287 trace_kind: PhantomData,
288 };
289 builder.named(format!("n4r1b-trace-{}", utils::rand_string()))
291 }
292
293 pub fn stop(mut self) -> TraceResult<()> {
298 self.non_consuming_stop()
299 }
300}
301
302mod private {
303 use super::*;
307
308 #[derive(Debug, PartialEq, Eq)]
309 pub enum TraceKind {
310 User,
311 Kernel,
312 }
313
314 pub trait PrivateRealTimeTraceTrait: PrivateTraceTrait {
315 const TRACE_KIND: TraceKind;
316 #[allow(clippy::redundant_allocation)] fn build(
318 properties: EventTraceProperties,
319 control_handle: ControlHandle,
320 trace_handle: TraceHandle,
321 callback_data: Box<Arc<CallbackData>>,
322 ) -> Self;
323 fn augmented_file_mode() -> u32;
324 fn enable_flags(_providers: &[Provider]) -> u32;
325 }
326
327 pub trait PrivateTraceTrait {
328 fn non_consuming_stop(&mut self) -> TraceResult<()>;
331 }
332}
333
334impl private::PrivateRealTimeTraceTrait for UserTrace {
335 const TRACE_KIND: private::TraceKind = private::TraceKind::User;
336
337 fn build(
338 properties: EventTraceProperties,
339 control_handle: ControlHandle,
340 trace_handle: TraceHandle,
341 callback_data: Box<Arc<CallbackData>>,
342 ) -> Self {
343 UserTrace {
344 properties,
345 control_handle,
346 trace_handle,
347 callback_data,
348 }
349 }
350
351 fn augmented_file_mode() -> u32 {
352 0
353 }
354 fn enable_flags(_providers: &[Provider]) -> u32 {
355 0
356 }
357}
358
359impl private::PrivateTraceTrait for UserTrace {
360 fn non_consuming_stop(&mut self) -> TraceResult<()> {
361 close_trace(self.trace_handle, &self.callback_data)?;
362 control_trace(
363 &mut self.properties,
364 self.control_handle,
365 Etw::EVENT_TRACE_CONTROL_STOP,
366 )?;
367 Ok(())
368 }
369}
370
371impl private::PrivateRealTimeTraceTrait for KernelTrace {
372 const TRACE_KIND: private::TraceKind = private::TraceKind::Kernel;
373
374 fn build(
375 properties: EventTraceProperties,
376 control_handle: ControlHandle,
377 trace_handle: TraceHandle,
378 callback_data: Box<Arc<CallbackData>>,
379 ) -> Self {
380 KernelTrace {
381 properties,
382 control_handle,
383 trace_handle,
384 callback_data,
385 }
386 }
387
388 fn augmented_file_mode() -> u32 {
389 if version_helper::is_win8_or_greater() {
390 EVENT_TRACE_SYSTEM_LOGGER_MODE
391 } else {
392 0
393 }
394 }
395
396 fn enable_flags(providers: &[Provider]) -> u32 {
397 providers.iter().fold(0, |acc, x| acc | x.kernel_flags())
398 }
399}
400
401impl private::PrivateTraceTrait for KernelTrace {
402 fn non_consuming_stop(&mut self) -> TraceResult<()> {
403 close_trace(self.trace_handle, &self.callback_data)?;
404 control_trace(
405 &mut self.properties,
406 self.control_handle,
407 Etw::EVENT_TRACE_CONTROL_STOP,
408 )?;
409 Ok(())
410 }
411}
412
413impl private::PrivateTraceTrait for FileTrace {
414 fn non_consuming_stop(&mut self) -> TraceResult<()> {
415 close_trace(self.trace_handle, &self.callback_data)?;
416 Ok(())
417 }
418}
419
420impl<T: RealTimeTraceTrait + PrivateRealTimeTraceTrait> TraceBuilder<T> {
421 pub fn named(mut self, name: String) -> Self {
427 if T::TRACE_KIND == private::TraceKind::Kernel && !version_helper::is_win8_or_greater() {
428 self.name = String::from(KERNEL_LOGGER_NAME);
429 } else {
430 self.name = name;
431 };
432
433 self
434 }
435
436 pub fn set_trace_properties(mut self, props: TraceProperties) -> Self {
440 self.properties = props;
441 self
442 }
443
444 pub fn set_etl_dump_file(mut self, params: DumpFileParams) -> Self {
455 self.etl_dump_file = Some(params);
456 self
457 }
458
459 pub fn enable(mut self, provider: Provider) -> Self {
467 self.rt_callback_data.add_provider(provider);
468 self
469 }
470
471 pub fn start(self) -> TraceResult<(T, TraceHandle)> {
485 let trace_wide_name = U16CString::from_str_truncate(self.name);
487 let mut trace_wide_vec = trace_wide_name.into_vec();
488 trace_wide_vec.truncate(crate::native::etw_types::TRACE_NAME_MAX_CHARS);
489 let trace_wide_name = U16CString::from_vec_truncate(trace_wide_vec);
490
491 let wide_etl_dump_file = match self.etl_dump_file {
493 None => None,
494 Some(DumpFileParams {
495 file_path,
496 file_logging_mode,
497 max_size,
498 }) => {
499 let wide_path = U16CString::from_os_str_truncate(file_path.as_os_str());
500 let mut wide_path_vec = wide_path.into_vec();
501 wide_path_vec.truncate(crate::native::etw_types::TRACE_NAME_MAX_CHARS);
502 Some((
503 U16CString::from_vec_truncate(wide_path_vec),
504 file_logging_mode,
505 max_size,
506 ))
507 }
508 };
509
510 let flags = self.rt_callback_data.provider_flags::<T>();
511 let (full_properties, control_handle) = start_trace::<T>(
512 &trace_wide_name,
513 wide_etl_dump_file
514 .as_ref()
515 .map(|(path, params, max_size)| (path.as_ucstr(), *params, *max_size)),
516 &self.properties,
517 flags,
518 )?;
519
520 if T::TRACE_KIND == private::TraceKind::User {
523 for prov in self.rt_callback_data.providers() {
524 enable_provider(control_handle, prov)?;
525 }
526 }
527
528 let callback_data = Box::new(Arc::new(CallbackData::RealTime(self.rt_callback_data)));
529 let trace_handle = open_trace(
530 SubscriptionSource::RealTimeSession(trace_wide_name),
531 &callback_data,
532 )?;
533
534 Ok((
535 T::build(full_properties, control_handle, trace_handle, callback_data),
536 trace_handle,
537 ))
538 }
539
540 pub fn start_and_process(self) -> TraceResult<T> {
546 let (trace, trace_handle) = self.start()?;
547
548 std::thread::spawn(move || UserTrace::process_from_handle(trace_handle));
549
550 Ok(trace)
551 }
552}
553
554impl FileTrace {
555 #[allow(clippy::new_ret_no_self)]
557 pub fn new<T>(path: PathBuf, callback: T) -> FileTraceBuilder
558 where
559 T: FnMut(&EventRecord, &SchemaLocator) + Send + Sync + 'static,
560 {
561 FileTraceBuilder {
562 etl_file_path: path,
563 callback: Box::new(callback),
564 }
565 }
566
567 fn non_consuming_stop(&mut self) -> TraceResult<()> {
568 close_trace(self.trace_handle, &self.callback_data)?;
569 Ok(())
570 }
571}
572
573impl FileTraceBuilder {
574 pub fn start(self) -> TraceResult<(FileTrace, TraceHandle)> {
578 let wide_etl_file_path = U16CString::from_os_str_truncate(self.etl_file_path.as_os_str());
580
581 let from_file_cb = CallbackDataFromFile::new(self.callback);
582 let callback_data = Box::new(Arc::new(CallbackData::FromFile(from_file_cb)));
583 let trace_handle = open_trace(
584 SubscriptionSource::FromFile(wide_etl_file_path),
585 &callback_data,
586 )?;
587
588 Ok((
589 FileTrace {
590 trace_handle,
591 callback_data,
592 },
593 trace_handle,
594 ))
595 }
596
597 pub fn start_and_process(self) -> TraceResult<FileTrace> {
603 let (trace, trace_handle) = self.start()?;
604
605 std::thread::spawn(move || FileTrace::process_from_handle(trace_handle));
606
607 Ok(trace)
608 }
609}
610
611impl Drop for UserTrace {
612 fn drop(&mut self) {
613 let _ignored_error_in_drop = self.non_consuming_stop();
614 }
615}
616
617impl Drop for KernelTrace {
618 fn drop(&mut self) {
619 let _ignored_error_in_drop = self.non_consuming_stop();
620 }
621}
622
623impl Drop for FileTrace {
624 fn drop(&mut self) {
625 let _ignored_error_in_drop = self.non_consuming_stop();
626 }
627}
628
629pub fn stop_trace_by_name(trace_name: &str) -> TraceResult<()> {
634 let trace_properties = TraceProperties::default();
635 let flags = Etw::EVENT_TRACE_FLAG::default();
636 let wide_name = U16CString::from_str(trace_name).map_err(|_| TraceError::InvalidTraceName)?;
637
638 let mut properties = EventTraceProperties::new::<UserTrace>(
639 &wide_name,
641 None, &trace_properties,
643 flags,
644 );
645
646 control_trace_by_name(&mut properties, &wide_name, Etw::EVENT_TRACE_CONTROL_STOP)?;
647
648 Ok(())
649}
650
651#[cfg(test)]
652mod test {
653 use super::*;
654
655 #[test]
656 fn test_enable_multiple_providers() {
657 let prov = Provider::by_guid("22fb2cd6-0e7b-422b-a0c7-2fad1fd0e716").build();
658 let prov1 = Provider::by_guid("A0C1853B-5C40-4B15-8766-3CF1C58F985A").build();
659
660 let trace_builder = UserTrace::new().enable(prov).enable(prov1);
661
662 assert_eq!(trace_builder.rt_callback_data.providers().len(), 2);
663 }
664}