diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 652966b..7b75495 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -35,11 +35,6 @@ jobs:
- uses: actions-rs/cargo@v1
with:
command: test
- # Cargo test (for the doctests only)
- - uses: actions-rs/cargo@v1
- with:
- command: test
- args: --doc
clippy-on-diffs:
runs-on: windows-2022
diff --git a/Cargo.toml b/Cargo.toml
index 897b71e..133dc4e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,7 +3,9 @@ name = "ferrisetw"
version = "1.0.0"
license = "MIT OR Apache-2.0"
description = "Basically a KrabsETW rip-off written in Rust"
-authors = ["n4r1b"]
+keywords = ["etw", "krabsetw", "event", "tracing", "windows"]
+categories = ["api-bindings", "parsing"]
+authors = ["n4r1b", "daladim"]
edition = "2018"
repository = "https://github.com/n4r1b/ferrisetw"
@@ -37,6 +39,3 @@ zerocopy = "0.6"
time = { version = "0.3", features = ["large-dates"], optional = true }
# thiserror = "~1.0"
# anyhow = "~1.0"
-
-[lib]
-doctest = false
diff --git a/README.md b/README.md
index 2717fdb..0e5d6f5 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,10 @@
# FerrisETW 🦀
-**Basically a [KrabsETW](https://github.com/microsoft/krabsetw/) rip-off written in Rust**,
-hence the name [`Ferris`](https://rustacean.net/) 🦀
-All **credits** go to the team at Microsoft who develop KrabsEtw, without it, this project
-probably wouldn't be a thing.
+This crate provides safe Rust abstractions over the ETW consumer APIs.
-## Motivation
-Since lately I've been working very closely with ETW and Rust, I thought that having a tool that
-would simplify ETW management written in Rust and available as a crate for other to consume would
-be pretty neat and that's where this crate comes into play 🔥
+It started as a [KrabsETW](https://github.com/microsoft/krabsetw/) rip-off written in Rust (hence the name [`Ferris`](https://rustacean.net/) 🦀).
+All credits go to the team at Microsoft who develop KrabsEtw, without it, this project probably wouldn't be a thing.
+Since version 1.0, the API and internal architecture of this crate is slightly diverging from `krabsetw`, so that it is more Rust-idiomatic.
## Examples
You can find a examples within the
@@ -16,81 +12,28 @@ You can find a examples within the
as well as the [examples](./examples) and the [tests](./tests) folders.
If you are familiar with KrabsETW you'll see that is very similar.
-In case you've never used KrabsETW before, the examples are very straight forward and should be easy to follow. If you have
-any issues don't hesitate in asking.
+In case you've never used KrabsETW before, the examples are very straight forward and should be easy to follow. If you have any issues don't hesitate in asking.
-The following snippet shows the basic usage of the library
-```rust
-fn wmi_callback(record: EventRecord, schema_locator: &mut SchemaLocator) {
- // We locate the Schema for the Event
- match schema_locator.event_schema(record) {
- Ok(schema) => {
- // We filter the event by EventId
- if schema.event_id() == 12 {
- // We obtain the Parser for the Schema
- let mut parser = Parser::create(&schema);
- // We parse the data from the Event based on the names of the fields of the Event
- // Type annotations or Fully Qualified Syntax are needed when calling TryParse
- let op: String = parser
- .try_parse("Operation")
- .unwrap_or(String::from("Operation missing"));
- let provider_name: String = parser
- .try_parse("ProviderName")
- .unwrap_or(String::from("ProviderName missing"));
- // Could also use String as type
- let provider_guid: GUID =
- parser.try_parse("ProviderGuid").unwrap_or(GUID::zeroed());
- println!(
- "WMI-Activity -> ProviderName {}, ProviderGuid: {:?}, Operation: {}",
- provider_name, provider_guid, op
- );
- }
- }
- Err(err) => println!("Error {:?}", err),
- };
-}
-
-fn main() {
- // We first build a Provider
- let wmi_provider = Provider::new()
- .by_guid("1418ef04-b0b4-4623-bf7e-d74ab47bbdaa") // Microsoft-Windows-WMI-Activity
- .add_callback(wmi_callback)
- .build()
- .unwrap();
-
- // We enable the Provider in a new Trace and start the trace
- // This internally will launch a new thread
- let mut trace = UserTrace::new().enable(wmi_provider).start().unwrap();
-
- std::thread::sleep(Duration::new(20, 0));
-
- // We stop the trace
- trace.stop();
-}
-```
## Documentation
-I'm having some trouble to get docs.rs to build the documentation for the crate so at the moment is being hosted on my domain.
-[FerrisETW Doc](https://www.n4r1b.com/doc/ferrisetw/index.html)
+This crate is documented at [docs.rs](https://docs.rs/crate/ferrisetw/latest).
## Notes
-- The project is still WIP, there's still plenty of things to evaluate/investigate and things to fix and do better.
- Any help would be greatly appreciated, also any issues you may have!
+- The project is still WIP.
+ Feel free to report bugs, issues, feature requests, etc.
+ Of course, contributing will be happily accepted!
+
-
- The types available for parsing are those that implement the trait TryParse for Parser, basic types are already
implemented. In the near future I'll add more :)
-
-- I tried to keep dependencies as minimal as possible, also you'll see I went with the new [windows-rs](https://github.com/microsoft/windows-rs) instead of
+
+- I tried to keep dependencies as minimal as possible, also you'll see I went with the new [windows-rs](https://github.com/microsoft/windows-rs) instead of
using the [winapi](https://docs.rs/winapi/0.3.9/winapi/). This is a personal decision mainly because I believe the
Windows bindings is going to be the "standard" to interact with the Windows API in the near future.
-- Although I encourage everyone to use Rust, I do believe that, at the moment, if you plan on interacting
- with ETW in a production level and the programming language is not a constraint you should definitely
- go with **KrabsETW** as a more robust and tested option. Hopefully in next iterations I'll be able
- to remove this disclaimer 😃
-
### Acknowledgments
-- First of all, the team at MS who develop KrabsETW!!
+- First of all, the team at MS who develop KrabsETW!!
- [Shaddy](https://github.com/Shaddy) for, pretty much, teaching me all the Rust I know 😃
+- [n4r1b](https://github.com/n4r1b) for creating this great crate
+- [daladim](https://github.com/daladim) for adding even more features
diff --git a/src/lib.rs b/src/lib.rs
index dee47a8..4be9f55 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,8 +1,9 @@
//! # Event Windows Tracing FTW!
-//! **Basically a [KrabsETW] rip-off written in Rust**, hence the name `Ferris` 🦀
+//! This crate provides safe Rust abstractions over the ETW consumer APIs.
//!
-//! All **credits** go to the team at Microsoft who develop KrabsEtw, without it, this project
-//! probably wouldn't be a thing.
+//! It started as a [KrabsETW](https://github.com/microsoft/krabsetw/) rip-off written in Rust (hence the name [`Ferris`](https://rustacean.net/) 🦀).
+//! All credits go to the team at Microsoft who develop KrabsEtw, without it, this project probably wouldn't be a thing.
+//! Since version 1.0, the API and internal architecture of this crate is slightly diverging from `krabsetw`, so that it is more Rust-idiomatic.
//!
//! # What's ETW
//! Event Tracing for Windows (ETW) is an efficient kernel-level tracing facility that lets you log
@@ -30,15 +31,6 @@
//! would simplify ETW management written in Rust and available as a crate for other to consume would
//! be pretty neat and that's where this crate comes into play 🔥
//!
-//! # Disclaimer
-//! This project is still WIP. There's still plenty of things to evaluate/investigate and things to
-//! fix and do better. Any help would be greatly appreciated, also any issues you may have!
-//!
-//! Although I encourage everyone to use Rust, I do believe that, at the moment, if you plan on interacting
-//! with ETW in a production level and the programming language is not a constraint you should definitely
-//! consider [KrabsETW] as a more robust and tested option. Hopefully in next iterations I'll be able
-//! to remove this disclaimer 😃
-//!
//! # Getting started
//! If you are familiar with KrabsEtw you'll see using the crate is very similar, in case you are not
//! familiar with it the following example shows the basics on how to build a provider, start a trace
@@ -53,26 +45,27 @@
//! use ferrisetw::trace::{UserTrace, TraceTrait};
//!
//! fn process_callback(record: &EventRecord, schema_locator: &SchemaLocator) {
-//! // Within the callback we first locate the proper Schema for the event
-//! match schema_locator.event_schema(record) {
-//! Ok(schema) => {
-//! // At the moment we can only filter by checking the event_id
-//! if record.event_id() == 2 {
+//! // Basic event scrutinizing can be done directly from the `EventRecord`
+//! if record.event_id() == 2 {
+//! // More advanced info can be retrieved from the event schema
+//! // (the SchemaLocator caches the schema for a given kind of event, so this call is cheap in case you've already encountered the same event kind previously)
+//! match schema_locator.event_schema(record) {
+//! Err(err) => println!("Error {:?}", err),
+//! Ok(schema) => {
+//! println!("Received an event from provider {}", schema.provider_name());
+//!
+//! // Finally, properties for a given event can be retrieved using a Parser
+//! let parser = Parser::create(record, &schema);
//!
-//! // We build the Parser based on the Schema
-//! let mut parser = Parser::create(record, &schema);
-//!
-//! // Finally, Parse data from the Event, proper error handling should be done
//! // Type annotations or Fully Qualified Syntax are needed when calling TryParse
//! // Supported types implement the trait TryParse for Parser
-//!
+//! // In actual code, be sure to correctly handle Err values!
//! let process_id: u32 = parser.try_parse("ProcessID").unwrap();
//! let image_name: String = parser.try_parse("ImageName").unwrap();
//! println!("PID: {} ImageName: {}", process_id, image_name);
//! }
//! }
-//! Err(err) => println!("Error {:?}", err),
-//! };
+//! }
//! }
//!
//! fn main() {
diff --git a/src/native/etw_types.rs b/src/native/etw_types.rs
index 5375fec..273e2ee 100644
--- a/src/native/etw_types.rs
+++ b/src/native/etw_types.rs
@@ -262,7 +262,7 @@ impl<'callbackdata> EventTraceLogfile<'callbackdata> {
/// Newtype wrapper over an [ENABLE_TRACE_PARAMETERS]
///
-/// [ENABLE_TRACE_PARAMETERS]: https://microsoft.github.io/windows-docs-rs/doc/bindings/Windows/Win32/Etw/struct.ENABLE_TRACE_PARAMETERS.html
+/// [ENABLE_TRACE_PARAMETERS]: https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Diagnostics/Etw/struct.ENABLE_TRACE_PARAMETERS.html
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct EnableTraceParameters<'filters>{
@@ -279,6 +279,9 @@ impl<'filters> EnableTraceParameters<'filters> {
params.native.EnableProperty = trace_flags.bits();
+ // Note: > Each type of filter (a specific Type member) may only appear once in a call to the EnableTraceEx2 function.
+ // https://learn.microsoft.com/en-us/windows/win32/api/evntrace/nf-evntrace-enabletraceex2#remarks
+ // > The maximum number of filters that can be included in a call to EnableTraceEx2 is set by MAX_EVENT_FILTERS_COUNT
let mut win_filter_descriptors: Vec = filters
.iter()
.map(|efd| efd.as_event_filter_descriptor())
@@ -305,7 +308,7 @@ impl<'filters> EnableTraceParameters<'filters> {
/// Wrapper over the [DECODING_SOURCE] type
///
-/// [DECODING_SOURCE]: https://microsoft.github.io/windows-docs-rs/doc/bindings/Windows/Win32/Etw/struct.DECODING_SOURCE.html
+/// [DECODING_SOURCE]: https://learn.microsoft.com/en-us/windows/win32/api/tdh/ne-tdh-decoding_source
#[derive(Debug)]
pub enum DecodingSource {
DecodingSourceXMLFile,
diff --git a/src/native/etw_types/event_record.rs b/src/native/etw_types/event_record.rs
index 5647d8f..8248fd6 100644
--- a/src/native/etw_types/event_record.rs
+++ b/src/native/etw_types/event_record.rs
@@ -126,7 +126,7 @@ impl EventRecord {
}
}
- /// Returns the ExtendedData from the ETW Event
+ /// Returns the `ExtendedData` from the ETW Event
///
/// Their availability is mostly determined by the flags passed to [`Provider::trace_flags`](crate::provider::Provider::trace_flags)
///
diff --git a/src/provider/event_filter.rs b/src/provider/event_filter.rs
index ed6071f..581cc8d 100644
--- a/src/provider/event_filter.rs
+++ b/src/provider/event_filter.rs
@@ -6,10 +6,15 @@ use windows::Win32::System::Diagnostics::Etw::{EVENT_FILTER_DESCRIPTOR, EVENT_FI
use windows::Win32::System::Diagnostics::Etw::{MAX_EVENT_FILTER_EVENT_ID_COUNT, MAX_EVENT_FILTER_PID_COUNT};
/// Specifies how this provider will filter its events
+///
+/// Some filters are not effective prior to Windows 8.1 ([source](https://learn.microsoft.com/en-us/windows/win32/api/evntprov/ns-evntprov-event_filter_descriptor#remarks))
#[derive(Debug)]
pub enum EventFilter {
/// Filter by PID.
/// This is only effective on kernel mode logger session.
+ /// TODO: even for `KernelTrace`, this does not seem to work.
+ /// Maybe there's a distinction between "a trace run in kernel-mode" and a "System trace"?
+ /// See
ByPids(Vec),
/// Filter by ETW Event ID.
ByEventIds(Vec),