├── .github └── workflows │ └── ci-workflow.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE ├── README.md ├── SECURITY.md ├── examples └── hello_world │ ├── Cargo.toml │ └── src │ └── main.rs ├── win_etw_logger ├── Cargo.toml ├── examples │ └── hello.rs └── src │ └── lib.rs ├── win_etw_macros ├── Cargo.toml └── src │ ├── errors.rs │ ├── lib.rs │ ├── tests.rs │ └── well_known_types.rs ├── win_etw_metadata ├── Cargo.toml └── src │ └── lib.rs ├── win_etw_provider ├── Cargo.toml └── src │ ├── data_descriptor.rs │ ├── guid.rs │ ├── lib.rs │ ├── provider.rs │ └── types.rs └── win_etw_tracing ├── Cargo.toml └── src └── lib.rs /.github/workflows/ci-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | RUSTFLAGS: -Dwarnings 11 | 12 | jobs: 13 | test: 14 | strategy: 15 | matrix: 16 | rust: [1.77.0, stable] 17 | os: [windows-latest, ubuntu-latest] 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions-rs/toolchain@v1 22 | with: 23 | toolchain: ${{ matrix.rust }} 24 | profile: minimal 25 | override: true 26 | components: rustfmt 27 | 28 | - name: Build 29 | run: cargo build --verbose 30 | - name: Run tests 31 | run: cargo test --verbose 32 | - name: fmt 33 | if: matrix.rust == 'stable' 34 | run: cargo fmt --all -- --check 35 | 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | 4 | /.vscode 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "examples/hello_world", 6 | "win_etw_logger", 7 | "win_etw_macros", 8 | "win_etw_metadata", 9 | "win_etw_provider", 10 | "win_etw_tracing", 11 | ] 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust support for Event Tracing for Windows (ETW) 2 | 3 | Provides the `#[trace_logging_provider]` macro, which allows you to define a 4 | [Trace Logging Provider](https://docs.microsoft.com/en-us/windows/win32/etw/about-event-tracing#providers) 5 | for use with the [Event Tracing for Windows (ETW)](https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-portal) 6 | framework. 7 | 8 | This macro is intended for use only when targeting Windows. When targeting other platforms, 9 | this macro will still work, but will generate code that does nothing. 10 | 11 | This framework allows applications to log schematized events, rather than textual strings. 12 | ETW analysis tools can reliably identify fields within your events, and treat them as 13 | strongly-typed data, rather than text strings. 14 | 15 | To use [tracing](https://tracing.rs) with ETW, see [tracing-etw](https://github.com/microsoft/tracing-etw). 16 | 17 | ## How to create and use an event provider 18 | 19 | In ETW, an _event provider_ is a software object that generates events. _Event controllers_ 20 | set up event logging sessions, and _event consumers_ read and interpret event data. This crate 21 | focuses on enabling applications to create _event providers_. 22 | 23 | ### Add crate dependencies 24 | 25 | Add these dependencies to your `Cargo.toml` file: 26 | 27 | ```text 28 | [dependencies] 29 | win_etw_macros = "0.1.*" 30 | win_etw_provider = "0.1.*" 31 | ``` 32 | 33 | `win_etw_macros` contains the procedural macro that generates eventing code. 34 | `win_etw_provider` contains library code that is called by the code that is generated by 35 | `win_etw_macros`. 36 | 37 | ### Define the event provider and its events 38 | 39 | Add a trait definition to your source code and annotate it with the `#[trace_logging_provider]` 40 | macro. The `#[trace_logging_provider]` macro consumes the trait definition and produces a `struct` 41 | definition with the same name and the same method signatures. (The trait is _not_ available for 42 | use as an ordinary trait.): 43 | 44 | ```rust 45 | #[trace_logging_provider] 46 | pub trait MyAppEvents {} 47 | ``` 48 | 49 | [Each event provider _must_ have a unique name _and_ GUID.](https://learn.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-tracelogging_define_provider#provider-name-and-id) 50 | ETW uses this GUID to identify events that are generated by your provider. Windows contains many 51 | event providers, so it is important to be able to select only the events generated by your 52 | application. This GUID is also used internally by ETW to identify event metadata (field types), so 53 | it is important that your GUID be unique. Otherwise, events from conflicting sources that use the 54 | same GUID may be incorrectly interpreted. 55 | 56 | #### Specify your provider name 57 | 58 | Unless overridden, `#[trace_logging_provider]` uses the name of your trait definition as the name of ETW provider ("MyAppEvents" in the example aboved). 59 | To specify a different name, specify the name with `#[trace_logging_provider(name = "MyCompany.MyComponent")]`. 60 | 61 | #### Generate a GUID for your event provider 62 | 63 | The `#[trace_logging_provider]` macro will generate a .NET `EventSource`-compatible name-based GUID if you do not 64 | specify a `guid` parameter. The generated GUID is identical to the one generated by the following PowerShell code: 65 | `[System.Diagnostics.Tracing.EventSource]::new("MyCompany.MyComponent").Guid`. The GUID is accessible from your Rust code via the associated constant named `PROVIDER_GUID` (e.g., `MyAppEvents::PROVIDER_GUID`). 66 | 67 | If you are not interested in using a name-based GUID, you can generate a GUID using a tool like 68 | `uuidgen` (available from Visual Studio command line, or an Ubuntu shell) and specify it with 69 | `#[trace_logging_provider(guid = "... your guid here ...")]`. 70 | 71 | #### Add events to your provider 72 | 73 | In the trait definition, add method signatures. Each method signature defines an _event type_. 74 | The parameters of each method define the fields of the event type. Only a limited set of field 75 | types are supported (enumerated below). 76 | 77 | ```rust 78 | use win_etw_macros::trace_logging_provider; 79 | #[trace_logging_provider(name = "MyCompany.MyComponent")] 80 | pub trait MyAppEvents { 81 | fn http_request(client_address: &SockAddr, is_https: bool, status_code: u32, status: &str); 82 | fn database_connection_created(connection_id: u64, server: &str); 83 | fn database_connection_closed(connection_id: u64); 84 | // ... 85 | } 86 | ``` 87 | 88 | ### Create an instance of the event provider 89 | 90 | At initialization time (in your `fn main()`, etc.), create an instance of the event provider: 91 | 92 | ```rust 93 | let my_app_events = MyAppEvents::new(); 94 | ``` 95 | 96 | Your application should only create a single instance of each event provider, per process. 97 | That is, you should create a single instance of your event provider and share it across your 98 | process. Typically, an instance is stored in static variable, using a lazy / atomic assignment. 99 | There are many crates and types which can support this usage pattern. 100 | 101 | ### Call event methods to report events 102 | 103 | To report an event, call one of the methods defined on the event provider. The method will 104 | call into ETW to report the event, but there is no guarantee that the event is stored or 105 | forwarded; events can be dropped if event buffer resources are scarce. 106 | 107 | ```rust 108 | my_app_events.client_connected(None, &"192.168.0.42:6667".parse(), false, 100, "OK"); 109 | ``` 110 | 111 | Note that all generated event methods have an added first parameters, 112 | `options: Option<&EventOptions>`. This parameter allows you to override per-event parameters, 113 | such as the event level and event correlation IDs. In most cases, you should pass `None`. 114 | 115 | ## Supported field types 116 | 117 | Only a limited set of field types are supported. 118 | 119 | * Integer primitives up to 64 bits: `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64` 120 | * Floating point primitives: `f32`, `f64` 121 | * Architecture-dependent sizes: `usize`, `isize`. 122 | * Boolean: `bool` 123 | * Slices of all of the supported primitives: `&[u8]`, `&[u16]`, etc. 124 | * Windows [`FILETIME`](https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime). 125 | The type must be declared _exactly_ as `FILETIME`; type aliases or fully-qualified paths 126 | (such as `winapi::shared::minwindef::FILETIME`) _will not work_. The parameter type in the 127 | generated code will be `win_etw_provider::FILETIME`, which is a newtype over `u64`. 128 | * `std::time::SystemTime` is supported, but it must be declared _exactly_ as `SystemTime`; 129 | type aliases or fully-qualified paths (such as `std::time::SystemTime`) _will not work_. 130 | * `SockAddr`, `SockAddrV4`, and `SockAddrV6` are supported. They must be declared exactly as 131 | shown, not using fully-qualified names or type aliases. 132 | 133 | ## How to capture and view events 134 | 135 | There are a variety of tools which can be used to capture and view ETW events. 136 | The simplest tool is the `TraceView` tool from the Windows SDK. Typically it is installed 137 | at this path: `C:\Program Files (x86)\Windows Kits\10\bin\10.0..0\x64\traceview.exe`, 138 | where `` is the release number of the Windows SDK. 139 | 140 | Run `TraceView`, then select "File", then "Create New Log Session". Select "Manually Entered 141 | GUID or Hashed Name" and enter the GUID that you have assigned to your event provider. Click OK. 142 | The next dialog will prompt you to choose a source of WPP format information; select Auto 143 | and click OK. 144 | 145 | At this point, `TraceView` should be capturing events (for your assigned GUID) and displaying 146 | them in real time, regardless of which process reported the events. 147 | 148 | These tools can also be used to capture ETW events: 149 | 150 | * [Windows Performance Recorder](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-recorder) 151 | This tool is intended for capturing system-wide event streams. It is not useful for capturing 152 | events for a specific event provider. 153 | * [logman](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/logman) 154 | is a command-line tool for managing events. 155 | * [Tracelog](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/tracelog) 156 | 157 | There are other tools, such as the Windows Performance Recorder, which can capture ETW events. 158 | 159 | ## Ideas for improvement 160 | 161 | * Better handling of per-event overrides, rather than using `Option<&EventOptions>`. 162 | 163 | ## References 164 | 165 | * [Event Tracing for Windows (ETW) Simplified](https://support.microsoft.com/en-us/help/2593157/event-tracing-for-windows-etw-simplified) 166 | * [TraceLogging for Event Tracing for Windows (ETW)](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-portal) 167 | * [Record and View TraceLogging Events](https://docs.microsoft.com/en-us/windows/win32/tracelogging/tracelogging-record-and-display-tracelogging-events) 168 | * [TraceLoggingOptionGroup](https://docs.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-traceloggingoptiongroup) 169 | * [Provider Traits](https://docs.microsoft.com/en-us/windows/win32/etw/provider-traits) 170 | * [TRACELOGGING_DEFINE_PROVIDER macro](https://learn.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-tracelogging_define_provider) 171 | 172 | ## Contributing 173 | 174 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 175 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 176 | the rights to use your contribution. For details, visit . 177 | 178 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 179 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 180 | provided by the bot. You will only need to do this once across all repos using our CLA. 181 | 182 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 183 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 184 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 185 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /examples/hello_world/Cargo.toml: -------------------------------------------------------------------------------- 1 | # This package is not published. 2 | 3 | [package] 4 | name = "hello_world" 5 | version = "0.1.1" 6 | authors = ["Arlie Davis "] 7 | edition = "2018" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | win_etw_macros = { path = "../../win_etw_macros" } 13 | win_etw_provider = { path = "../../win_etw_provider", features = ["std"] } 14 | widestring = "^1.0" 15 | winapi = { version = "^0.3.8", features = ["ntstatus"] } 16 | 17 | [features] 18 | default = [] 19 | -------------------------------------------------------------------------------- /examples/hello_world/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unreadable_literal)] 2 | #![forbid(unsafe_code)] 3 | use win_etw_macros::trace_logging_provider; 4 | 5 | use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; 6 | use std::time::{Duration, SystemTime}; 7 | use widestring::{U16CString, U16String}; 8 | use win_etw_provider::{guid, FILETIME, GUID}; 9 | 10 | // {861A3948-3B6B-4DDF-B862-B2CB361E238E} 11 | // DEFINE_GUID(my_provider_guid, 0x861a3948, 0x3b6b, 0x4ddf, 0xb8, 0x62, 0xb2, 0xcb, 0x36, 0x1e, 0x23, 0x8e); 12 | const EXAMPLE_GUID: GUID = 13 | guid!(0x861a3948, 0x3b6b, 0x4ddf, 0xb8, 0x62, 0xb2, 0xcb, 0x36, 0x1e, 0x23, 0x8e); 14 | 15 | fn main() { 16 | let hello_provider = HelloWorldProvider::new(); 17 | 18 | hello_provider.arg_str(None, "Hello, world!"); 19 | hello_provider.arg_slice_u8(None, &[44, 55, 66]); 20 | hello_provider.arg_slice_i32(None, &[10001, 20002, 30003]); 21 | hello_provider.arg_f32(None, core::f32::consts::PI); 22 | hello_provider.arg_guid(None, &EXAMPLE_GUID); 23 | 24 | let client_addr_v4: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::new(192, 168, 23, 42), 6667); 25 | hello_provider.client_connected_v4(None, &client_addr_v4); 26 | 27 | let client_addr_v6 = "[2001:db8::1]:8080".parse::().unwrap(); 28 | hello_provider.client_connected_v6(None, &client_addr_v6); 29 | 30 | hello_provider.client_connected(None, &SocketAddr::V4(client_addr_v4)); 31 | hello_provider.client_connected(None, &SocketAddr::V6(client_addr_v6)); 32 | 33 | hello_provider.something_bad_happened(None, "uh oh!"); 34 | 35 | hello_provider.file_created(None, SystemTime::now()); 36 | hello_provider.file_created_filetime( 37 | None, 38 | FILETIME((11644473600 + (3 * 365 + 31 + 28 + 31 + 30 + 31 + 15) * 86400) * 10_000_000), 39 | ); 40 | 41 | hello_provider.arg_u32_hex(None, 0xcafef00d); 42 | 43 | use std::ffi::OsString; 44 | 45 | hello_provider.arg_u16str(None, &U16String::from_str("this is a u16str")); 46 | hello_provider.arg_u16cstr(None, &U16CString::from_str("this is a u16cstr").unwrap()); 47 | hello_provider.arg_osstr(None, &OsString::from("hello!")); 48 | 49 | #[cfg(target_os = "windows")] 50 | { 51 | pub use winapi::shared::ntstatus; 52 | pub use winapi::shared::winerror; 53 | 54 | hello_provider.arg_hresult(None, winerror::DXGI_DDI_ERR_WASSTILLDRAWING); 55 | hello_provider.arg_ntstatus(None, ntstatus::STATUS_DEVICE_REQUIRES_CLEANING as u32); 56 | hello_provider.arg_win32error(None, winerror::ERROR_OUT_OF_PAPER); 57 | } 58 | 59 | let args = std::env::args().collect::>(); 60 | if args.len() >= 2 && args[1] == "loop" { 61 | eprintln!("looping"); 62 | loop { 63 | std::thread::sleep(Duration::from_millis(3000)); 64 | hello_provider.hello(None, "Looping... (from Rust)"); 65 | hello_provider.message_at_critical(None, "something super exciting happened!"); 66 | hello_provider.message_at_error(None, "something pretty bad happened!"); 67 | hello_provider.message_at_warn(None, "something warning-worthy happened"); 68 | hello_provider.message_at_info(None, "something normal happened"); 69 | hello_provider.message_at_verbose(None, "noisy noisy noisy"); 70 | hello_provider.message_at_level_8(None, "incredibly detailed level 8 tracing"); 71 | } 72 | } 73 | } 74 | 75 | /// Hello, World, from ETW 76 | #[trace_logging_provider( 77 | guid = "861A3948-3B6B-4DDF-B862-B2CB361E238E", 78 | provider_group_guid = "6aeb6059-444a-4606-a3ea-06fd00fe3378" 79 | )] 80 | trait HelloWorldProvider { 81 | fn hello(a: &str); 82 | fn arg_i32(a: i32); 83 | fn arg_u8(a: u8); 84 | 85 | /// Log a floating point value. 86 | #[event(level = "info")] 87 | fn arg_f32(a: f32); 88 | 89 | fn arg_slice_u8(arg: &[u8]); 90 | fn arg_slice_i32(arg: &[i32]); 91 | fn arg_str(arg: &str); 92 | 93 | fn arg_guid(arg: &GUID); 94 | 95 | #[event(level = "error")] 96 | fn something_bad_happened(message: &str); 97 | 98 | #[event(task = 42, opcode = 99)] 99 | fn client_connected_v4(client_addr: &SocketAddrV4); 100 | 101 | #[event(task = 42, opcode = 99)] 102 | fn client_connected_v6(client_addr: &SocketAddrV6); 103 | 104 | #[event(task = 42, opcode = 99)] 105 | fn client_connected(client_addr: &SocketAddr); 106 | 107 | fn file_created(create_time: SystemTime); 108 | 109 | fn file_created_filetime(t: FILETIME); 110 | 111 | fn arg_bool(a: bool); 112 | 113 | fn arg_usize(a: usize); 114 | fn arg_isize(a: isize); 115 | 116 | fn arg_u32_hex(#[event(output = "hex")] a: u32); 117 | 118 | fn arg_hresult(a: HRESULT); 119 | fn arg_ntstatus(a: NTSTATUS); 120 | fn arg_win32error(a: WIN32ERROR); 121 | 122 | fn arg_u16str(a: &U16Str); 123 | fn arg_u16cstr(a: &U16CStr); 124 | fn arg_osstr(a: &OsStr); 125 | 126 | #[event(level = "critical")] 127 | fn message_at_critical(msg: &str); 128 | 129 | #[event(level = "info")] 130 | fn message_at_info(msg: &str); 131 | 132 | #[event(level = "warn")] 133 | fn message_at_warn(msg: &str); 134 | 135 | #[event(level = "error")] 136 | fn message_at_error(msg: &str); 137 | 138 | #[event(level = "verbose")] 139 | fn message_at_verbose(msg: &str); 140 | 141 | #[event(level = 8)] 142 | fn message_at_level_8(msg: &str); 143 | } 144 | 145 | #[trace_logging_provider(guid = "76d66486-d11a-47a8-af05-88942b6edb55")] 146 | trait AnotherFineProvider { 147 | fn arg_str(arg: &str); 148 | } 149 | 150 | #[trace_logging_provider(guid = "b9978f10-b3e0-4bbe-a4f2-160a2e7148d6")] 151 | trait TestManyEvents { 152 | fn arg_none(); 153 | fn arg_bool(a: bool); 154 | fn arg_u8(a: u8); 155 | fn arg_u16(a: u16); 156 | fn arg_u32(a: u32); 157 | fn arg_u64(a: u64); 158 | fn arg_i8(a: i8); 159 | fn arg_i16(a: i16); 160 | fn arg_i32(a: i32); 161 | fn arg_i64(a: i64); 162 | fn arg_f32(a: f32); 163 | fn arg_f64(a: f64); 164 | fn arg_usize(a: usize); 165 | fn arg_isize(a: isize); 166 | 167 | fn arg_slice_bool(a: &[bool]); 168 | fn arg_slice_u8(a: &[u8]); 169 | fn arg_slice_u16(a: &[u16]); 170 | fn arg_slice_u32(a: &[u32]); 171 | fn arg_slice_u64(a: &[u64]); 172 | fn arg_slice_i8(a: &[i8]); 173 | fn arg_slice_i16(a: &[i16]); 174 | fn arg_slice_i32(a: &[i32]); 175 | fn arg_slice_i64(a: &[i64]); 176 | fn arg_slice_f32(a: &[f32]); 177 | fn arg_slice_f64(a: &[f64]); 178 | fn arg_slice_usize(a: &[usize]); 179 | fn arg_slice_isize(a: &[isize]); 180 | 181 | fn arg_str(arg: &str); 182 | fn arg_guid(arg: &GUID); 183 | fn arg_system_time(a: SystemTime); 184 | fn arg_filetime(a: FILETIME); 185 | 186 | #[event(level = "critical")] 187 | fn arg_u8_at_critical(a: u8); 188 | 189 | #[event(level = "info")] 190 | fn arg_u8_at_info(a: u8); 191 | 192 | #[event(level = "warn")] 193 | fn arg_u8_at_warn(a: u8); 194 | 195 | #[event(level = "error")] 196 | fn arg_u8_at_error(a: u8); 197 | 198 | #[event(level = "verbose")] 199 | fn arg_u8_at_verbose(a: u8); 200 | 201 | #[event(level = 8)] 202 | fn arg_u8_at_level_8(a: u8); 203 | 204 | #[event(task = 100)] 205 | fn arg_with_task(a: u8); 206 | 207 | #[event(opcode = 10)] 208 | fn arg_with_opcode(a: u8); 209 | 210 | fn arg_u32_hex(#[event(output = "hex")] a: u32); 211 | 212 | fn arg_hresult(a: HRESULT); 213 | fn arg_ntstatus(a: NTSTATUS); 214 | fn arg_win32error(a: WIN32ERROR); 215 | } 216 | 217 | #[trace_logging_provider] 218 | trait ProviderWithAutogeneratedGuid { 219 | fn arg_str(arg: &str); 220 | } 221 | 222 | #[trace_logging_provider(name = "Your.Provider.Name")] 223 | trait ProviderWithAutogeneratedGuidAndCustomProviderName { 224 | fn arg_str(arg: &str); 225 | } 226 | 227 | #[trace_logging_provider( 228 | name = "Another.Provider.Name", 229 | guid = "86fe5bee-dec7-40f2-b39b-a46c20633042" 230 | )] 231 | trait ProviderWithCustomGuidAndCustomProviderName { 232 | fn arg_str(arg: &str); 233 | } 234 | 235 | #[cfg(test)] 236 | mod tests { 237 | use super::{ 238 | ProviderWithAutogeneratedGuid, ProviderWithAutogeneratedGuidAndCustomProviderName, 239 | ProviderWithCustomGuidAndCustomProviderName, 240 | }; 241 | use win_etw_provider::guid; 242 | 243 | #[test] 244 | fn provider_with_autogenerated_guid() { 245 | assert_eq!( 246 | "ProviderWithAutogeneratedGuid", 247 | ProviderWithAutogeneratedGuid::PROVIDER_NAME 248 | ); 249 | 250 | assert_eq!( 251 | guid!( 252 | 0x0d31f5cc, 253 | 0xfb84, 254 | 0x50db, 255 | [0xa6, 0x02, 0x8c, 0x7b, 0xed, 0x9c, 0x5b, 0x8b] 256 | ), 257 | ProviderWithAutogeneratedGuid::PROVIDER_GUID, 258 | ); 259 | } 260 | 261 | #[test] 262 | fn provider_with_autogenerated_guid_and_custom_provider_name() { 263 | assert_eq!( 264 | "Your.Provider.Name", 265 | ProviderWithAutogeneratedGuidAndCustomProviderName::PROVIDER_NAME 266 | ); 267 | 268 | assert_eq!( 269 | guid!( 270 | 0x0cec4c9d, 271 | 0xcaa7, 272 | 0x5d85, 273 | [0xb3, 0xbd, 0xc7, 0x35, 0x77, 0xf0, 0x3f, 0xd8] 274 | ), 275 | ProviderWithAutogeneratedGuidAndCustomProviderName::PROVIDER_GUID 276 | ); 277 | } 278 | 279 | #[test] 280 | fn provider_with_custom_guid_and_custom_provider_name() { 281 | assert_eq!( 282 | "Another.Provider.Name", 283 | ProviderWithCustomGuidAndCustomProviderName::PROVIDER_NAME 284 | ); 285 | 286 | assert_eq!( 287 | guid!( 288 | 0x86fe5bee, 289 | 0xdec7, 290 | 0x40f2, 291 | [0xb3, 0x9b, 0xa4, 0x6c, 0x20, 0x63, 0x30, 0x42] 292 | ), 293 | ProviderWithCustomGuidAndCustomProviderName::PROVIDER_GUID 294 | ); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /win_etw_logger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "win_etw_logger" 3 | version = "0.1.7" 4 | authors = ["Arlie Davis "] 5 | edition = "2018" 6 | description = "A Rust log provider which forwards events to Event Tracing for Windows (ETW)." 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/microsoft/rust_win_etw" 9 | readme = "../README.md" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | log = { version = "^0.4", features = ["std"] } 15 | win_etw_provider = { version = "0.1.9", path = "../win_etw_provider" } 16 | win_etw_macros = { version = "0.1.7", path = "../win_etw_macros" } 17 | win_etw_metadata = { version = "0.1.2", path = "../win_etw_metadata" } 18 | winapi = { version = "^0.3.8" } 19 | -------------------------------------------------------------------------------- /win_etw_logger/examples/hello.rs: -------------------------------------------------------------------------------- 1 | use log::{debug, error, info, log_enabled, trace, warn, Level}; 2 | use std::time::Duration; 3 | use win_etw_logger::TraceLogger; 4 | 5 | fn main() { 6 | let logger = TraceLogger::new().unwrap(); 7 | 8 | log::set_boxed_logger(Box::new(logger)).unwrap(); 9 | log::set_max_level(log::LevelFilter::Debug); 10 | 11 | info!("Rust logging through ETW! n = {}", 42); 12 | warn!("This is too much fun"); 13 | debug!("maybe we can make this code work"); 14 | 15 | let args = std::env::args().collect::>(); 16 | if args.len() >= 2 && args[1] == "loop" { 17 | eprintln!("looping"); 18 | loop { 19 | std::thread::sleep(Duration::from_millis(3000)); 20 | error!("error: something pretty bad happened!"); 21 | warn!("warn: something warning-worthy happened"); 22 | info!("info: something normal happened"); 23 | debug!("debug: noisy debug noisy debug"); 24 | trace!("trace: noisy tracing noisy tracing"); 25 | 26 | let error_is_enabled = log_enabled!(Level::Error); 27 | let warn_is_enabled = log_enabled!(Level::Warn); 28 | let info_is_enabled = log_enabled!(Level::Info); 29 | let debug_is_enabled = log_enabled!(Level::Debug); 30 | let trace_is_enabled = log_enabled!(Level::Trace); 31 | eprintln!( 32 | "is_enabled? error: {:5?}, warn: {:5?}, info: {:5?}, debug: {:5?}, trace: {:5?}", 33 | error_is_enabled, 34 | warn_is_enabled, 35 | info_is_enabled, 36 | debug_is_enabled, 37 | trace_is_enabled, 38 | ); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /win_etw_logger/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Provides a `log::Log` implementation that sends events to Event Tracing for Windows (ETW). 2 | 3 | #![no_std] 4 | #![deny(missing_docs)] 5 | #![forbid(unsafe_code)] 6 | #![allow(clippy::unreadable_literal)] 7 | #![allow(clippy::useless_let_if_seq)] 8 | 9 | extern crate alloc; 10 | use alloc::string::ToString; 11 | use core::sync::atomic::{AtomicBool, Ordering}; 12 | use win_etw_macros::trace_logging_provider; 13 | 14 | #[trace_logging_provider(guid = "7f006a22-73fb-4c17-b1eb-0a3070f9f187")] 15 | trait RustLogProvider { 16 | fn log(module_path: &str, file: &str, line: u32, message: &str); 17 | } 18 | 19 | /// Provides a `log::Log` implementation that sends events to Event Tracing for Windows (ETW). 20 | pub struct TraceLogger { 21 | provider: RustLogProvider, 22 | log_module_path: AtomicBool, 23 | log_file_path: AtomicBool, 24 | } 25 | 26 | impl TraceLogger { 27 | /// Registers the `TraceLogger` with ETW. 28 | pub fn new() -> Result { 29 | let provider = RustLogProvider::new(); 30 | Ok(TraceLogger { 31 | provider, 32 | log_module_path: AtomicBool::new(true), 33 | log_file_path: AtomicBool::new(true), 34 | }) 35 | } 36 | 37 | /// Controls whether Rust module paths are included in event records. 38 | /// The default is `true` (module paths are included). 39 | /// This is provided to give control over privacy and to control the size of event records. 40 | pub fn set_log_module_path(&self, value: bool) { 41 | self.log_module_path.store(value, Ordering::Release); 42 | } 43 | 44 | /// Controls whether source file names and line numbers are included in event 45 | /// records. The default is `true` (source file names and line numbers are included). 46 | /// This is provided to give control over privacy and to control the size of event records. 47 | pub fn set_log_file_path(&self, value: bool) { 48 | self.log_file_path.store(value, Ordering::Release); 49 | } 50 | 51 | /// Returns `true` if this logger will include Rust module paths in event records. 52 | pub fn log_module_path(&self) -> bool { 53 | self.log_module_path.load(Ordering::Acquire) 54 | } 55 | 56 | /// Returns `true` if this logger will include source file names and line numbers in event 57 | /// records. 58 | pub fn log_file_path(&self) -> bool { 59 | self.log_file_path.load(Ordering::Acquire) 60 | } 61 | } 62 | 63 | fn level_to_etw(level: log::Level) -> win_etw_provider::Level { 64 | match level { 65 | log::Level::Error => win_etw_provider::Level::ERROR, 66 | log::Level::Warn => win_etw_provider::Level::WARN, 67 | log::Level::Info => win_etw_provider::Level::INFO, 68 | log::Level::Debug => win_etw_provider::Level::VERBOSE, 69 | log::Level::Trace => win_etw_provider::Level(6), 70 | } 71 | } 72 | 73 | impl log::Log for TraceLogger { 74 | fn enabled(&self, metadata: &log::Metadata) -> bool { 75 | self.provider 76 | .log_is_enabled(Some(level_to_etw(metadata.level()))) 77 | } 78 | 79 | fn log(&self, record: &log::Record) { 80 | let module_path = if self.log_module_path() { 81 | record.module_path().unwrap_or("") 82 | } else { 83 | "" 84 | }; 85 | 86 | let file_path; 87 | let file_line; 88 | if self.log_file_path() { 89 | file_path = record.file().unwrap_or(""); 90 | file_line = record.line().unwrap_or(0); 91 | } else { 92 | file_path = ""; 93 | file_line = 0; 94 | } 95 | 96 | let message = record.args().to_string(); 97 | let metadata = record.metadata(); 98 | 99 | let options = win_etw_provider::EventOptions { 100 | level: Some(level_to_etw(metadata.level())), 101 | ..Default::default() 102 | }; 103 | self.provider 104 | .log(Some(&options), module_path, file_path, file_line, &message); 105 | } 106 | 107 | fn flush(&self) {} 108 | } 109 | -------------------------------------------------------------------------------- /win_etw_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "win_etw_macros" 3 | version = "0.1.9" 4 | authors = ["Arlie Davis "] 5 | edition = "2018" 6 | description = "Enables apps to report events to Event Tracing for Windows (ETW)." 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/microsoft/rust_win_etw" 9 | readme = "../README.md" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | proc-macro2 = "^1.0" 18 | syn = { version = "^1.0", features = ["full", "extra-traits"] } 19 | quote = "^1.0" 20 | win_etw_metadata = { version = "0.1.2", path = "../win_etw_metadata" } 21 | uuid = { version = "^1.3", features = ["v5"]} 22 | sha1_smol = "1.0.0" 23 | -------------------------------------------------------------------------------- /win_etw_macros/src/errors.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | use syn::Error; 3 | 4 | #[derive(Default)] 5 | pub(crate) struct CombinedErrors { 6 | error: Option, 7 | } 8 | 9 | impl CombinedErrors { 10 | pub fn push(&mut self, error: Error) { 11 | match self.error.as_mut() { 12 | Some(existing) => existing.combine(error), 13 | None => self.error = Some(error), 14 | } 15 | } 16 | pub fn into_result(self, value: T) -> Result { 17 | match self.error { 18 | Some(error) => Err(error), 19 | None => Ok(value), 20 | } 21 | } 22 | pub fn scope<'a, F: FnOnce(&mut ErrorScope<'a>) -> Result<(), Error>>( 23 | &'a mut self, 24 | span: Span, 25 | f: F, 26 | ) { 27 | let mut scope = ErrorScope { errors: self, span }; 28 | match f(&mut scope) { 29 | Ok(()) => {} 30 | Err(e) => { 31 | scope.errors.push(e); 32 | } 33 | } 34 | } 35 | } 36 | 37 | pub(crate) struct ErrorScope<'a> { 38 | span: Span, 39 | errors: &'a mut CombinedErrors, 40 | } 41 | 42 | impl<'a> ErrorScope<'a> { 43 | pub fn msg(&mut self, s: &str) { 44 | self.errors.push(Error::new(self.span, s)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /win_etw_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Provides the `#[trace_logging_provider]` macro, which allows you to define a 2 | //! [Trace Logging Provider](https://docs.microsoft.com/en-us/windows/win32/etw/about-event-tracing#providers) 3 | //! for use with the [Event Tracing for Windows (ETW)](https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-portal) 4 | //! framework. 5 | //! 6 | //! This macro is intended for use only when targeting Windows. When targeting other platforms, 7 | //! this macro will still work, but will generate code that does nothing. 8 | //! 9 | //! This framework allows applications to log schematized events, rather than textual strings. 10 | //! ETW analysis tools can reliably identify fields within your events, and treat them as 11 | //! strongly-typed data, rather than text strings. 12 | //! 13 | //! ## How to create and use an event provider 14 | //! 15 | //! In ETW, an _event provider_ is a software object that generates events. _Event controllers_ 16 | //! set up event logging sessions, and _event consumers_ read and interpret event data. This crate 17 | //! focuses on enabling applications to create _event providers_. 18 | //! 19 | //! ## Add crate dependencies 20 | //! Add these dependencies to your `Cargo.toml` file: 21 | //! 22 | //! ```text 23 | //! [dependencies] 24 | //! win_etw_macros = "0.1.*" 25 | //! win_etw_provider = "0.1.*" 26 | //! ``` 27 | //! 28 | //! `win_etw_macros` contains the procedural macro that generates eventing code. 29 | //! `win_etw_provider` contains library code that is called by the code that is generated by 30 | //! `win_etw_macros`. 31 | //! 32 | //! ### Define the event provider and its events 33 | //! 34 | //! Add a trait definition to your source code and annotate it with the `#[trace_logging_provider]` 35 | //! macro. The `#[trace_logging_provider]` macro consumes the trait definition and produces a `struct` 36 | //! definition with the same name and the same method signatures. (The trait is _not_ available for 37 | //! use as an ordinary trait.): 38 | //! 39 | //! ```rust,ignore 40 | //! #[trace_logging_provider] 41 | //! pub trait MyAppEvents {} 42 | //! ``` 43 | //! 44 | //! [Each event provider _must_ have a unique name _and_ GUID.](https://learn.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-tracelogging_define_provider#provider-name-and-id) 45 | //! ETW uses this GUID to identify events that are generated by your provider. Windows contains many 46 | //! event providers, so it is important to be able to select only the events generated by your 47 | //! application. This GUID is also used internally by ETW to identify event metadata (field types), so 48 | //! it is important that your GUID be unique. Otherwise, events from conflicting sources that use the 49 | //! same GUID may be incorrectly interpreted. 50 | //! 51 | //! #### Specify your provider name 52 | //! 53 | //! Unless overridden, `#[trace_logging_provider]` uses the name of your trait definition as the name of ETW provider ("MyAppEvents" in the example aboved). 54 | //! To specify a different name, specify the name with `#[trace_logging_provider(name = "MyCompany.MyComponent")]`. 55 | //! 56 | //! #### Generate a GUID for your event provider 57 | //! 58 | //! The `#[trace_logging_provider]` macro will generate a .NET `EventSource`-compatible name-based GUID if you do not 59 | //! specify a `guid` parameter. The generated GUID is identical to the one generated by the following PowerShell code: 60 | //! `[System.Diagnostics.Tracing.EventSource]::new("MyCompany.MyComponent").Guid`. The GUID is accessible from your Rust code via the associated constant named `PROVIDER_GUID` (e.g., `MyAppEvents::PROVIDER_GUID`). 61 | //! 62 | //! If you are not interested in using a name-based GUID, you can generate a GUID using a tool like 63 | //! `uuidgen` (available from Visual Studio command line, or an Ubuntu shell) and specify it with 64 | //! `#[trace_logging_provider(guid = "... your guid here ...")]`. 65 | //! 66 | //! #### Add events to your provider 67 | //! 68 | //! In the trait definition, add method signatures. Each method signature defines an _event type_. 69 | //! The parameters of each method define the fields of the event type. Only a limited set of field 70 | //! types are supported (enumerated below). 71 | //! 72 | //! ```rust,ignore 73 | //! use win_etw_macros::trace_logging_provider; 74 | //! #[trace_logging_provider(name = "MyCompany.MyComponent")] 75 | //! pub trait MyAppEvents { 76 | //! fn http_request(client_address: &SockAddr, is_https: bool, status_code: u32, status: &str); 77 | //! fn database_connection_created(connection_id: u64, server: &str); 78 | //! fn database_connection_closed(connection_id: u64); 79 | //! // ... 80 | //! } 81 | //! ``` 82 | //! 83 | //! ## Create an instance of the event provider 84 | //! At initialization time (in your `fn main()`, etc.), create an instance of the event provider: 85 | //! 86 | //! ```rust,ignore 87 | //! let my_app_events = MyAppEvents::new(); 88 | //! ``` 89 | //! 90 | //! Your application should only create a single instance of each event provider, per process. 91 | //! That is, you should create a single instance of your event provider and share it across your 92 | //! process. Typically, an instance is stored in static variable, using a lazy / atomic assignment. 93 | //! There are many crates and types which can support this usage pattern. 94 | //! 95 | //! ## Call event methods to report events 96 | //! To report an event, call one of the methods defined on the event provider. The method will 97 | //! call into ETW to report the event, but there is no guarantee that the event is stored or 98 | //! forwarded; events can be dropped if event buffer resources are scarce. 99 | //! 100 | //! ```rust,ignore 101 | //! my_app_events.client_connected(&"192.168.0.42:6667".parse(), false, 100, "OK"); 102 | //! ``` 103 | //! 104 | //! # Supported field types 105 | //! Only a limited set of field types are supported. 106 | //! 107 | //! * Integer primitives up to 64 bits: `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64` 108 | //! * Floating point primitives: `f32`, `f64` 109 | //! * Architecture-dependent sizes: `usize`, `isize`. 110 | //! * Boolean: `bool` 111 | //! * Slices of all of the supported primitives, except for bool: `&[u8]`, `&[u16]`, etc. 112 | //! `&[bool]` is not supported because `bool` does not have a guaranteed stable representation. 113 | //! * Windows `[FILETIME](https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime)`. 114 | //! The type must be declared _exactly_ as `FILETIME`; type aliases or fully-qualified paths 115 | //! (such as `winapi::shared::minwindef::FILETIME`) _will not work_. The parameter type in the 116 | //! generated code will be `win_etw_provider::FILETIME`, which is a newtype over `u64`. 117 | //! * `std::time::SystemTime` is supported, but it must be declared _exactly_ as `SystemTime`; 118 | //! type aliases or fully-qualified paths (such as `std::time::SystemTime`) _will not work_. 119 | //! * `SockAddr`, `SockAddrV4`, and `SockAddrV6` are supported. They must be declared exactly as 120 | //! shown, not using fully-qualified names or type aliases. 121 | //! 122 | //! # Provider groups 123 | //! 124 | //! When creating an ETW provider, you can place ETW providers into _provider groups_. A provider 125 | //! group can be enabled or disabled as a unit. To do so, specify the GUID of the provider group 126 | //! when declaring the ETW provider. For example: 127 | //! 128 | //! ```no_test 129 | //! [trace_logging_provider( 130 | //! guid = "...", // GUID of this provider 131 | //! provider_group_guid = "..." // GUID of the provider group that this provider belongs to 132 | //! )] 133 | //! pub trait MyEvents { 134 | //! // ... 135 | //! } 136 | //! ``` 137 | //! 138 | //! ## The `#[event]` attribute 139 | //! 140 | //! The `#[event]` atttribute allows you to control various aspects of each event type. 141 | //! It is not necessary to use the `#[event]` attribute; if it is not specified, then 142 | //! reasonable defaults will be chosen. You can use the `#[event]` attribute to control 143 | //! these aspects of each event type: 144 | //! 145 | //! * `#[event(id = NN)]` - Specifies the event ID. All event types declared on a specific 146 | //! event provider must have unique event IDs. See [EVENT_DESCRIPTOR]::Id. 147 | //! * `#[event(level = NN)]` or `#[event(level = "...")]` - Specifies the event level. 148 | //! See [EVENT_DESCRIPTOR]::Level. 149 | //! This can either be a numeric value, or one of the following literal strings: 150 | //! `"critical"`, `"error"`, `"warn"`, `"info"`, `"verbose"`. 151 | //! * `#[event(opcode = NN)]` - Specifies the [EVENT_DESCRIPTOR]::Opcode field. 152 | //! * `#[event(task = NN)` - Specifies the [EVENT_DESCRIPTOR]::Task field. 153 | //! * `#[event(keyword = NN)` - Specifies the [EVENT_DESCRIPTOR]::Keyword field. 154 | //! 155 | //! [EVENT_DESCRIPTOR]: https://docs.microsoft.com/en-us/windows/win32/api/evntprov/ns-evntprov-event_descriptor 156 | //! 157 | //! You can use a single `#[event]` attribute with multiple values, or you can use 158 | //! multiple `#[event]` attributes. 159 | //! 160 | //! # How to capture and view events 161 | //! 162 | //! There are a variety of tools which can be used to capture and view ETW events. 163 | //! The simplest tool is the `TraceView` tool from the Windows SDK. Typically it is installed 164 | //! at this path: `C:\Program Files (x86)\Windows Kits\10\bin\10.0..0\x64\traceview.exe`, 165 | //! where `` is the release number of the Windows SDK. 166 | //! 167 | //! Run `TraceView`, then select "File", then "Create New Log Session". Select "Manually Entered 168 | //! GUID or Hashed Name" and enter the GUID that you have assigned to your event provider. Click OK. 169 | //! The next dialog will prompt you to choose a source of WPP format information; select Auto 170 | //! and click OK. 171 | //! 172 | //! At this point, `TraceView` should be capturing events (for your assigned GUID) and displaying 173 | //! them in real time, regardless of which process reported the events. 174 | //! 175 | //! These tools can also be used to capture ETW events: 176 | //! * [Windows Performance Recorder](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-recorder) 177 | //! This tool is intended for capturing system-wide event streams. It is not useful for capturing 178 | //! events for a specific event provider. 179 | //! * [logman](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/logman) 180 | //! is a command-line tool for managing events. 181 | //! * [Tracelog](https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/tracelog) 182 | //! 183 | //! There are other tools, such as the Windows Performance Recorder, which can capture ETW events. 184 | //! 185 | //! # References 186 | //! * [Event Tracing for Windows (ETW) Simplified](https://support.microsoft.com/en-us/help/2593157/event-tracing-for-windows-etw-simplified) 187 | //! * [TraceLogging for Event Tracing for Windows (ETW)](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-portal) 188 | //! * [Record and View TraceLogging Events](https://docs.microsoft.com/en-us/windows/win32/tracelogging/tracelogging-record-and-display-tracelogging-events) 189 | //! * [TraceLoggingOptionGroup](https://docs.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-traceloggingoptiongroup) 190 | //! * [Provider Traits](https://docs.microsoft.com/en-us/windows/win32/etw/provider-traits) 191 | //! * [TRACELOGGING_DEFINE_PROVIDER macro](https://learn.microsoft.com/en-us/windows/win32/api/traceloggingprovider/nf-traceloggingprovider-tracelogging_define_provider) 192 | 193 | // https://doc.rust-lang.org/reference/procedural-macros.html 194 | 195 | #![deny(missing_docs)] 196 | #![allow(clippy::too_many_arguments)] 197 | #![allow(clippy::cognitive_complexity)] 198 | #![allow(clippy::single_match)] 199 | #![allow(clippy::upper_case_acronyms)] 200 | #![allow(clippy::vec_init_then_push)] 201 | 202 | extern crate proc_macro; 203 | 204 | mod errors; 205 | mod well_known_types; 206 | 207 | use errors::{CombinedErrors, ErrorScope}; 208 | use proc_macro2::TokenStream; 209 | use quote::{quote, quote_spanned}; 210 | use std::{collections::HashMap, iter::Extend}; 211 | use syn::spanned::Spanned; 212 | use syn::{parse_quote, Error, Expr, ExprLit, FnArg, Ident, Lit, Token}; 213 | use uuid::Uuid; 214 | use well_known_types::{WellKnownType, WellKnownTypes}; 215 | 216 | #[cfg(test)] 217 | mod tests; 218 | 219 | /// Allows you to create ETW Trace Logging Providers. See the module docs for more detailed 220 | /// instructions for this macro. 221 | #[proc_macro_attribute] 222 | pub fn trace_logging_provider( 223 | attr: proc_macro::TokenStream, 224 | input: proc_macro::TokenStream, 225 | ) -> proc_macro::TokenStream { 226 | // let logging_trait = parse_macro_input!(input as syn::ItemTrait); 227 | let output = trace_logging_events_core(attr.into(), input.into()); 228 | output.into() 229 | } 230 | 231 | fn trace_logging_events_core(attr: TokenStream, item_tokens: TokenStream) -> TokenStream { 232 | let mut errors: Vec = Vec::new(); 233 | 234 | let logging_trait: syn::ItemTrait = match syn::parse2(item_tokens) { 235 | Err(e) => { 236 | return e.to_compile_error(); 237 | } 238 | Ok(syn::Item::Trait(t)) => t, 239 | Ok(syn::Item::Mod(m)) => { 240 | return Error::new_spanned(&m, "Modules are not yet supported, but thanks for asking.") 241 | .to_compile_error(); 242 | } 243 | Ok(unrecognized) => { 244 | return Error::new_spanned( 245 | &unrecognized, 246 | "The #[trace_logging_provider] attribute cannot be used with this kind of item.", 247 | ) 248 | .to_compile_error(); 249 | } 250 | }; 251 | 252 | let provider_attrs: ProviderAttributes = match syn::parse2(attr) { 253 | Ok(p) => p, 254 | Err(e) => { 255 | errors.push(e); 256 | ProviderAttributes::default() 257 | } 258 | }; 259 | 260 | // provider_ident is the identifier used in Rust source code for the generated provider type. 261 | let provider_ident = &logging_trait.ident; 262 | let provider_ident_string = provider_ident.to_string(); 263 | 264 | let wk = WellKnownTypes::new(); 265 | 266 | let mut output = TokenStream::new(); 267 | 268 | let provider_metadata_ident = Ident::new( 269 | &format!("{}_PROVIDER_METADATA", provider_ident_string), 270 | provider_ident.span(), 271 | ); 272 | 273 | // Create the provider metadata. 274 | // provider_ident is the identifier used in Rust source code for the generated code. 275 | // When writing the provider metadata, we allow the user to specify a different name to ETW. 276 | let provider_name = provider_attrs 277 | .provider_name 278 | .as_ref() 279 | .unwrap_or(&provider_ident_string); 280 | output.extend(create_provider_metadata( 281 | provider_name, 282 | &provider_metadata_ident, 283 | )); 284 | 285 | // Definitions that go inside the "impl MyProvider { ... }" block. 286 | let mut provider_impl_items = TokenStream::new(); 287 | 288 | // To make this simple, we either require all events to be labeled 289 | // with an event id or autogenerated. 290 | let mut event_ids_auto_generated = true; 291 | let mut event_id_mappings = HashMap::new(); 292 | 293 | for (method_index, method) in logging_trait 294 | .items 295 | .iter() 296 | .filter_map(|item| { 297 | if let syn::TraitItem::Method(m) = item { 298 | Some(m) 299 | } else { 300 | None 301 | } 302 | }) 303 | .enumerate() 304 | { 305 | let event_index = method_index as u16; 306 | 307 | // Check requirements for the method signature. If the requirements are not met, we 308 | // emit an error but keep going. This allows us to report as many errors as possible in 309 | // each build, rather than having errors "unlocked" one by one. 310 | if method.sig.asyncness.is_some() { 311 | errors.push(Error::new_spanned( 312 | method, 313 | "Async event methods are not supported.", 314 | )); 315 | } 316 | if method.sig.unsafety.is_some() { 317 | errors.push(Error::new_spanned( 318 | method, 319 | "Event methods should not be marked unsafe.", 320 | )); 321 | } 322 | if !method.sig.generics.params.is_empty() { 323 | errors.push(Error::new_spanned( 324 | method, 325 | "Generic event methods are not supported.", 326 | )); 327 | } 328 | match &method.sig.output { 329 | syn::ReturnType::Default => {} 330 | _ => { 331 | errors.push(Error::new_spanned( 332 | method, 333 | "Event methods must not return data.", 334 | )); 335 | } 336 | } 337 | if let Some(block) = method.default.as_ref() { 338 | errors.push(Error::new_spanned( 339 | block, 340 | "Event methods must not contain an implementation.", 341 | )); 342 | } 343 | 344 | let event_name: String = method.sig.ident.to_string(); 345 | 346 | // Here we build the data descriptor array. The data descriptor array is constructed on 347 | // the stack, and has a statically-known size. It contains pointers to data fields. The 348 | // event metadata describes the order and type of the data pointed-to by the data 349 | // descriptors. 350 | // 351 | // For self-describing events (TraceLogging), the first two items in the data descriptor 352 | // array identify the provider metadata and the event metadata. 353 | let mut data_descriptor_array = TokenStream::new(); 354 | 355 | // See comments in traceloggingprovider.h, around line 2300, which describe the 356 | // encoding of the event mdata. 357 | let mut event_metadata: Vec = Vec::new(); 358 | event_metadata.push(parse_quote! { 0 }); // reserve space for the size (byte 0) 359 | event_metadata.push(parse_quote! { 0 }); // reserve space for the size (byte 1) 360 | event_metadata.push(parse_quote! { 0 }); // no extensions 361 | append_utf8_str_chars(&mut event_metadata, &event_name); 362 | 363 | // Some fields require running some code before building the data descriptors, so we 364 | // collect statements here. 365 | let mut statements = TokenStream::new(); 366 | 367 | // Each parameter (except for &self) becomes an event field. 368 | let mut found_receiver = false; 369 | 370 | // sig is the function signature for the function that we will generate for this event. 371 | let mut sig = method.sig.clone(); 372 | 373 | for param in sig.inputs.iter_mut() { 374 | let param_span = param.span(); 375 | match param { 376 | FnArg::Receiver(_) => { 377 | errors.push(Error::new_spanned(param, "Event methods should not provide any receiver arguments (&self, &mut self, etc.).")); 378 | found_receiver = true; 379 | } 380 | 381 | FnArg::Typed(param_typed) => { 382 | let mut event_attr: Option = None; 383 | param_typed.attrs.retain(|a| { 384 | if a.path == parse_quote!(event) { 385 | event_attr = Some(a.clone()); 386 | false 387 | } else if a.path == parse_quote!(doc) { 388 | true 389 | } else { 390 | errors.push(Error::new_spanned( 391 | a, 392 | "This attribute is not permitted on event fields.", 393 | )); 394 | true 395 | } 396 | }); 397 | let param_name: &Ident = match &*param_typed.pat { 398 | syn::Pat::Ident(ref name) => &name.ident, 399 | _ => { 400 | errors.push(Error::new( 401 | param.span(), 402 | "Only ordinary parameter patterns are supported on event methods.", 403 | )); 404 | continue; 405 | } 406 | }; 407 | 408 | if parse_event_field( 409 | &mut errors, 410 | &wk, 411 | event_attr.as_ref(), 412 | param_span, 413 | param_name, 414 | &mut param_typed.ty, 415 | &mut data_descriptor_array, 416 | &mut event_metadata, 417 | &mut statements, 418 | ) 419 | .is_err() 420 | { 421 | errors.push(Error::new_spanned( 422 | ¶m, 423 | "This type is not supported for event parameters.", 424 | )); 425 | } 426 | } 427 | } 428 | } 429 | 430 | // We require that every function declare a '&self' receiver parameter. 431 | if !found_receiver { 432 | sig.inputs.insert(0, parse_quote!(&self)); 433 | } 434 | 435 | // Insert the "options" argument. 436 | sig.inputs.insert( 437 | 1, 438 | parse_quote!(options: core::option::Option<&::win_etw_provider::EventOptions>), 439 | ); 440 | 441 | // Now that we have processed all parameters ("fields"), we can finish constructing 442 | // the per-event metadata. 443 | let event_metadata_len = event_metadata.len(); 444 | if event_metadata_len > 0xffff { 445 | errors.push(Error::new( 446 | method.span(), 447 | "Event metadata is too large to encode; reduce the complexity of this event.", 448 | )); 449 | continue; 450 | } 451 | let event_metadata_len_b0 = (event_metadata_len & 0xff) as u8; 452 | let event_metadata_len_b1 = (event_metadata_len >> 8) as u8; 453 | event_metadata[0] = parse_quote! { #event_metadata_len_b0 }; 454 | event_metadata[1] = parse_quote! { #event_metadata_len_b1 }; 455 | 456 | let event_attrs = parse_event_attributes(&mut errors, &method.sig.ident, &method.attrs); 457 | 458 | // Generate the event descriptor for this event. 459 | // This is a static variable. The name is exactly the name of the event. 460 | let event_level = event_attrs.level; 461 | let event_opcode = event_attrs.opcode; 462 | let event_task = event_attrs.task; 463 | let potential_event_id = event_attrs.event_id; 464 | let event_keyword = event_attrs 465 | .keyword 466 | .as_ref() 467 | .cloned() 468 | .unwrap_or_else(|| parse_quote!(0)); 469 | 470 | // We use the first entry to see if we have user provided IDs 471 | // or we are generating one. 472 | if event_index == 0 { 473 | event_ids_auto_generated = potential_event_id.is_none(); 474 | } 475 | 476 | // We have some events with user provided id and some without. 477 | if event_ids_auto_generated != potential_event_id.is_none() { 478 | errors.push(Error::new( 479 | method.span(), 480 | "Event ids must be set for all events, or for none.", 481 | )); 482 | } 483 | 484 | let event_id = potential_event_id.unwrap_or(event_index); 485 | 486 | // Only care about #[event(id = #)] types so we don't get 487 | // confusing messages when forget to add an id for some 488 | // event. 489 | if potential_event_id.is_some() { 490 | let identifier = method.sig.ident.to_string(); 491 | if let Some(previous) = event_id_mappings.get(&event_id) { 492 | errors.push(Error::new( 493 | method.span(), 494 | format!( 495 | "Event id {} has already been defined on {}.", 496 | event_id, previous 497 | ), 498 | )); 499 | } 500 | event_id_mappings.insert(event_id, identifier); 501 | } 502 | 503 | // an expression which generates EventDescriptor 504 | let event_descriptor = quote! { 505 | ::win_etw_provider::EventDescriptor { 506 | id: #event_id, 507 | version: 0, 508 | channel: 11, 509 | level: #event_level, 510 | opcode: #event_opcode, 511 | task: #event_task, 512 | keyword: #event_keyword, 513 | }; 514 | }; 515 | 516 | let event_attrs_method_attrs = &event_attrs.method_attrs; 517 | 518 | // Generate the `${name}_is_enabled` function for this event. 519 | // We do not use ident_suffix() because this is not a private identifier. 520 | let event_is_enabled_name = Ident::new( 521 | &format!("{}_is_enabled", method.sig.ident), 522 | method.sig.ident.span(), 523 | ); 524 | 525 | // Build the method that implements this event. 526 | provider_impl_items.extend(quote!{ 527 | #( #event_attrs_method_attrs )* 528 | #[cfg_attr(not(target_os = "windows"), allow(unused_variables))] 529 | pub #sig 530 | { 531 | #[cfg(target_os = "windows")] 532 | { 533 | use ::win_etw_provider::EventDataDescriptor; 534 | 535 | // This places the EVENT_METADATA into a read-only linker section, properly 536 | // ordered with respect to TRACE_LOGGING_METADATA and other related sections. 537 | #[link_section = ".rdata$etw1"] 538 | #[used] 539 | static EVENT_METADATA: [u8; #event_metadata_len] = [ #( #event_metadata, )* ]; 540 | 541 | let mut event_descriptor: ::win_etw_provider::EventDescriptor = #event_descriptor; 542 | 543 | if let Some(opts) = options { 544 | if let Some(level) = opts.level { 545 | event_descriptor.level = level; 546 | } 547 | } 548 | 549 | #statements 550 | 551 | let data_descriptors = [ 552 | EventDataDescriptor::for_provider_metadata(&#provider_metadata_ident[..]), 553 | EventDataDescriptor::for_event_metadata(&EVENT_METADATA[..]), 554 | #data_descriptor_array 555 | ]; 556 | ::win_etw_provider::Provider::write(&self.provider, 557 | options, 558 | &event_descriptor, 559 | &data_descriptors, 560 | ); 561 | } 562 | } 563 | 564 | pub fn #event_is_enabled_name(&self, level: ::core::option::Option<::win_etw_provider::Level>) -> bool { 565 | #[cfg(target_os = "windows")] 566 | { 567 | let mut event_descriptor: ::win_etw_provider::EventDescriptor = #event_descriptor; 568 | if let Some(level) = level { 569 | event_descriptor.level = level; 570 | } 571 | 572 | ::win_etw_provider::Provider::is_event_enabled( 573 | &self.provider, 574 | &event_descriptor) 575 | } 576 | #[cfg(not(target_os = "windows"))] 577 | { 578 | false 579 | } 580 | } 581 | }); 582 | } 583 | 584 | // We propagate the visibility of the trait definition to the structure definition. 585 | let vis = logging_trait.vis.clone(); 586 | let provider_guid = match provider_attrs.uuid { 587 | Some(uuid) => uuid, 588 | None => etw_event_source_guid(provider_name), 589 | }; 590 | let provider_guid_const = uuid_to_expr(&provider_guid); 591 | 592 | // If the input item has doc attributes, then carry them over to the output type. 593 | let doc_path: syn::Path = parse_quote!(doc); 594 | let provider_doc_attrs = logging_trait 595 | .attrs 596 | .iter() 597 | .filter(|a| a.path == doc_path) 598 | .collect::>(); 599 | 600 | // Build a code fragment that registers the provider traits. 601 | let register_traits: TokenStream = create_register_provider_traits( 602 | &provider_name, 603 | provider_attrs.provider_group_guid.as_ref(), 604 | ); 605 | 606 | output.extend(quote! { 607 | #( #provider_doc_attrs )* 608 | #vis struct #provider_ident { 609 | provider: ::core::option::Option<::win_etw_provider::EtwProvider>, 610 | } 611 | 612 | impl #provider_ident { 613 | /// Creates (registers) a new instance of this provider. If registration fails, 614 | /// returns a "null" provider. This prevents problems with event logging from 615 | /// disrupting the normal operation of applications. 616 | /// 617 | /// On non-Windows platforms, this function always returns a null provider. 618 | /// 619 | /// Creating an event source is a costly operation, because it requires contacting the 620 | /// event manager, allocating event buffers, potentially receiving callbacks from 621 | /// event consumers, etc. Applications should only create event sources during process 622 | /// initialization, and should always reuse them, never re-creating them. 623 | pub fn new() -> Self { 624 | let provider = match ::win_etw_provider::EtwProvider::new(&Self::PROVIDER_GUID) { 625 | Ok(mut provider) => { 626 | #[cfg(target_os = "windows")] 627 | { 628 | #register_traits 629 | } 630 | 631 | Some(provider) 632 | } 633 | Err(_) => None, 634 | }; 635 | Self { provider } 636 | } 637 | 638 | /// Creates (registers) a new instance of this provider. If registration fails, then 639 | /// this method returns a "null" provider. 640 | /// 641 | /// On non-Windows platforms, this function always returns `Ok`, containing a null 642 | /// provider. 643 | /// 644 | /// Creating an event source is a costly operation, because it requires contacting the 645 | /// event manager, allocating event buffers, potentially receiving callbacks from 646 | /// event consumers, etc. Applications should only create event sources during process 647 | /// initialization, and should always reuse them, never re-creating them. 648 | pub fn new_err() -> ::core::result::Result { 649 | Ok(Self { 650 | provider: Some(::win_etw_provider::EtwProvider::new(&Self::PROVIDER_GUID)?), 651 | }) 652 | } 653 | 654 | /// Creates a new "null" instance of the provider. All events written to this provider 655 | /// are discarded. 656 | pub fn null() -> Self { 657 | Self { provider: None } 658 | } 659 | 660 | #[allow(unused_variable)] 661 | pub const PROVIDER_GUID: ::win_etw_provider::GUID = #provider_guid_const; 662 | pub const PROVIDER_NAME: &'static str = #provider_name; 663 | } 664 | 665 | // We intentionally generate identifiers that are not snake-case. 666 | #[allow(non_snake_case)] 667 | impl #provider_ident { 668 | #provider_impl_items 669 | } 670 | }); 671 | 672 | output.extend(errors.into_iter().map(|e| e.to_compile_error())); 673 | output 674 | } 675 | 676 | /// Creates a fragment of code (statements) which will register the 677 | /// provider traits for this provider. 678 | // 679 | // See https://docs.microsoft.com/en-us/windows/win32/etw/provider-traits 680 | fn create_register_provider_traits( 681 | provider_name: &str, 682 | provider_group_guid: Option<&Uuid>, 683 | ) -> TokenStream { 684 | let mut traits_bytes: Vec = Vec::new(); 685 | traits_bytes.push(0); // reserve space for TraitsSize (u16) 686 | traits_bytes.push(0); 687 | 688 | traits_bytes.extend_from_slice(provider_name.as_bytes()); 689 | traits_bytes.push(0); 690 | 691 | if let Some(provider_group_guid) = provider_group_guid { 692 | // Add trait for provider guid 693 | let provider_guid_trait_offset = traits_bytes.len(); 694 | traits_bytes.push(0); // reserve space for TraitSize (u16) 695 | traits_bytes.push(0); 696 | traits_bytes.push(ETW_PROVIDER_TRAIT_TYPE_GROUP); 697 | traits_bytes.extend_from_slice(&provider_group_guid.to_bytes_le()); 698 | let provider_guid_trait_len = traits_bytes.len() - provider_guid_trait_offset; 699 | // Set TraitSize (u16) 700 | traits_bytes[provider_guid_trait_offset] = provider_guid_trait_len as u8; 701 | traits_bytes[provider_guid_trait_offset + 1] = (provider_guid_trait_len >> 8) as u8; 702 | } 703 | 704 | // Set TraitsSize (u16) 705 | traits_bytes[0] = traits_bytes.len() as u8; 706 | traits_bytes[1] = (traits_bytes.len() >> 8) as u8; 707 | 708 | let traits_bytes_len = traits_bytes.len(); 709 | 710 | quote! { 711 | static PROVIDER_TRAITS: [u8; #traits_bytes_len] = [ #(#traits_bytes),* ]; 712 | // We ignore the Result from calling set_provider_traits. 713 | // There is no good way to report it. 714 | let _ = provider.set_provider_traits(&PROVIDER_TRAITS); 715 | } 716 | } 717 | 718 | fn err_spanned(item: &T, msg: &str) -> TokenStream { 719 | Error::new_spanned(item, msg).to_compile_error() 720 | } 721 | 722 | // `provider_name` is not necessarily the same as the identifier used in source code. 723 | // It can be overridden using `#[trace_logging_provider(name = "some_name_here")]`. 724 | fn create_provider_metadata(provider_name: &str, provider_metadata_ident: &Ident) -> TokenStream { 725 | let mut provider_metadata: Vec = Vec::new(); 726 | 727 | let provider_metadata_len = 2 + provider_name.len() + 1; 728 | if provider_metadata_len > 0xffff { 729 | return err_spanned(&provider_name, "The provider name is too long."); 730 | } 731 | provider_metadata.push((provider_metadata_len & 0xff) as u8); 732 | provider_metadata.push((provider_metadata_len >> 8) as u8); 733 | provider_metadata.extend_from_slice(provider_name.as_bytes()); 734 | provider_metadata.push(0); 735 | 736 | quote! { 737 | #[link_section = ".rdata$etw2"] 738 | #[used] 739 | #[allow(non_upper_case_globals)] 740 | #[cfg(target_os = "windows")] 741 | static #provider_metadata_ident: [u8; #provider_metadata_len] = [ 742 | #( 743 | #provider_metadata, 744 | )* 745 | ]; 746 | } 747 | } 748 | 749 | fn append_utf8_str_chars(output: &mut Vec, s: &str) { 750 | for &b in s.as_bytes().iter() { 751 | output.push(parse_quote! { #b }); 752 | } 753 | // Add the NUL byte at the end. 754 | output.push(parse_quote! { 0 }); 755 | } 756 | 757 | struct UnsupportedField; 758 | 759 | /// Parses one event field. Event fields are declared as function parameters. 760 | /// 761 | /// * `event_metadata`: This builds the static [u8; N] array that contains the metadata for this 762 | /// event. It can contain literals, symbolic expressions, etc. 763 | fn parse_event_field( 764 | errors: &mut Vec, 765 | well_known_types: &WellKnownTypes, 766 | event_attr: Option<&syn::Attribute>, 767 | field_span: proc_macro2::Span, 768 | field_name: &Ident, 769 | field_ty: &mut syn::Type, 770 | data_descriptor_array: &mut TokenStream, 771 | event_metadata: &mut Vec, 772 | statements: &mut TokenStream, 773 | ) -> Result<(), UnsupportedField> { 774 | // Write the field metadata. 775 | // // FieldMetadata: 776 | // struct FieldMetadata // Variable-length pseudo-structure, byte-aligned, tightly-packed. 777 | // { 778 | // char Name[]; // UTF-8 nul-terminated field name 779 | // UINT8 InType; // Values from the TlgIn enumeration. 780 | // UINT8 OutType; // TlgOut enumeration. Only present if (InType & 128) == 128. 781 | // UINT8 Extension[]; // Only present if OutType is present and (OutType & 128) == 128. Read until you hit a byte with high bit unset. 782 | // UINT16 ValueCount; // Only present if (InType & CountMask) == Ccount. 783 | // UINT16 TypeInfoSize; // Only present if (InType & CountMask) == Custom. 784 | // char TypeInfo[TypeInfoSize]; // Only present if (InType & CountMask) == Custom. 785 | // }; 786 | 787 | let param_name_string = field_name.to_string(); 788 | append_utf8_str_chars(event_metadata, ¶m_name_string); 789 | // We will append more data to event_metadata, below. 790 | 791 | // The user can annotate fields with #[event(...)] in order to specify output formats. 792 | let mut output_hex = false; 793 | if let Some(event_attr) = event_attr { 794 | match event_attr.parse_meta() { 795 | Ok(syn::Meta::List(list)) => { 796 | for item in list.nested.iter() { 797 | match item { 798 | syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { 799 | path, 800 | lit, 801 | .. 802 | })) => { 803 | if path.is_ident("output") { 804 | match &lit { 805 | Lit::Str(lit) => { 806 | let output_string = lit.value(); 807 | match output_string.as_str() { 808 | "hex" => { 809 | output_hex = true; 810 | } 811 | _ => { 812 | errors.push(Error::new_spanned( 813 | path, 814 | "Output format is not recognized.", 815 | )); 816 | } 817 | } 818 | } 819 | _ => errors.push(Error::new_spanned( 820 | path, 821 | "This metadata is expected to be a string.", 822 | )), 823 | } 824 | } else { 825 | errors.push(Error::new_spanned( 826 | path, 827 | "This metadata key is not recognized.", 828 | )); 829 | } 830 | } 831 | _ => errors.push(Error::new_spanned( 832 | item, 833 | "This metadata item is not recognized.", 834 | )), 835 | } 836 | } 837 | } 838 | Ok(_) => errors.push(Error::new_spanned( 839 | event_attr, 840 | "This metadata is not recognized.", 841 | )), 842 | Err(e) => { 843 | errors.push(e); 844 | } 845 | } 846 | } 847 | 848 | let mut field_metadata_intype: Expr; 849 | let mut field_metadata_out_type: Option = None; 850 | 851 | if let Some(t) = well_known_types.find(&*field_ty) { 852 | field_metadata_intype = if let Some(in_type_expr) = t.opts.in_type_expr.as_ref() { 853 | in_type_expr.clone() 854 | } else { 855 | let in_type: u8 = t.in_type.bits(); 856 | parse_quote!(#in_type) 857 | }; 858 | 859 | if let Some(out_type) = t.opts.out_type { 860 | let out_type: u8 = out_type.bits(); 861 | field_metadata_out_type = Some(parse_quote!(#out_type)); 862 | } else { 863 | field_metadata_out_type = None; 864 | } 865 | 866 | if let Some(r) = t.opts.replacement_type.as_ref() { 867 | *field_ty = r.clone(); 868 | } 869 | match t.code { 870 | WellKnownType::ref_str => { 871 | // We encode &str as COUNTEDANSISTRING (so that we do not need 872 | // a NUL-terminated string) and marking its output type as UTF-8. 873 | // This uses two EVENT_DATA_DESCRIPTOR slots. 874 | let field_len = ident_suffix(field_name, "len"); 875 | statements.extend(quote_spanned! { 876 | field_span => 877 | let #field_len: u16 = #field_name.len() as u16; 878 | }); 879 | data_descriptor_array.extend(quote! { 880 | EventDataDescriptor::from(&#field_len), 881 | EventDataDescriptor::from(#field_name), 882 | }); 883 | } 884 | WellKnownType::u16str => { 885 | // UCS-2 string without NUL terminator. 886 | let field_len = ident_suffix(field_name, "len"); 887 | statements.extend(quote! { 888 | let #field_len: usize = #field_name.len(); // length in code units 889 | // Since we're recording this as COUNTEDUNICODESTRING, we 890 | // want the length in bytes of the string, excluding the NUL. 891 | // Which is easy, because there is no NUL. 892 | let #field_len: u16 = (#field_len * 2).min(0xffff) as u16; 893 | }); 894 | data_descriptor_array.extend(quote! { 895 | EventDataDescriptor::from(&#field_len), 896 | EventDataDescriptor::from(#field_name), 897 | }); 898 | } 899 | WellKnownType::u16cstr => { 900 | // UCS-2 string without NUL terminator. 901 | let field_len = ident_suffix(field_name, "len"); 902 | statements.extend(quote! { 903 | let #field_len: usize = #field_name.len(); // length in code units 904 | // Since we're recording this as COUNTEDUNICODESTRING, we 905 | // want the length in bytes of the string, excluding the NUL. 906 | let #field_len: u16 = (#field_len * 2).min(0xffff) as u16; 907 | }); 908 | data_descriptor_array.extend(quote! { 909 | EventDataDescriptor::from(&#field_len), 910 | EventDataDescriptor::from(#field_name), 911 | }); 912 | } 913 | WellKnownType::osstr => { 914 | let field_len = ident_suffix(field_name, "len"); 915 | let field_desc = ident_suffix(field_name, "desc"); 916 | let field_u16cstring = ident_suffix(field_name, "u16cstring"); 917 | let field_u16cstr = ident_suffix(field_name, "u16cstr"); 918 | statements.extend(quote! { 919 | let #field_desc: EventDataDescriptor; 920 | let #field_len: u16; 921 | let #field_u16cstring: ::win_etw_provider::types::U16CString; 922 | let #field_u16cstr: &::win_etw_provider::types::U16CStr; 923 | match ::win_etw_provider::types::U16CString::from_os_str(#field_name) { 924 | Ok(s) => { 925 | #field_u16cstring = s; 926 | #field_u16cstr = #field_u16cstring.as_ref(); 927 | #field_desc = EventDataDescriptor::from(#field_u16cstr); 928 | #field_len = (#field_u16cstr.len() as u16 * 2); // compute length in bytes 929 | } 930 | Err(_) => { 931 | #field_desc = EventDataDescriptor::empty(); 932 | #field_len = 0; 933 | } 934 | } 935 | }); 936 | data_descriptor_array.extend(quote! { 937 | EventDataDescriptor::from(&#field_len), 938 | #field_desc, 939 | }); 940 | } 941 | WellKnownType::SocketAddrV4 => { 942 | // We cannot simply pass a copy of std::net::SocketAddrV4 to ETW because it does 943 | // not have a guaranteed memory layout. So we convert it to 944 | // win_etw_provider::SocketAddrV4, which does. 945 | let field_len = ident_suffix(field_name, "len"); 946 | statements.extend(quote_spanned! { 947 | field_span => 948 | let #field_name = ::win_etw_provider::SocketAddrV4::from(#field_name); 949 | let #field_len: u16 = (::core::mem::size_of::<::win_etw_provider::SocketAddrV4>()) as u16; 950 | }); 951 | data_descriptor_array.extend(quote! { 952 | EventDataDescriptor::from(&#field_len), 953 | EventDataDescriptor::from(&#field_name), 954 | }); 955 | } 956 | WellKnownType::SocketAddrV6 => { 957 | // We cannot simply pass a copy of std::net::SocketAddrV6 to ETW because it does 958 | // not have a guaranteed memory layout. So we convert it to 959 | // win_etw_provider::SocketAddrV6, which does. 960 | let field_len = ident_suffix(field_name, "len"); 961 | statements.extend(quote_spanned! { 962 | field_span => 963 | let #field_name = ::win_etw_provider::SocketAddrV6::from(#field_name); 964 | let #field_len: u16 = (::core::mem::size_of::<::win_etw_provider::SocketAddrV6>()) as u16; 965 | }); 966 | data_descriptor_array.extend(quote_spanned! { 967 | field_span => 968 | EventDataDescriptor::from(&#field_len), 969 | EventDataDescriptor::from(&#field_name), 970 | }); 971 | } 972 | WellKnownType::SocketAddr => { 973 | let field_desc = ident_suffix(field_name, "desc"); 974 | let field_v4 = ident_suffix(field_name, "v4"); 975 | let field_v6 = ident_suffix(field_name, "v6"); 976 | let field_len_ident = ident_suffix(field_name, "len"); 977 | statements.extend(quote_spanned! { 978 | field_span => 979 | let #field_v4; 980 | let #field_v6; 981 | let #field_len_ident; 982 | let #field_desc; 983 | match #field_name { 984 | ::std::net::SocketAddr::V4(ref address_v4) => { 985 | #field_v4 = ::win_etw_provider::SocketAddrV4::from(address_v4); 986 | #field_len_ident = ::core::mem::size_of::<::win_etw_provider::SocketAddrV4>() as u16; 987 | #field_desc = EventDataDescriptor::from(&#field_v4); 988 | } 989 | ::std::net::SocketAddr::V6(ref address_v6) => { 990 | #field_v6 = ::win_etw_provider::SocketAddrV6::from(address_v6); 991 | #field_len_ident = ::core::mem::size_of::<::win_etw_provider::SocketAddrV6>() as u16; 992 | #field_desc = EventDataDescriptor::from(&#field_v6); 993 | } 994 | } 995 | }); 996 | data_descriptor_array.extend(quote_spanned! { 997 | field_span => 998 | EventDataDescriptor::from(&#field_len_ident), 999 | #field_desc, 1000 | }); 1001 | } 1002 | WellKnownType::SystemTime => { 1003 | // If the SystemTime value cannot be converted to a FILETIME, then the data 1004 | // descriptor for this field will be empty, rather than pointing to an invalid 1005 | // or incorrect time value. 1006 | statements.extend(quote_spanned!{ 1007 | field_span => 1008 | let #field_name = <::win_etw_provider::FILETIME as ::core::convert::TryFrom<::std::time::SystemTime>> 1009 | ::try_from(#field_name); 1010 | }); 1011 | data_descriptor_array.extend(quote_spanned! { 1012 | field_span => 1013 | match &#field_name { 1014 | Ok(ref t) => EventDataDescriptor::from(&t.0), 1015 | Err(_) => EventDataDescriptor::empty(), 1016 | } 1017 | }); 1018 | } 1019 | WellKnownType::FILETIME => { 1020 | data_descriptor_array.extend(quote_spanned! { 1021 | field_span => 1022 | EventDataDescriptor::from(&#field_name.0), 1023 | }); 1024 | } 1025 | WellKnownType::bool => { 1026 | statements.extend(quote_spanned! { 1027 | field_span => 1028 | let #field_name: i8 = #field_name as i8; 1029 | }); 1030 | data_descriptor_array.extend(quote! { 1031 | EventDataDescriptor::from(&#field_name), 1032 | }); 1033 | } 1034 | _ => { 1035 | if t.is_ref { 1036 | data_descriptor_array.extend(quote_spanned! { 1037 | field_span => 1038 | EventDataDescriptor::from(#field_name), 1039 | }); 1040 | } else { 1041 | data_descriptor_array.extend(quote_spanned! { 1042 | field_span => 1043 | EventDataDescriptor::from(&#field_name), 1044 | }); 1045 | } 1046 | } 1047 | } 1048 | } else { 1049 | match &*field_ty { 1050 | syn::Type::Reference(ref_ty) => { 1051 | match &*ref_ty.elem { 1052 | syn::Type::Slice(slice_ty) => { 1053 | if let Some(t) = well_known_types.find(&slice_ty.elem) { 1054 | if !t.primitive { 1055 | return Err(UnsupportedField); 1056 | } 1057 | // Slices are encoded using two data descriptors. 1058 | // The first is for the length field, the second for the data. 1059 | let field_len_ident = ident_suffix(field_name, "len"); 1060 | statements.extend(quote_spanned! { 1061 | field_span => 1062 | let #field_name = &#field_name[..#field_name.len().min(0xffff)]; 1063 | let #field_len_ident: u16 = #field_name.len() as u16; 1064 | }); 1065 | data_descriptor_array.extend(quote! { 1066 | EventDataDescriptor::from(&#field_len_ident), 1067 | EventDataDescriptor::from(#field_name), 1068 | }); 1069 | // 0x40 is VCOUNT flag 1070 | let in_type_u8 = t.in_type.bits(); 1071 | field_metadata_intype = parse_quote!(#in_type_u8); 1072 | field_metadata_intype = parse_quote!(#field_metadata_intype | ::win_etw_provider::metadata::InFlag::VCOUNT_FLAG.bits()); 1073 | } else { 1074 | return Err(UnsupportedField); 1075 | } 1076 | } 1077 | _ => { 1078 | return Err(UnsupportedField); 1079 | } 1080 | } 1081 | } 1082 | _ => { 1083 | return Err(UnsupportedField); 1084 | } 1085 | } 1086 | } 1087 | 1088 | if output_hex { 1089 | let hex: Expr = parse_quote!(::win_etw_provider::metadata::OutFlag::HEX.bits()); 1090 | field_metadata_out_type = Some(if let Some(out_type) = field_metadata_out_type { 1091 | parse_quote!(#out_type | #hex) 1092 | } else { 1093 | hex 1094 | }); 1095 | } 1096 | 1097 | if let Some(out_type) = field_metadata_out_type { 1098 | field_metadata_intype = parse_quote!(#field_metadata_intype | ::win_etw_provider::metadata::InFlag::CHAIN_FLAG.bits()); 1099 | event_metadata.push(field_metadata_intype); 1100 | event_metadata.push(out_type); 1101 | } else { 1102 | event_metadata.push(field_metadata_intype); 1103 | } 1104 | Ok(()) 1105 | } 1106 | 1107 | /// Represents the "attribute" parameter of the `#[trace_logging_provider]` proc macro. 1108 | #[derive(Default, Debug)] 1109 | struct ProviderAttributes { 1110 | uuid: Option, 1111 | provider_name: Option, 1112 | provider_group_guid: Option, 1113 | } 1114 | 1115 | impl syn::parse::Parse for ProviderAttributes { 1116 | fn parse(stream: syn::parse::ParseStream) -> syn::Result { 1117 | let mut errors = CombinedErrors::default(); 1118 | 1119 | let mut uuid_opt = None; 1120 | let items: syn::punctuated::Punctuated = 1121 | stream.parse_terminated(syn::Meta::parse)?; 1122 | 1123 | let mut provider_group_guid: Option = None; 1124 | 1125 | let parse_guid_value = |lit: &Lit, scope: &mut ErrorScope| -> Uuid { 1126 | if let syn::Lit::Str(s) = lit { 1127 | let guid_str = s.value(); 1128 | if let Ok(value) = guid_str.parse::() { 1129 | if value == Uuid::nil() { 1130 | scope.msg("The GUID cannot be the NIL (all-zeroes) GUID."); 1131 | } 1132 | value 1133 | } else { 1134 | scope.msg("The attribute value is required to be a valid GUID."); 1135 | Uuid::nil() 1136 | } 1137 | } else { 1138 | scope.msg("The attribute value is required to be a GUID in string form."); 1139 | Uuid::nil() 1140 | } 1141 | }; 1142 | 1143 | let mut provider_name = None; 1144 | for item in items.iter() { 1145 | errors.scope(item.span(), |scope| { 1146 | match item { 1147 | syn::Meta::NameValue(syn::MetaNameValue { path, lit, .. }) => { 1148 | if path.is_ident("guid") { 1149 | let uuid = parse_guid_value(lit, scope); 1150 | if uuid_opt.is_some() { 1151 | scope.msg( 1152 | "The 'guid' attribute key cannot be specified more than once.", 1153 | ); 1154 | } else { 1155 | uuid_opt = Some(uuid); 1156 | } 1157 | } else if path.is_ident("name") { 1158 | if let syn::Lit::Str(s) = lit { 1159 | if provider_name.is_none() { 1160 | provider_name = Some(s.value()); 1161 | } else { 1162 | scope.msg("The 'name' attribute can only be specified once."); 1163 | } 1164 | } else { 1165 | scope.msg("The 'name' attribute key requires a string value."); 1166 | } 1167 | } else if path.is_ident("provider_group_guid") { 1168 | let uuid = parse_guid_value(lit, scope); 1169 | if provider_group_guid.is_some() { 1170 | scope.msg("The 'provider_group_guid' attribute key cannot be specified more than once."); 1171 | } else { 1172 | provider_group_guid = Some(uuid); 1173 | } 1174 | } else { 1175 | scope.msg("Unrecognized attribute key."); 1176 | } 1177 | } 1178 | syn::Meta::Path(path) if path.is_ident("static_mode") => { 1179 | // eprintln!("Found 'static'"); 1180 | } 1181 | _ => { 1182 | // eprintln!("item: {:#?}", item); 1183 | scope.msg("Unrecognized attribute item."); 1184 | } 1185 | } 1186 | Ok(()) 1187 | }); 1188 | } 1189 | 1190 | errors.into_result(ProviderAttributes { 1191 | uuid: uuid_opt, 1192 | provider_name, 1193 | provider_group_guid, 1194 | }) 1195 | } 1196 | } 1197 | 1198 | const ETW_EVENT_SOURCE_NAMESPACE: Uuid = uuid::uuid!("482c2db2-c390-47c8-87f8-1a15bfc130fb"); 1199 | 1200 | /// Generates a Uuid from a provider name using the same algorithm as .NET's EventSource class. 1201 | /// Many tools convert a provider name to a GUID using this algorithm. 1202 | fn etw_event_source_guid(provider_name: &str) -> Uuid { 1203 | use sha1_smol::Sha1; 1204 | 1205 | let provider_bytes: Vec = provider_name 1206 | .to_uppercase() 1207 | .encode_utf16() 1208 | .flat_map(|x| x.to_be_bytes()) 1209 | .collect(); 1210 | 1211 | let mut hasher = Sha1::new(); 1212 | hasher.update(ETW_EVENT_SOURCE_NAMESPACE.as_bytes()); 1213 | hasher.update(&provider_bytes); 1214 | 1215 | let mut bytes = [0; 16]; 1216 | bytes.copy_from_slice(&hasher.digest().bytes()[..16]); 1217 | 1218 | // .NET EventSource reads the bytes from the SHA-1 hash for the first 3 fields 1219 | // as little-endian, but does not set the "variant (aka type)" to the "Microsoft" value which 1220 | // indicates little-endian in a RFC4122-conforming UUID. The "variant" field 1221 | // ends up being the "random" value copied from the from the SHA-1 hash, instead. 1222 | uuid::Builder::from_bytes_le(bytes) 1223 | .with_version(uuid::Version::Sha1) 1224 | .into_uuid() 1225 | } 1226 | 1227 | fn uuid_to_expr(uuid: &Uuid) -> syn::Expr { 1228 | let bytes: &[u8; 16] = uuid.as_bytes(); 1229 | let data1: u32 = ((bytes[0] as u32) << 24) 1230 | | ((bytes[1] as u32) << 16) 1231 | | ((bytes[2] as u32) << 8) 1232 | | (bytes[3] as u32); 1233 | let data2: u16 = ((bytes[4] as u16) << 8) | (bytes[5] as u16); 1234 | let data3: u16 = ((bytes[6] as u16) << 8) | (bytes[7] as u16); 1235 | let data4_0 = bytes[8]; 1236 | let data4_1 = bytes[9]; 1237 | let data4_2 = bytes[10]; 1238 | let data4_3 = bytes[11]; 1239 | let data4_4 = bytes[12]; 1240 | let data4_5 = bytes[13]; 1241 | let data4_6 = bytes[14]; 1242 | let data4_7 = bytes[15]; 1243 | parse_quote! { 1244 | ::win_etw_provider::GUID { 1245 | data1: #data1, 1246 | data2: #data2, 1247 | data3: #data3, 1248 | data4: [ 1249 | #data4_0, 1250 | #data4_1, 1251 | #data4_2, 1252 | #data4_3, 1253 | #data4_4, 1254 | #data4_5, 1255 | #data4_6, 1256 | #data4_7, 1257 | ] 1258 | } 1259 | } 1260 | } 1261 | 1262 | struct EventAttributes { 1263 | level: syn::Expr, 1264 | opcode: syn::Expr, 1265 | task: syn::Expr, 1266 | keyword: Option, 1267 | event_id: Option, 1268 | method_attrs: Vec, 1269 | } 1270 | 1271 | fn parse_event_attributes( 1272 | errors: &mut Vec, 1273 | method_ident: &Ident, 1274 | input_method_attrs: &[syn::Attribute], 1275 | ) -> EventAttributes { 1276 | let mut level: Expr = parse_quote!(::win_etw_provider::Level::VERBOSE); 1277 | let mut opcode: Expr = parse_quote!(0); 1278 | let mut task: Expr = parse_quote!(0); 1279 | let mut keyword: Option = None; 1280 | 1281 | // I am not aware of how to convert from Expr to actual value 1282 | // so going to handle this here. 1283 | let mut event_id: Option = None; 1284 | 1285 | let mut method_attrs: Vec = Vec::new(); 1286 | 1287 | let mut event_already_has_doc = false; 1288 | let enable_default_event_doc = false; 1289 | 1290 | for attr in input_method_attrs.iter() { 1291 | if attr.path == parse_quote!(doc) { 1292 | method_attrs.push(attr.clone()); 1293 | event_already_has_doc = true; 1294 | } else if attr.path.is_ident("event") { 1295 | // The #[event] attribute lets the application specify the level, opcode, task, 1296 | // keyword, etc. 1297 | match attr.parse_meta() { 1298 | Ok(syn::Meta::List(list)) => { 1299 | for item in list.nested.iter() { 1300 | match item { 1301 | syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { 1302 | path, 1303 | lit, 1304 | .. 1305 | })) => { 1306 | if path.is_ident("level") { 1307 | match lit { 1308 | Lit::Str(lit_str) => { 1309 | let level_ident = match lit_str.value().as_str() { 1310 | "critical" => quote!(CRITICAL), 1311 | "error" => quote!(ERROR), 1312 | "warn" => quote!(WARN), 1313 | "info" => quote!(INFO), 1314 | "verbose" => quote!(VERBOSE), 1315 | _ => { 1316 | errors.push(Error::new_spanned(item, "The value specified for 'level' is not a valid string.")); 1317 | quote!(VERBOSE) 1318 | } 1319 | }; 1320 | level = parse_quote!(::win_etw_provider::Level::#level_ident); 1321 | } 1322 | Lit::Int(_) => { 1323 | level = parse_quote!(::win_etw_provider::Level(#lit)); 1324 | } 1325 | _ => { 1326 | errors.push(Error::new_spanned(item, "The value specified for 'level' is not recognized.")); 1327 | } 1328 | } 1329 | } else if path.is_ident("opcode") { 1330 | opcode = Expr::Lit(ExprLit { 1331 | lit: lit.clone(), 1332 | attrs: Vec::new(), 1333 | }); 1334 | } else if path.is_ident("task") { 1335 | task = Expr::Lit(ExprLit { 1336 | lit: lit.clone(), 1337 | attrs: Vec::new(), 1338 | }); 1339 | } else if path.is_ident("keyword") { 1340 | if keyword.is_some() { 1341 | errors.push(Error::new(attr.span(), "The 'keyword' attribute cannot be specified more than once.")); 1342 | } else { 1343 | keyword = Some(Expr::Lit(ExprLit { 1344 | lit: lit.clone(), 1345 | attrs: Vec::new(), 1346 | })); 1347 | } 1348 | } else if path.is_ident("id") { 1349 | if event_id.is_some() { 1350 | errors.push(Error::new( 1351 | lit.span(), 1352 | "Event id has already been defined.".to_string(), 1353 | )); 1354 | } else if let Lit::Int(lit_int) = lit { 1355 | if let Ok(int_value) = lit_int.base10_parse() { 1356 | event_id = Some(int_value); 1357 | } else { 1358 | errors.push(Error::new( 1359 | lit.span(), 1360 | "Event id must be a u16.".to_string(), 1361 | )); 1362 | } 1363 | } else { 1364 | errors.push(Error::new( 1365 | lit.span(), 1366 | "Event id must be a u16.".to_string(), 1367 | )); 1368 | } 1369 | } else { 1370 | errors 1371 | .push(Error::new_spanned(item, "Unrecognized attribute.")); 1372 | } 1373 | } 1374 | _ => { 1375 | errors.push(Error::new_spanned(item, "Unrecognized attribute.")); 1376 | } 1377 | } 1378 | } 1379 | } 1380 | Ok(_) => { 1381 | errors.push(Error::new_spanned( 1382 | attr, 1383 | "The form of the #[event] attribute is invalid. \ 1384 | It should be: #[event(name = \"value\", name2 = \"value2\", ...)].", 1385 | )); 1386 | } 1387 | Err(e) => { 1388 | errors.push(Error::new( 1389 | attr.span(), 1390 | format!("Unrecognized attribute: {}", e), 1391 | )); 1392 | } 1393 | } 1394 | } else { 1395 | errors.push(Error::new_spanned( 1396 | attr, 1397 | "The only attributes allowed on event methods are #[doc] and #[event(...)] attributes.", 1398 | )); 1399 | } 1400 | } 1401 | 1402 | if !event_already_has_doc && enable_default_event_doc { 1403 | let method_doc = format!("Writes the `{}` event to the ETW log stream.", method_ident); 1404 | method_attrs.push(parse_quote!( #![doc = #method_doc] )); 1405 | } 1406 | 1407 | EventAttributes { 1408 | method_attrs, 1409 | level, 1410 | opcode, 1411 | task, 1412 | event_id, 1413 | keyword, 1414 | } 1415 | } 1416 | 1417 | /// The separator we use to build dynamic identifiers, based on existing identifiers. 1418 | /// Ideally, we would use a string that will not cause collisions with user-provided 1419 | /// identifiers. Rust supports non-ASCII identifiers, which would allow us to 1420 | /// use something like `U+0394 GREEK CAPITAL LETTER DELTA`, but this is an unstable 1421 | /// feature. 1422 | /// 1423 | /// Instead, we use "__". Idiomatic identifiers should not contain "__" because this 1424 | /// does not meet Rust's definition of a snake-case name. So we add use this, and add 1425 | /// `#[allow(non_snake_case)]` to our generated code. 1426 | const IDENT_SEPARATOR: &str = "__"; 1427 | 1428 | /// Builds a new identifier, based on an existing identifier. 1429 | fn ident_suffix(ident: &Ident, suffix: &str) -> Ident { 1430 | Ident::new( 1431 | &format!("{}{}{}", ident, IDENT_SEPARATOR, suffix), 1432 | ident.span(), 1433 | ) 1434 | } 1435 | 1436 | const ETW_PROVIDER_TRAIT_TYPE_GROUP: u8 = 1; 1437 | -------------------------------------------------------------------------------- /win_etw_macros/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | struct CompileErrors { 4 | errors: Vec, 5 | } 6 | 7 | use proc_macro2::TokenTree; 8 | use syn::buffer::Cursor; 9 | 10 | impl syn::parse::Parse for CompileErrors { 11 | fn parse(s: syn::parse::ParseStream) -> syn::Result { 12 | s.step(|c| { 13 | let mut c: Cursor = *c; 14 | 15 | let mut errors = Vec::new(); 16 | 17 | while !c.eof() { 18 | if let Some((i, next)) = c.ident() { 19 | if i == "compile_error" { 20 | if let Some((p, next)) = next.punct() { 21 | if p.as_char() == '!' { 22 | if let Some((TokenTree::Group(args), next)) = next.token_tree() { 23 | // println!("found compile_error!(...): {:?}", args); 24 | let real_args: syn::LitStr = syn::parse2(args.stream())?; 25 | // println!("real_args: {:?}", real_args); 26 | errors.push(real_args.value()); 27 | // errors.push(args); 28 | c = next; 29 | continue; 30 | } 31 | } 32 | } 33 | } 34 | } 35 | // Didn't recognize it. 36 | if let Some((_ignored, next)) = c.token_tree() { 37 | c = next; 38 | } else { 39 | println!("cursor is positioned on something that is not a token tree!"); 40 | break; 41 | } 42 | } 43 | 44 | Ok((Self { errors }, Cursor::empty())) 45 | }) 46 | } 47 | } 48 | 49 | fn test_worker(attrs: TokenStream, input: TokenStream, expected_errors: &[&'static str]) { 50 | let output = trace_logging_events_core(attrs, input); 51 | 52 | // Set WIN_ETW_SHOW_OUTPUT=1 (or = anything at all) to see the output of 53 | // the trace_logging_provider macro for unit tests. This is useful during 54 | // development. 55 | if std::env::var("WIN_ETW_SHOW_OUTPUT").is_ok() { 56 | let output_str = format!("{}", output); 57 | use std::io::Write; 58 | use std::process::{Command, Stdio}; 59 | let mut rustfmt_cmd = Command::new("rustfmt") 60 | .stdin(Stdio::piped()) 61 | .spawn() 62 | .expect("rustfmt failed"); 63 | let mut child_stdin = rustfmt_cmd.stdin.take().unwrap(); 64 | child_stdin.write_all(output_str.as_bytes()).unwrap(); 65 | drop(child_stdin); 66 | rustfmt_cmd.wait().unwrap(); 67 | } 68 | 69 | // Scan 'output' for errors. 70 | let errors: CompileErrors = syn::parse2(output).unwrap(); 71 | if expected_errors.is_empty() { 72 | assert!( 73 | errors.errors.is_empty(), 74 | "Macro produced errors:\n{:#?}", 75 | errors.errors 76 | ); 77 | } else { 78 | // For each of the errors in expected_errors, scan the list of actual errors. 79 | // Do a simple substring search. 80 | for &expected_error in expected_errors.iter() { 81 | if errors.errors.iter().any(|e| { 82 | // println!("checking in {:?}", e); 83 | e.contains(expected_error) 84 | }) { 85 | // println!("found expected error {:?}", expected_error); 86 | } else { 87 | panic!( 88 | "Did not find expected error {:?} in list:\n{:#?}", 89 | expected_error, errors.errors 90 | ); 91 | } 92 | } 93 | } 94 | 95 | // Make sure all errors found in compilation is expected. 96 | for error in errors.errors { 97 | if expected_errors.iter().any(|e| { 98 | // println!("checking in {:?}", e); 99 | let s = String::from(*e); 100 | error.contains(&s) 101 | }) { 102 | // println!("found expected error {:?}", expected_error); 103 | } else { 104 | panic!( 105 | "Did not match error {:?} with expected error in list:\n{:#?}", 106 | error, expected_errors 107 | ); 108 | } 109 | } 110 | } 111 | 112 | macro_rules! test_case { 113 | ( 114 | #[test] 115 | fn $test_case_name:ident(); 116 | 117 | input: { 118 | #[trace_logging_provider ( $( $attrs:tt )* )] 119 | $( $input:tt )* 120 | } 121 | 122 | expected_errors: [ 123 | $( $error:expr, )* 124 | ] 125 | 126 | ) => { 127 | #[test] 128 | fn $test_case_name() { 129 | let attrs = quote!{ $( $attrs )* }; 130 | 131 | let input = quote!{ $( $input )* }; 132 | let expected_errors = [ $( $error, )* ]; 133 | test_worker(attrs, input, &expected_errors); 134 | 135 | } 136 | } 137 | } 138 | 139 | test_case! { 140 | #[test] 141 | fn test_many_types(); 142 | input: { 143 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 144 | trait Events { 145 | fn arg_none(); 146 | 147 | fn arg_bool(a: bool); 148 | fn arg_u8(a: u8); 149 | fn arg_u16(a: u16); 150 | fn arg_u32(a: u32); 151 | fn arg_u64(a: u64); 152 | fn arg_i8(a: i8); 153 | fn arg_i16(a: i16); 154 | fn arg_i32(a: i32); 155 | fn arg_i64(a: i64); 156 | fn arg_f32(a: f32); 157 | fn arg_f64(a: f64); 158 | fn arg_usize(a: usize); 159 | fn arg_isize(a: isize); 160 | 161 | fn arg_slice_bool(a: &[bool]); 162 | fn arg_slice_u8(a: &[u8]); 163 | fn arg_slice_u16(a: &[u16]); 164 | fn arg_slice_u32(a: &[u32]); 165 | fn arg_slice_u64(a: &[u64]); 166 | fn arg_slice_i8(a: &[i8]); 167 | fn arg_slice_i16(a: &[i16]); 168 | fn arg_slice_i32(a: &[i32]); 169 | fn arg_slice_i64(a: &[i64]); 170 | fn arg_slice_f32(a: &[f32]); 171 | fn arg_slice_f64(a: &[f64]); 172 | fn arg_slice_usize(a: &[usize]); 173 | fn arg_slice_isize(a: &[isize]); 174 | 175 | fn arg_str(arg: &str); 176 | fn arg_u16cstr(arg: &U16CStr); 177 | fn arg_guid(arg: &GUID); 178 | fn arg_system_time(a: SystemTime); 179 | fn arg_filetime(a: FILETIME); 180 | 181 | #[event(level = "info")] 182 | fn arg_u8_at_info(a: u8); 183 | 184 | #[event(level = "warn")] 185 | fn arg_u8_at_warn(a: u8); 186 | 187 | #[event(level = "error")] 188 | fn arg_u8_at_error(a: u8); 189 | 190 | #[event(level = "verbose")] 191 | fn arg_u8_at_verbose(a: u8); 192 | 193 | #[event(level = 8)] 194 | fn arg_u8_at_level_8(a: u8); 195 | 196 | #[event(task = 100)] 197 | fn arg_with_task(a: u8); 198 | 199 | #[event(opcode = 10)] 200 | fn arg_with_opcode(a: u8); 201 | 202 | fn arg_u32_hex(#[event(output = "hex")] a: u32); 203 | 204 | fn arg_hresult(a: HRESULT); 205 | fn arg_ntstatus(a: NTSTATUS); 206 | fn arg_win32error(a: WIN32ERROR); 207 | } 208 | } 209 | expected_errors: [] 210 | } 211 | 212 | test_case! { 213 | #[test] 214 | fn test_unsupported_field_types(); 215 | input: { 216 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 217 | trait Events { 218 | fn event(a: ()); 219 | } 220 | } 221 | expected_errors: [ 222 | "This type is not supported for event parameters.", 223 | ] 224 | } 225 | 226 | test_case! { 227 | #[test] 228 | fn test_event_return_type(); 229 | input: { 230 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 231 | trait Events { 232 | fn event() -> String; 233 | } 234 | } 235 | expected_errors: [ 236 | "Event methods must not return data.", 237 | ] 238 | } 239 | 240 | test_case! { 241 | #[test] 242 | fn test_event_default_implementation(); 243 | input: { 244 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 245 | trait Events { 246 | fn event() { } 247 | } 248 | } 249 | expected_errors: [ 250 | "Event methods must not contain an implementation.", 251 | ] 252 | } 253 | 254 | test_case! { 255 | #[test] 256 | fn test_event_generic(); 257 | input: { 258 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 259 | trait Events { 260 | fn event(); 261 | } 262 | } 263 | expected_errors: [ 264 | "Generic event methods are not supported.", 265 | ] 266 | } 267 | 268 | test_case! { 269 | #[test] 270 | fn test_event_generic_lifetime(); 271 | input: { 272 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 273 | trait Events { 274 | fn event<'a>(); 275 | } 276 | } 277 | expected_errors: [ 278 | "Generic event methods are not supported.", 279 | ] 280 | } 281 | 282 | test_case! { 283 | #[test] 284 | fn test_wrong_self_ref(); 285 | input: { 286 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 287 | trait Events { 288 | fn event(&self); 289 | } 290 | } 291 | expected_errors: [ 292 | "Event methods should not provide any receiver arguments", 293 | ] 294 | } 295 | 296 | test_case! { 297 | #[test] 298 | fn test_wrong_self_mut(); 299 | input: { 300 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 301 | trait Events { 302 | fn event(&mut self); 303 | } 304 | } 305 | expected_errors: [ 306 | "Event methods should not provide any receiver arguments", 307 | ] 308 | } 309 | 310 | test_case! { 311 | #[test] 312 | fn test_wrong_self_move(); 313 | input: { 314 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 315 | trait Events { 316 | fn event(self); 317 | } 318 | } 319 | expected_errors: [ 320 | "Event methods should not provide any receiver arguments", 321 | ] 322 | } 323 | 324 | test_case! { 325 | #[test] 326 | fn test_bad_guid_literal(); 327 | input: { 328 | #[trace_logging_provider(guid = 0)] 329 | trait Events {} 330 | } 331 | expected_errors: [ 332 | "The attribute value is required to be a GUID in string form.", 333 | ] 334 | } 335 | 336 | test_case! { 337 | #[test] 338 | fn test_all_event_ids(); 339 | input: { 340 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 341 | trait Events { 342 | #[event(id = 1)] 343 | fn event_one(); 344 | 345 | #[event(id = 2)] 346 | fn event_two(); 347 | } 348 | } 349 | expected_errors: [ 350 | ] 351 | } 352 | 353 | test_case! { 354 | #[test] 355 | fn test_mixed_event_ids_fail(); 356 | input: { 357 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 358 | trait Events { 359 | #[event(id = 1)] 360 | fn event_one(); 361 | 362 | fn event_two(); 363 | } 364 | } 365 | 366 | expected_errors: [ 367 | "Event ids must be set for all events, or for none.", 368 | ] 369 | } 370 | 371 | test_case! { 372 | #[test] 373 | fn test_duplicate_event_ids_fail(); 374 | input: { 375 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 376 | trait Events { 377 | #[event(id = 1, id = 2)] 378 | fn event_one(); 379 | 380 | #[event(id = 3)] 381 | fn event_two(); 382 | } 383 | } 384 | 385 | expected_errors: [ 386 | "Event id has already been defined.", 387 | ] 388 | } 389 | 390 | test_case! { 391 | #[test] 392 | fn test_single_repeated_event_ids_fail(); 393 | input: { 394 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 395 | trait Events { 396 | #[event(id = 1)] 397 | fn event_one(); 398 | 399 | #[event(id = 1)] 400 | fn event_two(); 401 | } 402 | } 403 | 404 | expected_errors: [ 405 | "Event id 1 has already been defined on event_one.", 406 | ] 407 | } 408 | 409 | test_case! { 410 | #[test] 411 | fn test_multiple_repeated_event_ids_fail(); 412 | input: { 413 | #[trace_logging_provider(guid = "610259b8-9270-46f2-ad94-2f805721b287")] 414 | trait Events { 415 | #[event(id = 1)] 416 | fn event_one(); 417 | 418 | #[event(id = 2)] 419 | fn event_two(); 420 | 421 | #[event(id = 1)] 422 | fn event_three(); 423 | 424 | #[event(id = 2)] 425 | fn event_four(); 426 | } 427 | } 428 | 429 | expected_errors: [ 430 | "Event id 1 has already been defined on event_one.", 431 | "Event id 2 has already been defined on event_two.", 432 | ] 433 | } 434 | 435 | test_case! { 436 | #[test] 437 | fn test_bad_multiple_errors(); 438 | input: { 439 | #[trace_logging_provider(guid = "bad guid")] 440 | trait Events { 441 | fn bad_arg(a: ()); 442 | } 443 | } 444 | expected_errors: [ 445 | "The attribute value is required to be a valid GUID.", 446 | "This type is not supported for event parameters.", 447 | ] 448 | } 449 | 450 | test_case! { 451 | #[test] 452 | fn test_invalid_event_attributes(); 453 | input: { 454 | #[trace_logging_provider(guid = "00000000-0000-0000-0000-000000000001")] 455 | trait Events { 456 | #[event(bad_name = "bad_value")] 457 | fn event(); 458 | } 459 | } 460 | expected_errors: [ 461 | "Unrecognized attribute.", 462 | ] 463 | } 464 | 465 | test_case! { 466 | #[test] 467 | fn test_event_attributes_others_forbidden(); 468 | input: { 469 | #[trace_logging_provider(guid = "00000000-0000-0000-0000-000000000001")] 470 | trait Events { 471 | #[some_other_attribute] 472 | fn event(); 473 | } 474 | } 475 | expected_errors: [ 476 | "The only attributes allowed on event methods are #[doc] and #[event(...)] attributes.", 477 | ] 478 | } 479 | 480 | test_case! { 481 | #[test] 482 | fn wrong_item_kind(); 483 | input: { 484 | #[trace_logging_provider()] 485 | fn wrong_item_kind() {} 486 | } 487 | expected_errors: [ 488 | "The #[trace_logging_provider] attribute cannot be used with this kind of item.", 489 | ] 490 | } 491 | 492 | test_case! { 493 | #[test] 494 | fn multiple_errors_detected(); 495 | input: { 496 | #[trace_logging_provider()] 497 | trait Events 498 | { 499 | #[some_other_attribute] 500 | fn event_one(); 501 | 502 | #[event(id = 1)] 503 | fn event_two(); 504 | 505 | #[event(id = 2)] 506 | fn event_three(); 507 | 508 | #[event(id = 1)] 509 | fn event_four(); 510 | } 511 | 512 | } 513 | expected_errors: [ 514 | "The only attributes allowed on event methods are #[doc] and #[event(...)] attributes.", 515 | "Event ids must be set for all events, or for none.", 516 | "Event id 1 has already been defined on event_two.", 517 | ] 518 | } 519 | 520 | test_case! { 521 | #[test] 522 | fn provider_groups(); 523 | input: { 524 | #[trace_logging_provider( 525 | guid = "00000000-0000-0000-0000-000000000001", 526 | provider_group_guid = "00000000-0000-0000-0000-000000000002", 527 | )] 528 | trait Events 529 | { 530 | fn foo(); 531 | } 532 | } 533 | expected_errors: [ 534 | ] 535 | } 536 | 537 | test_case! { 538 | #[test] 539 | fn provider_attributes_multiple_provider_groups(); 540 | input: { 541 | #[trace_logging_provider( 542 | guid = "00000000-0000-0000-0000-000000000001", 543 | provider_group_guid = "00000000-0000-0000-0000-000000000002", 544 | provider_group_guid = "00000000-0000-0000-0000-000000000003", 545 | )] 546 | trait Events 547 | { 548 | fn foo(); 549 | } 550 | } 551 | expected_errors: [ 552 | "The 'provider_group_guid' attribute key cannot be specified more than once.", 553 | ] 554 | } 555 | 556 | test_case! { 557 | #[test] 558 | fn event_with_keyword(); 559 | input: { 560 | #[trace_logging_provider(guid = "00000000-0000-0000-0000-000000000001")] 561 | trait Events 562 | { 563 | #[event(keyword = 100)] 564 | fn foo(); 565 | } 566 | } 567 | expected_errors: [] 568 | } 569 | 570 | test_case! { 571 | #[test] 572 | fn error_multiple_keyword(); 573 | input: { 574 | #[trace_logging_provider(guid = "00000000-0000-0000-0000-000000000001")] 575 | trait Events 576 | { 577 | #[event(keyword = 100)] 578 | fn foo(); 579 | } 580 | } 581 | expected_errors: [] 582 | } 583 | 584 | use quote::quote; 585 | 586 | fn test_provider_attributes_error(input: TokenStream, expected_errors: &[&str]) { 587 | match syn::parse2::(input) { 588 | Ok(parsed) => { 589 | panic!("Expected parsing of input to fail. Output: {:?}", parsed); 590 | } 591 | Err(combined_error) => { 592 | check_errors(&combined_error, expected_errors); 593 | } 594 | } 595 | } 596 | 597 | #[test] 598 | fn provider_attributes_invalid_meta() { 599 | // We do not check the error details for this, because they are not under our control. 600 | // This is a failure to parse the comma-separated syn::Meta list. 601 | let result = syn::parse2::(quote! { bad bad bad }); 602 | assert!(result.is_err()); 603 | } 604 | 605 | #[test] 606 | fn provider_attributes_unrecognized_key() { 607 | test_provider_attributes_error( 608 | quote!(bad_name = "bad_value"), 609 | &["Unrecognized attribute key."], 610 | ); 611 | } 612 | 613 | #[test] 614 | fn provider_attributes_nil_guid() { 615 | test_provider_attributes_error( 616 | quote!(guid = "00000000-0000-0000-0000-000000000000"), 617 | &["The GUID cannot be the NIL (all-zeroes) GUID."], 618 | ); 619 | } 620 | 621 | #[test] 622 | fn provider_attributes_invalid_guid() { 623 | test_provider_attributes_error( 624 | quote!(guid = "xxx"), 625 | &["The attribute value is required to be a valid GUID."], 626 | ); 627 | } 628 | 629 | #[test] 630 | fn provider_attributes_dup_guid() { 631 | test_provider_attributes_error( 632 | quote!( 633 | guid = "610259b8-9270-46f2-ad94-2f805721b287", 634 | guid = "610259b8-9270-46f2-ad94-2f805721b287" 635 | ), 636 | &["The 'guid' attribute key cannot be specified more than once."], 637 | ); 638 | } 639 | 640 | #[test] 641 | fn provider_attributes_valid() { 642 | let result = syn::parse2::(quote! { 643 | guid = "610259b8-9270-46f2-ad94-2f805721b287" 644 | }); 645 | assert!(result.is_ok(), "Result: {:?}", result); 646 | } 647 | 648 | #[test] 649 | fn provider_attributes_valid_static() { 650 | let result = syn::parse2::(quote! { 651 | guid = "610259b8-9270-46f2-ad94-2f805721b287", static_mode 652 | }); 653 | assert!(result.is_ok(), "Result: {:?}", result); 654 | } 655 | 656 | fn check_errors(error: &Error, expected_errors: &[&str]) { 657 | let mut error_strings: Vec = error.into_iter().map(|e| format!("{}", e)).collect(); 658 | 659 | // Go through all the expected errors and ensure we find them in the output. 660 | for expected_error in expected_errors.iter() { 661 | let position = error_strings 662 | .iter() 663 | .position(|e| e.contains(expected_error)); 664 | if let Some(idx) = position { 665 | error_strings.remove(idx); 666 | // good 667 | } else { 668 | eprintln!("\nDid not find this error in list: {:?}", expected_error); 669 | eprintln!("Actual errors:"); 670 | for e in error_strings.iter() { 671 | eprintln!(" {:?}", e); 672 | } 673 | panic!("Error strings did not match."); 674 | } 675 | } 676 | 677 | for error_string in error_strings.iter() { 678 | panic!("Unexpected error: {}", error_string); 679 | } 680 | } 681 | 682 | #[test] 683 | fn test_etw_name_based_guid() { 684 | // the expected values below were generated by passing the provider name to 685 | // `[System.Diagnostics.Tracing.EventSource]::new("YourProviderName").Guid` 686 | use uuid::uuid; 687 | assert_eq!( 688 | uuid!("5fefebda-b28e-5a81-d371-cebf3d3ddb41"), 689 | etw_event_source_guid("YourProviderName") 690 | ); 691 | assert_eq!( 692 | uuid!("6f8eac67-f87f-598a-71a0-67e48d8c468d"), 693 | etw_event_source_guid("YourProviderNameYourProviderName") 694 | ); 695 | assert_eq!( 696 | uuid!("5b1bddda-c110-53c9-b030-17aea3bfabd9"), 697 | etw_event_source_guid("YourProviderNameYourProviderNameYourProviderNameYourProviderName") 698 | ); 699 | assert_eq!( 700 | uuid!("0cec4c9d-caa7-5d85-b3bd-c73577f03fd8"), 701 | etw_event_source_guid("Your.Provider.Name") 702 | ); 703 | assert_eq!( 704 | uuid!("32805bf5-e0cc-522a-5f56-20c3e359ed93"), 705 | etw_event_source_guid("Your.Provider.Name.Your.Provider.Name") 706 | ); 707 | assert_eq!( 708 | uuid!("ce5fa4ea-ab00-5402-8b76-9f76ac858fb5"), 709 | etw_event_source_guid("MyCompany.MyComponent") 710 | ); 711 | assert_eq!( 712 | uuid!("0d31f5cc-fb84-50db-a602-8c7bed9c5b8b"), 713 | etw_event_source_guid("ProviderWithAutogeneratedGuid") 714 | ); 715 | } 716 | -------------------------------------------------------------------------------- /win_etw_macros/src/well_known_types.rs: -------------------------------------------------------------------------------- 1 | use syn::parse_quote; 2 | use win_etw_metadata::{InFlag, OutFlag}; 3 | 4 | pub struct WellKnownTypeInfo { 5 | pub ty: syn::Type, 6 | pub code: WellKnownType, 7 | pub in_type: InFlag, 8 | pub is_ref: bool, 9 | /// Indicates whether this type can be used in a slice, e.g. &[T]. 10 | /// Should probably rename to `can_slice`. 11 | pub primitive: bool, 12 | pub opts: WellKnownTypeOptions, 13 | } 14 | 15 | #[derive(Default)] 16 | pub struct WellKnownTypeOptions { 17 | pub out_type: Option, 18 | pub in_type_expr: Option, 19 | pub replacement_type: Option, 20 | #[allow(unused)] 21 | pub can_output_hex: bool, 22 | } 23 | 24 | macro_rules! well_known_types{ 25 | ( 26 | $( 27 | $t:ident: $tt:ty => { 28 | is_ref: $is_ref:expr, 29 | primitive: $primitive:expr, 30 | in_type: $in_type:expr, 31 | $( $opt_name:ident: $opt_value:expr, )* 32 | } 33 | )* 34 | ) => { 35 | #[allow(non_camel_case_types)] 36 | #[derive(Copy, Clone, Eq, PartialEq)] 37 | pub enum WellKnownType { 38 | $($t,)* 39 | } 40 | 41 | #[allow(non_snake_case)] 42 | pub struct WellKnownTypes { 43 | $( 44 | $t: WellKnownTypeInfo, 45 | )* 46 | } 47 | 48 | impl WellKnownTypes { 49 | pub fn new() -> Self { 50 | Self { 51 | $( 52 | $t: WellKnownTypeInfo { 53 | ty: parse_quote!( $tt ), 54 | code: WellKnownType::$t, 55 | is_ref: $is_ref, 56 | primitive: $primitive, 57 | in_type: $in_type, 58 | opts: WellKnownTypeOptions { 59 | $($opt_name: $opt_value,)* 60 | .. 61 | WellKnownTypeOptions::default() 62 | }, 63 | }, 64 | )* 65 | } 66 | } 67 | 68 | pub fn find(&self, ty: &syn::Type) -> Option<&WellKnownTypeInfo> { 69 | $( 70 | if *ty == self.$t.ty { 71 | return Some(&self.$t); 72 | } 73 | )* 74 | None 75 | } 76 | } 77 | } 78 | } 79 | 80 | well_known_types! { 81 | bool: bool => { 82 | is_ref: false, 83 | primitive: true, 84 | in_type: InFlag::INT8, 85 | out_type: Some(OutFlag::BOOLEAN), 86 | } 87 | u8: u8 => { is_ref: false, primitive: true, in_type: InFlag::UINT8, can_output_hex: true, } 88 | u16: u16 => { is_ref: false, primitive: true, in_type: InFlag::UINT16, can_output_hex: true, } 89 | u32: u32 => { is_ref: false, primitive: true, in_type: InFlag::UINT32, can_output_hex: true, } 90 | u64: u64 => { is_ref: false, primitive: true, in_type: InFlag::UINT64, can_output_hex: true, } 91 | i8: i8 => { is_ref: false, primitive: true, in_type: InFlag::INT8, can_output_hex: true, } 92 | i16: i16 => { is_ref: false, primitive: true, in_type: InFlag::INT16, can_output_hex: true, } 93 | i32: i32 => { is_ref: false, primitive: true, in_type: InFlag::INT32, can_output_hex: true, } 94 | i64: i64 => { is_ref: false, primitive: true, in_type: InFlag::INT64, can_output_hex: true, } 95 | f32: f32 => { is_ref: false, primitive: true, in_type: InFlag::FLOAT, } 96 | f64: f64 => { is_ref: false, primitive: true, in_type: InFlag::DOUBLE, } 97 | usize: usize => { is_ref: false, primitive: true, in_type: InFlag::NULL, 98 | in_type_expr: Some(parse_quote!{ 99 | ::win_etw_provider::metadata::InFlag::USIZE.bits() 100 | }), 101 | can_output_hex: true, 102 | } 103 | isize: isize => { is_ref: false, primitive: true, in_type: InFlag::NULL, 104 | in_type_expr: Some(parse_quote!{ 105 | ::win_etw_provider::metadata::InFlag::ISIZE.bits() 106 | }), 107 | can_output_hex: true, 108 | } 109 | ref_str: &str => { 110 | is_ref: true, 111 | primitive: false, 112 | in_type: InFlag::COUNTED_ANSI_STRING, 113 | out_type: Some(OutFlag::UTF8), 114 | } 115 | u16str: &U16Str => { 116 | is_ref: true, 117 | primitive: false, 118 | in_type: InFlag::COUNTED_UNICODE_STRING, 119 | replacement_type: Some(parse_quote!(&::widestring::U16Str)), 120 | } 121 | u16cstr: &U16CStr => { 122 | is_ref: true, 123 | primitive: false, 124 | in_type: InFlag::COUNTED_UNICODE_STRING, 125 | replacement_type: Some(parse_quote!(&::widestring::U16CStr)), 126 | } 127 | osstr: &OsStr => { 128 | is_ref: true, 129 | primitive: false, 130 | in_type: InFlag::COUNTED_UNICODE_STRING, 131 | replacement_type: Some(parse_quote!(&::std::ffi::OsStr)), 132 | } 133 | guid: &GUID => { 134 | is_ref: true, primitive: false, 135 | in_type: InFlag::GUID, 136 | replacement_type: Some(parse_quote!(&::win_etw_provider::GUID)), 137 | } 138 | SocketAddrV4: &SocketAddrV4 => { 139 | is_ref: false, 140 | primitive: false, 141 | in_type: InFlag::BINARY, 142 | out_type: Some(OutFlag::SOCKETADDRESS), 143 | replacement_type: Some(parse_quote!(&::std::net::SocketAddrV4)), 144 | } 145 | SocketAddrV6: &SocketAddrV6 => { 146 | is_ref: false, 147 | primitive: false, 148 | in_type: InFlag::BINARY, 149 | out_type: Some(OutFlag::SOCKETADDRESS), 150 | replacement_type: Some(parse_quote!(&::std::net::SocketAddrV6)), 151 | } 152 | SocketAddr: &SocketAddr => { 153 | is_ref: false, 154 | primitive: false, 155 | in_type: InFlag::BINARY, 156 | out_type: Some(OutFlag::SOCKETADDRESS), 157 | replacement_type: Some(parse_quote!(&::std::net::SocketAddr)), 158 | } 159 | SystemTime: SystemTime => { 160 | is_ref: false, 161 | primitive: false, 162 | in_type: InFlag::FILETIME, 163 | replacement_type: Some(parse_quote!(::std::time::SystemTime)), 164 | } 165 | FILETIME: FILETIME => { 166 | is_ref: true, 167 | primitive: false, 168 | in_type: InFlag::FILETIME, 169 | replacement_type: Some(parse_quote!(::win_etw_provider::FILETIME)), 170 | } 171 | HRESULT: HRESULT => { 172 | is_ref: false, 173 | primitive: false, 174 | in_type: InFlag::INT32, 175 | replacement_type: Some(parse_quote!(i32)), 176 | out_type: Some(OutFlag::HRESULT), 177 | } 178 | WIN32ERROR: WIN32ERROR => { 179 | is_ref: false, 180 | primitive: false, 181 | in_type: InFlag::UINT32, 182 | replacement_type: Some(parse_quote!(u32)), 183 | out_type: Some(OutFlag::WIN32ERROR), 184 | } 185 | NTSTATUS: NTSTATUS => { 186 | is_ref: false, 187 | primitive: false, 188 | in_type: InFlag::UINT32, 189 | replacement_type: Some(parse_quote!(u32)), 190 | out_type: Some(OutFlag::NTSTATUS), 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /win_etw_metadata/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "win_etw_metadata" 3 | version = "0.1.2" 4 | authors = ["Arlie Davis "] 5 | edition = "2018" 6 | description = "Provides metadata definitions for the win_etw_provider and win_etw_macros crates." 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/microsoft/rust_win_etw" 9 | readme = "../README.md" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | bitflags = "^1.2" 15 | 16 | [features] 17 | default = ["metadata_headers"] 18 | metadata_headers = [] 19 | -------------------------------------------------------------------------------- /win_etw_metadata/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Definitions for metadata used by 2 | //! [Event Tracing for Windows](https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-portal). 3 | //! 4 | //! These definitions are used by the `win_etw_macros` crate to describe the schema of events. 5 | //! Most applications will not need to use these definitions directly. 6 | 7 | #![no_std] 8 | #![deny(missing_docs)] 9 | 10 | use bitflags::bitflags; 11 | 12 | /// This structure describes the start of the ETW metadata section. A single static instance of 13 | /// this structure is placed in PE/COFF modules, and it identifies the start of the ETW metadata 14 | /// section. In this implementation, that single instance is `ETW_TRACE_LOGGING_METADATA`. 15 | #[repr(C)] 16 | pub struct TraceLoggingMetadata { 17 | signature: u32, // = _tlg_MetadataSignature = "ETW0" 18 | size: u16, // = sizeof(_TraceLoggingMetadata_t) 19 | version: u8, // = _tlg_MetadataVersion 20 | flags: u8, // = _tlg_MetadataFlags 21 | magic: u64, // = _tlg_MetadataMagic 22 | } 23 | 24 | /// The value stored in `TraceLoggingMetadata::signature`. 25 | /// In little-endian ASCII, this is "ETW0". 26 | pub const METADATA_SIGNATURE: u32 = 0x30_57_54_45; 27 | 28 | /// The value stored in `TraceLoggingMetadata::magic`. 29 | pub const METADATA_MAGIC: u64 = 0xBB8A_052B_8804_0E86; 30 | 31 | /// The version of the metadata emitted. Currently, there is only one version. 32 | pub const METADATA_VERSION: u8 = 0; 33 | 34 | /// The bit flag which indicates the size of pointers on the target architecture. 35 | /// The value of this constant depends on the target architecture. 36 | #[cfg(target_pointer_width = "64")] 37 | pub const METADATA_FLAGS_POINTER_WIDTH: u8 = 1; 38 | 39 | /// The bit flag which indicates the size of pointers on the target architecture. 40 | /// The value of this constant depends on the target architecture. 41 | #[cfg(not(target_pointer_width = "64"))] 42 | pub const METADATA_FLAGS_POINTER_WIDTH: u8 = 0; 43 | 44 | #[cfg(feature = "metadata_headers")] 45 | #[link_section = ".rdata$etw0"] 46 | #[used] 47 | #[no_mangle] 48 | static ETW_TRACE_LOGGING_METADATA: TraceLoggingMetadata = TraceLoggingMetadata { 49 | signature: METADATA_SIGNATURE, 50 | size: core::mem::size_of::() as u16, 51 | version: METADATA_VERSION, 52 | flags: METADATA_FLAGS_POINTER_WIDTH, 53 | magic: METADATA_MAGIC, 54 | }; 55 | 56 | /// Predefined event tracing levels 57 | #[repr(transparent)] 58 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] 59 | pub struct Level(pub u8); 60 | 61 | impl Level { 62 | /// Tracing is not on 63 | pub const NONE: Level = Level(0); 64 | /// Abnormal exit or termination 65 | pub const CRITICAL: Level = Level(1); 66 | /// Deprecated name for Abnormal exit or termination 67 | pub const FATAL: Level = Level(1); 68 | /// Severe errors that need logging 69 | pub const ERROR: Level = Level(2); 70 | /// Warnings such as allocation failure 71 | pub const WARN: Level = Level(3); 72 | /// Includes non-error cases(e.g.,Entry-Exit) 73 | pub const INFO: Level = Level(4); 74 | /// Detailed traces from intermediate steps 75 | pub const VERBOSE: Level = Level(5); 76 | } 77 | 78 | bitflags! { 79 | /// Defines the input type of a field. 80 | /// In traceloggingprovider.h, this is the 'TlgIn_t` enumerated type. 81 | #[repr(transparent)] 82 | pub struct InFlag: u8 { 83 | /// No value at all 84 | const NULL = 0; 85 | /// A wide string (UTF-16), corresponding to `PCWSTR` in Win32. 86 | const UNICODE_STRING = 1; 87 | /// An ANSI string, corresponding to `PCSTR` in Win32. 88 | /// The character set can be specified as UTF-8 by using `OutFlag::UTF8`. 89 | const ANSI_STRING = 2; 90 | /// `i8` 91 | const INT8 = 3; 92 | /// `u8` 93 | const UINT8 = 4; 94 | /// `i16`, stored in little-endian form. 95 | const INT16 = 5; 96 | /// `u16`, stored in little-endian form. 97 | const UINT16 = 6; 98 | /// `i16`, stored in little-endian form. 99 | const INT32 = 7; 100 | /// `u32`, stored in little-endian form. 101 | const UINT32 = 8; 102 | /// `i64`, stored in little-endian form. 103 | const INT64 = 9; 104 | /// `u64`, stored in little-endian form. 105 | const UINT64 = 10; 106 | /// `f32`, stored in little-endian form. 107 | const FLOAT = 11; 108 | /// `f64`, stored in little-endian form. 109 | const DOUBLE = 12; 110 | /// A Win32 'BOOL' value, which is `i32`, stored in little-endian form. 111 | const BOOL32 = 13; 112 | /// An array of bytes, stored in little-endian form. 113 | const BINARY = 14; 114 | /// A `GUID`, stored in canonical byte-oriented representation. The fields within the `GUID` 115 | /// are stored in big-endian form. 116 | const GUID = 15; 117 | // POINTER (16) is not supported 118 | /// A Win32 [`FILETIME`](https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime) 119 | /// value. `FILETIME` values are `u64` values, stored in little-endian form, counting 100ns 120 | /// intervals from the `FILETIME` epoch. 121 | const FILETIME = 17; 122 | /// A Win32 [`SYSTEMTIME`](https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime) 123 | /// value, with fields encoded in little-endian form. 124 | const SYSTEMTIME = 18; 125 | /// A Win32 [`SID`](https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid). 126 | const SID = 19; 127 | /// An `i32` value, encoded in little-endian form, displayed in hexadecimal. 128 | const HEXINT32 = 20; 129 | /// An `i64` value, encoded in little-endian form, displayed in hexadecimal. 130 | const HEXINT64 = 21; 131 | /// A counted wide string (UTF-16), corresponding to `UNICODE_STRING` in Win32. 132 | /// This type uses two data descriptor slots. The first is a `u16` value, giving the 133 | /// length of the string data in WCHAR units (not bytes). The second points to the 134 | /// character data. 135 | const COUNTED_UNICODE_STRING = 22; 136 | /// A counted ANSI string, corresponding to `STRING` in Win32. 137 | /// The character set can be specified as UTF-8 by using `OutFlag::UTF8`. 138 | /// This type uses two data descriptor slots. The first is a `u16` value, giving the 139 | /// length of the string data in WCHAR units (not bytes). The second points to the 140 | /// character data. 141 | const COUNTED_ANSI_STRING = 23; 142 | /// A flag which indicates that this field is an array of constant length. 143 | /// If this field is present, then the metadata contains an additional `u16` field, which 144 | /// is the constant length. 145 | const CCOUNT_FLAG = 0x20; 146 | /// A flag which indicates that this field has a dynamic length. The field uses two 147 | /// data descriptors. The first is a `u16` field specifying the length of the array. 148 | /// The second points to the array data. 149 | const VCOUNT_FLAG = 0x40; 150 | /// A flag which indicates that this field metadata also includes an `OutFlag`. The 151 | /// `OutFlag` byte immediately follows the `InFlag` byte. 152 | const CHAIN_FLAG = 0b1000_0000; 153 | /// A flag which indicates that the field uses a custom serializer. 154 | const CUSTOM_FLAG = 0b0110_0000; 155 | /// A mask of the field type flags. 156 | const TYPE_MASK = 0b0001_1111; 157 | /// A mask of the field length flags (`VCOUNT_FLAG`, `CCOUNT_FLAG`, `CUSTOM_FLAG`). 158 | const COUNT_MASK = 0b0110_0000; 159 | /// A mask over all of the flags (all bits excluding the type bits). 160 | const FLAG_MASK = 0b1110_0000; 161 | } 162 | } 163 | 164 | impl InFlag { 165 | /// An alias for the architecture-dependent `USIZE` (pointer-sized word) `InFlag`. 166 | #[cfg(target_pointer_width = "32")] 167 | pub const USIZE: InFlag = InFlag::UINT32; 168 | 169 | /// An alias for the architecture-dependent `ISIZE` (pointer-sized word) `InFlag`. 170 | #[cfg(target_pointer_width = "32")] 171 | pub const ISIZE: InFlag = InFlag::INT32; 172 | 173 | /// An alias for the architecture-dependent `USIZE` (pointer-sized word) `InFlag`. 174 | #[cfg(target_pointer_width = "64")] 175 | pub const USIZE: InFlag = InFlag::UINT64; 176 | 177 | /// An alias for the architecture-dependent `ISIZE` (pointer-sized word) `InFlag`. 178 | #[cfg(target_pointer_width = "64")] 179 | pub const ISIZE: InFlag = InFlag::INT64; 180 | } 181 | 182 | bitflags! { 183 | /// Specifies how a field should be interpreted or displayed. 184 | #[repr(transparent)] 185 | pub struct OutFlag: u8 { 186 | /// No display at all. 187 | const NULL = 0; 188 | /// No display at all. 189 | const NOPRINT = 1; 190 | /// Contains text. 191 | const STRING = 2; 192 | /// Is a boolean. This can only be used with `InFlag::INT8`. 193 | const BOOLEAN = 3; 194 | /// Display in hexadecimal. Can be used with any integer type. 195 | const HEX = 4; 196 | /// The field is a Win32 process ID. 197 | const PID = 5; 198 | /// The field is a Win32 thread ID. 199 | const TID = 6; 200 | /// The field is a TCP/IP or UDP/IP port, in big-endian encoding. 201 | const PORT = 7; 202 | /// The field is an IP v4 address, in big-endian encoding. 203 | const IPV4 = 8; 204 | /// The field is an IP v6 address, in big-endian encoding. 205 | const IPV6 = 9; 206 | /// The field is a `SOCKADDR`. See `[SOCKADDR](https://docs.microsoft.com/en-us/windows/win32/api/winsock/ns-winsock-sockaddr)`. 207 | const SOCKETADDRESS = 10; 208 | /// The field is text, and should be interpreted as XML. 209 | const XML = 11; 210 | /// The field is text, and should be interpreted as JSON. 211 | const JSON = 12; 212 | /// The field is a `Win32` error code. The field type should be `InFlag::UINT32`. 213 | const WIN32ERROR = 13; 214 | /// The field is an `NTSTATUS` error code. The field type should be `InFlag::UINT32`. 215 | const NTSTATUS = 14; 216 | /// The field is an `HRESULT` error code. The field type should be `InFlag::INT32`. 217 | const HRESULT = 15; 218 | /// The field is a Win32 `FILETIME` value. 219 | const FILETIME = 16; 220 | /// Display field as signed. 221 | const SIGNED = 17; 222 | /// Display field as unsigned. 223 | const UNSIGNED = 18; 224 | /// For strings, indicates that the string encoding is UTF-8. 225 | const UTF8 = 35; 226 | /// Used with `InFlag::BINARY`. 227 | const PKCS7_WITH_TYPE_INFO = 36; 228 | // const CODE_POINTER = 37; 229 | 230 | /// Indicates that the timezone for a time value is UTC. 231 | /// This can be used with `InFlag::FILETIME` or `InFlag::SYSTEMTIME`. 232 | const DATETIME_UTC = 38; 233 | } 234 | } 235 | 236 | // Event categories specified via keywords 237 | /// Event category for critical data (specified via keyword) 238 | pub const MICROSOFT_KEYWORD_CRITICAL_DATA: u64 = 0x0000_8000_0000_0000; 239 | /// Event category for measures (specified via keyword) 240 | pub const MICROSOFT_KEYWORD_MEASURES: u64 = 0x0000_4000_0000_0000; 241 | /// Event category for telemetry (specified via keyword) 242 | pub const MICROSOFT_KEYWORD_TELEMETRY: u64 = 0x0000_2000_0000_0000; 243 | 244 | // Event categories specified via event tags 245 | /// Event category defined by WIL 246 | pub const MICROSOFT_EVENTTAG_DROP_USER_IDS: u32 = 0x0000_8000; 247 | /// Event category defined by WIL 248 | pub const MICROSOFT_EVENTTAG_AGGREGATE: u32 = 0x0001_0000; 249 | /// Event category defined by WIL 250 | pub const MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP: u32 = 0x0002_0000; 251 | /// Event category defined by WIL 252 | pub const MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY: u32 = 0x0004_0000; 253 | /// Event category defined by WIL 254 | pub const MICROSOFT_EVENTTAG_CORE_DATA: u32 = 0x0008_0000; 255 | /// Event category defined by WIL 256 | pub const MICROSOFT_EVENTTAG_INJECT_XTOKEN: u32 = 0x0010_0000; 257 | /// Event category defined by WIL 258 | pub const MICROSOFT_EVENTTAG_REALTIME_LATENCY: u32 = 0x0020_0000; 259 | /// Event category defined by WIL 260 | pub const MICROSOFT_EVENTTAG_NORMAL_LATENCY: u32 = 0x0040_0000; 261 | /// Event category defined by WIL 262 | pub const MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE: u32 = 0x0080_0000; 263 | /// Event category defined by WIL 264 | pub const MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE: u32 = 0x0100_0000; 265 | /// Event category defined by WIL 266 | pub const MICROSOFT_EVENTTAG_DROP_PII: u32 = 0x0200_0000; 267 | /// Event category defined by WIL 268 | pub const MICROSOFT_EVENTTAG_HASH_PII: u32 = 0x0400_0000; 269 | /// Event category defined by WIL 270 | pub const MICROSOFT_EVENTTAG_MARK_PII: u32 = 0x0800_0000; 271 | -------------------------------------------------------------------------------- /win_etw_provider/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "win_etw_provider" 3 | version = "0.1.11" 4 | authors = ["Arlie Davis "] 5 | edition = "2018" 6 | description = "Enables apps to report events to Event Tracing for Windows (ETW)." 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/microsoft/rust_win_etw" 9 | readme = "../README.md" 10 | rust-version = "1.77" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | widestring = {version = "^1.0", default-features = false, features = ["alloc"]} 16 | zerocopy = { version = "0.8.23", features = ["derive"] } 17 | win_etw_metadata = { version = "^0.1.2", path = "../win_etw_metadata" } 18 | uuid = {version = "1", optional = true} 19 | 20 | [target.'cfg(windows)'.dependencies] 21 | winapi = { version = "^0.3", features = ["evntprov", "winerror", "evntrace"] } 22 | 23 | [dev-dependencies] 24 | atomic_lazy = { git = "https://github.com/sivadeilra/atomic_lazy" } 25 | uuid = "1" 26 | 27 | [features] 28 | std = [] 29 | default = ["no_std"] 30 | no_std = [] 31 | # dev is used only for development 32 | dev = [] 33 | -------------------------------------------------------------------------------- /win_etw_provider/src/data_descriptor.rs: -------------------------------------------------------------------------------- 1 | use crate::guid::GUID; 2 | use core::marker::PhantomData; 3 | use core::mem::size_of; 4 | use widestring::{U16CStr, U16Str}; 5 | use zerocopy::IntoBytes; 6 | 7 | /// Contains a reference to the data for an event field. The type of the data is not specified in 8 | /// this structure; instead, the type of the data is stored in the event's metadata. 9 | /// (See `win_etw_metadata::InFlag`.) 10 | /// 11 | /// The data that this type points to must have a well-defined (stable) byte representation. For 12 | /// example, `u32` has a well-defined byte representation, as long as there is agreement about 13 | /// whether the value is stored in big-endian or little-endian order. Similarly, `[u8]` and 14 | /// `[u32]` have well-defined byte representations. However, types such as `[bool]` do not have a 15 | /// stable byte representation, and so `EventDataDescriptor` cannot point to `&[bool]`. 16 | /// 17 | /// This type provides implementations of `From` that can be used to point to event data. 18 | /// All of the `EventDataDescriptor::From` implementations for types require that the types have a 19 | /// stable, guaranteed byte representation, and that is legal (meaningful) to read that byte 20 | /// representation. 21 | /// 22 | /// This type is equivalent to the Win32 structure `EVENT_DATA_DESCRIPTOR`, and its representation 23 | /// is guaranteed to be equivalent. 24 | /// 25 | /// # Implementation warning! 26 | /// 27 | /// This code is responsible for ensuring memory safety. Even though it contains only simple 28 | /// primitives, these primitives are actually native pointers and pointer bounds. This data 29 | /// structure is passed to the ETW implementation, which dereferences those pointers. The Rust 30 | /// type checker cannot "see" these dereferences, since they occur in non-Rust code, so the borrow 31 | /// checker does not know that `EventDataDescriptor` deals with memory safety. This is why the 32 | /// `phantom_ref` field exists, and it is _crucial_ that this code be used and encapsulated 33 | /// correctly. 34 | /// 35 | /// For type safety to be conserved, the following invariants *must* be maintained: 36 | /// 37 | /// * The `'a` lifetime parameter of `EventDataDescriptor<'a>` must be correctly associated with 38 | /// the lifetime of any reference that is used to construct an instance of `EventDataDescriptor`. 39 | /// 40 | /// * The fields of `EventDataDescriptor` must remain private. Arbitrary user code cannot be 41 | /// permitted to construct instances of `EventDataDescriptor` with arbitrary values for these 42 | /// fields. 43 | /// 44 | /// * `EventDataDescriptor` should only be used to pass to ETW functions. 45 | #[repr(C)] 46 | #[derive(Clone)] 47 | pub struct EventDataDescriptor<'a> { 48 | // descriptor: evntprov::EVENT_DATA_DESCRIPTOR, 49 | ptr: u64, 50 | size: u32, 51 | 52 | /// In the Windows SDK, this field is marked "reserved" and is a union. However, it is clear 53 | /// from usage within `traceloggingprovider.h` that this field is used to identify event data, 54 | /// provider metadata, and event metadata. 55 | kind: u32, 56 | 57 | /// Represents the lifetime of the pointed-to data. 58 | phantom_ref: PhantomData<&'a ()>, 59 | } 60 | 61 | impl EventDataDescriptor<'static> { 62 | /// Returns an empty data descriptor. 63 | pub fn empty() -> Self { 64 | Self { 65 | ptr: 0, 66 | size: 0, 67 | kind: 0, 68 | phantom_ref: PhantomData, 69 | } 70 | } 71 | } 72 | 73 | const EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA: u32 = 2; 74 | const EVENT_DATA_DESCRIPTOR_TYPE_EVENT_METADATA: u32 = 1; 75 | 76 | impl<'a> EventDataDescriptor<'a> { 77 | /// Creates an `EventDataDescriptor` for provider metadata. 78 | pub fn for_provider_metadata(s: &'a [u8]) -> Self { 79 | Self { 80 | ptr: s.as_ptr() as usize as u64, 81 | size: s.len() as u32, 82 | kind: EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA, 83 | phantom_ref: PhantomData, 84 | } 85 | } 86 | 87 | /// Creates an `EventDataDescriptor` for the metadata that describes a single event. 88 | pub fn for_event_metadata(s: &'a [u8]) -> Self { 89 | Self { 90 | ptr: s.as_ptr() as usize as u64, 91 | size: s.len() as u32, 92 | kind: EVENT_DATA_DESCRIPTOR_TYPE_EVENT_METADATA, 93 | phantom_ref: PhantomData, 94 | } 95 | } 96 | 97 | /// Creates a `EventDataDescriptor for a slice of bytes. 98 | pub fn for_bytes(s: &'a [u8]) -> Self { 99 | Self { 100 | ptr: s.as_ptr() as usize as u64, 101 | size: s.len() as u32, 102 | kind: 0, 103 | phantom_ref: PhantomData, 104 | } 105 | } 106 | } 107 | 108 | macro_rules! well_known_types { 109 | ( 110 | $( 111 | $t:ident ; 112 | )* 113 | ) => { 114 | $( 115 | impl<'a> From<&'a $t> for EventDataDescriptor<'a> { 116 | fn from(value: &'a $t) -> EventDataDescriptor<'a> { 117 | EventDataDescriptor::for_bytes(value.as_bytes()) 118 | } 119 | } 120 | 121 | impl<'a> From<&'a [$t]> for EventDataDescriptor<'a> { 122 | fn from(value: &'a [$t]) -> EventDataDescriptor<'a> { 123 | EventDataDescriptor::for_bytes(value.as_bytes()) 124 | } 125 | } 126 | )* 127 | } 128 | } 129 | 130 | well_known_types! { 131 | bool; 132 | u8; u16; u32; u64; 133 | i8; i16; i32; i64; 134 | f32; f64; 135 | usize; isize; 136 | } 137 | 138 | impl<'a> From<&'a str> for EventDataDescriptor<'a> { 139 | fn from(value: &'a str) -> EventDataDescriptor<'a> { 140 | let bytes: &'a [u8] = value.as_bytes(); 141 | EventDataDescriptor::for_bytes(bytes) 142 | } 143 | } 144 | 145 | impl<'a> From<&'a U16Str> for EventDataDescriptor<'a> { 146 | fn from(value: &'a U16Str) -> EventDataDescriptor<'a> { 147 | Self { 148 | ptr: value.as_ptr() as usize as u64, 149 | size: (value.len() * 2) as u32, 150 | kind: 0, 151 | phantom_ref: PhantomData, 152 | } 153 | } 154 | } 155 | 156 | impl<'a> From<&'a U16CStr> for EventDataDescriptor<'a> { 157 | fn from(value: &'a U16CStr) -> EventDataDescriptor<'a> { 158 | Self { 159 | ptr: value.as_ptr() as usize as u64, 160 | size: (value.len() * 2) as u32, 161 | kind: 0, 162 | phantom_ref: PhantomData, 163 | } 164 | } 165 | } 166 | 167 | impl<'a> From<&'a GUID> for EventDataDescriptor<'a> { 168 | fn from(value: &'a GUID) -> EventDataDescriptor<'a> { 169 | Self { 170 | ptr: value as *const GUID as usize as u64, 171 | size: size_of::() as u32, 172 | kind: 0, 173 | phantom_ref: PhantomData, 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /win_etw_provider/src/guid.rs: -------------------------------------------------------------------------------- 1 | use zerocopy::{FromBytes, IntoBytes}; 2 | 3 | /// Initializes a `GUID` from literal values. 4 | #[macro_export] 5 | macro_rules! guid { 6 | ( 7 | $a:expr, 8 | $b:expr, 9 | $c:expr, 10 | $d:expr 11 | ) => { 12 | $crate::GUID { 13 | data1: $a, 14 | data2: $b, 15 | data3: $c, 16 | data4: $d, 17 | } 18 | }; 19 | 20 | ( 21 | $a:expr, 22 | $b:expr, 23 | $c:expr, 24 | $d0:expr, 25 | $d1:expr, 26 | $d2:expr, 27 | $d3:expr, 28 | $d4:expr, 29 | $d5:expr, 30 | $d6:expr, 31 | $d7:expr 32 | ) => { 33 | $crate::GUID { 34 | data1: $a, 35 | data2: $b, 36 | data3: $c, 37 | data4: [$d0, $d1, $d2, $d3, $d4, $d5, $d6, $d7], 38 | } 39 | }; 40 | } 41 | 42 | /// The Windows [`GUID`](https://docs.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid) 43 | /// type. 44 | /// 45 | /// `win_etw_provider` defines this type, rather than directly referencing (or re-exporting) 46 | /// an equivalent type from other crates in order to minimize its dependencies. `GUID` has a well- 47 | /// defined byte representation, so converting between different implementations of `GUID` is 48 | /// not a problem. 49 | #[repr(C)] 50 | #[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, IntoBytes, FromBytes)] 51 | pub struct GUID { 52 | /// Contains bytes 0-3 (inclusive) of the GUID. 53 | pub data1: u32, 54 | /// Contains bytes 4-5 (inclusive) of the GUID. 55 | pub data2: u16, 56 | /// Contains bytes 6-7 (inclusive) of the GUID. 57 | pub data3: u16, 58 | /// Contains bytes 8-15 (inclusive) of the GUID. 59 | pub data4: [u8; 8], 60 | } 61 | 62 | impl core::fmt::Display for GUID { 63 | fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 64 | write!( 65 | fmt, 66 | "{:08x}-{:04x}-{:04x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", 67 | self.data1, 68 | self.data2, 69 | self.data3, 70 | self.data4[0], 71 | self.data4[1], 72 | self.data4[2], 73 | self.data4[3], 74 | self.data4[4], 75 | self.data4[5], 76 | self.data4[6], 77 | self.data4[7] 78 | ) 79 | } 80 | } 81 | 82 | impl core::fmt::Debug for GUID { 83 | fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 84 | core::fmt::Display::fmt(self, fmt) 85 | } 86 | } 87 | 88 | #[cfg(target_os = "windows")] 89 | impl From for GUID { 90 | fn from(value: winapi::shared::guiddef::GUID) -> Self { 91 | Self { 92 | data1: value.Data1, 93 | data2: value.Data2, 94 | data3: value.Data3, 95 | data4: value.Data4, 96 | } 97 | } 98 | } 99 | 100 | #[cfg(feature = "uuid")] 101 | impl From for GUID { 102 | fn from(value: uuid::Uuid) -> Self { 103 | let fields = value.as_fields(); 104 | Self { 105 | data1: fields.0, 106 | data2: fields.1, 107 | data3: fields.2, 108 | data4: *fields.3, 109 | } 110 | } 111 | } 112 | 113 | #[cfg(feature = "uuid")] 114 | #[cfg(test)] 115 | mod test { 116 | use crate::guid::GUID; 117 | use uuid::Uuid; 118 | #[test] 119 | fn test_uuid() { 120 | let uuid = Uuid::parse_str("1a1a1a1a-2b2b-3c3c-4142-434546474849").unwrap(); 121 | let guid: GUID = uuid.into(); 122 | assert_eq!(guid.data1, 0x1a1a_1a1a); 123 | assert_eq!(guid.data2, 0x2b2b); 124 | assert_eq!(guid.data3, 0x3c3c); 125 | assert_eq!(guid.data4, [0x41, 0x42, 0x43, 0x45, 0x46, 0x47, 0x48, 0x49]); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /win_etw_provider/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Enables Rust apps to report events using Event Tracing for Windows. 2 | //! 3 | //! See [About Event Tracing](https://docs.microsoft.com/en-us/windows/win32/etw/about-event-tracing). 4 | 5 | #![deny(missing_docs)] 6 | #![cfg_attr(all(not(test), not(feature = "std")), no_std)] 7 | #![cfg_attr(not(windows), allow(unused))] 8 | 9 | extern crate alloc; 10 | 11 | mod guid; 12 | mod provider; 13 | 14 | pub mod types; 15 | 16 | #[doc(inline)] 17 | pub use guid::GUID; 18 | 19 | #[doc(inline)] 20 | pub use provider::*; 21 | 22 | #[doc(hidden)] 23 | pub use types::*; 24 | 25 | #[doc(inline)] 26 | pub use types::{SocketAddrV4, SocketAddrV6, FILETIME}; 27 | 28 | #[doc(hidden)] 29 | pub use win_etw_metadata as metadata; 30 | 31 | mod data_descriptor; 32 | 33 | #[doc(inline)] 34 | pub use data_descriptor::EventDataDescriptor; 35 | 36 | /// Errors returned by `win_etw_provider` functions. 37 | /// 38 | /// When compiling for non-Windows platforms, this Error type becomes an uninhabited type. 39 | #[derive(Clone, PartialEq, Eq, Debug)] 40 | #[non_exhaustive] 41 | pub enum Error { 42 | /// A Windows (Win32) error code. 43 | #[cfg(target_os = "windows")] 44 | WindowsError(u32), 45 | 46 | /// The operation is not supported on this platform. 47 | /// 48 | /// Most operations defined in this crate do nothing on non-Windows platforms. Those operations 49 | /// that return information, such as the `new_activity_id()` function, use this error value. 50 | NotSupported, 51 | } 52 | 53 | /// Allows an application to override the parameters for an event. The first parameter of each 54 | /// generated event method is `options: Option<&EventOptions>`. 55 | #[derive(Default)] 56 | pub struct EventOptions { 57 | /// Overrides the level of the event, if present. Each event method has a default, which can be 58 | /// specified using (for example) `#[event(level = "warn")]`. If the event declaration does not 59 | /// specify a level, then the level will be `Level::VERBOSE`. 60 | pub level: Option, 61 | 62 | /// Specifies the activity ID of this event. 63 | pub activity_id: Option, 64 | 65 | /// Specifies a related activity ID for this event. This enables an application to indicate 66 | /// that two sets of events are related, by associating the activity IDs of the two sets. 67 | /// This is sometimes known as _event correlation_. 68 | pub related_activity_id: Option, 69 | } 70 | 71 | pub use win_etw_metadata::Level; 72 | -------------------------------------------------------------------------------- /win_etw_provider/src/provider.rs: -------------------------------------------------------------------------------- 1 | use crate::guid::GUID; 2 | use crate::Level; 3 | use crate::{Error, EventDataDescriptor}; 4 | use alloc::boxed::Box; 5 | use core::convert::TryFrom; 6 | use core::pin::Pin; 7 | use core::ptr::null; 8 | use core::sync::atomic::{AtomicU8, Ordering::SeqCst}; 9 | 10 | #[cfg(target_os = "windows")] 11 | use win_support::*; 12 | 13 | /// Generates a new activity ID. 14 | /// 15 | /// This function is only implemented on Windows. On other platforms, it will always return `Err`. 16 | pub fn new_activity_id() -> Result { 17 | #[cfg(target_os = "windows")] 18 | { 19 | win_support::new_activity_id() 20 | } 21 | 22 | #[cfg(not(target_os = "windows"))] 23 | { 24 | Err(Error::NotSupported) 25 | } 26 | } 27 | 28 | /// Describes the functions needed for an event provider backend. This is an implementation 29 | /// detail, and should not be used directly by applications. 30 | pub trait Provider { 31 | /// Writes one event. 32 | fn write( 33 | &self, 34 | options: Option<&crate::EventOptions>, 35 | descriptor: &EventDescriptor, 36 | data: &[EventDataDescriptor<'_>], 37 | ); 38 | 39 | /// Checks whether the event provider is enabled. 40 | fn is_enabled(&self, level: u8, keyword: u64) -> bool; 41 | 42 | /// Checks whether a specific event is enabled. 43 | fn is_event_enabled(&self, event_descriptor: &EventDescriptor) -> bool; 44 | } 45 | 46 | /// Implements `Provider` by discarding all events. 47 | pub struct NullProvider; 48 | 49 | impl Provider for NullProvider { 50 | fn write( 51 | &self, 52 | _options: Option<&crate::EventOptions>, 53 | _descriptor: &EventDescriptor, 54 | _data: &[EventDataDescriptor<'_>], 55 | ) { 56 | } 57 | 58 | fn is_enabled(&self, _level: u8, _keyword: u64) -> bool { 59 | false 60 | } 61 | fn is_event_enabled(&self, _event_descriptor: &EventDescriptor) -> bool { 62 | false 63 | } 64 | } 65 | 66 | impl Provider for Option { 67 | fn write( 68 | &self, 69 | options: Option<&crate::EventOptions>, 70 | descriptor: &EventDescriptor, 71 | data: &[EventDataDescriptor<'_>], 72 | ) { 73 | match self { 74 | Some(p) => p.write(options, descriptor, data), 75 | None => {} 76 | } 77 | } 78 | 79 | fn is_enabled(&self, level: u8, keyword: u64) -> bool { 80 | match self { 81 | Some(p) => p.is_enabled(level, keyword), 82 | None => false, 83 | } 84 | } 85 | fn is_event_enabled(&self, event_descriptor: &EventDescriptor) -> bool { 86 | match self { 87 | Some(p) => p.is_event_enabled(event_descriptor), 88 | None => false, 89 | } 90 | } 91 | } 92 | 93 | /// Implements `Provider` by registering with ETW. 94 | pub struct EtwProvider { 95 | #[cfg(target_os = "windows")] 96 | handle: evntprov::REGHANDLE, 97 | 98 | #[cfg(target_os = "windows")] 99 | // #[allow(dead_code)] // Needed for lifetime control 100 | stable: Pin>, 101 | } 102 | 103 | impl Provider for EtwProvider { 104 | #[inline(always)] 105 | fn write( 106 | &self, 107 | options: Option<&crate::EventOptions>, 108 | descriptor: &EventDescriptor, 109 | data: &[EventDataDescriptor<'_>], 110 | ) { 111 | #[cfg(target_os = "windows")] 112 | { 113 | unsafe { 114 | let mut activity_id_ptr = null(); 115 | let mut related_activity_id_ptr = null(); 116 | 117 | let mut event_descriptor = evntprov::EVENT_DESCRIPTOR { 118 | Id: descriptor.id, 119 | Version: descriptor.version, 120 | Channel: descriptor.channel, 121 | Level: descriptor.level.0, 122 | Opcode: descriptor.opcode, 123 | Task: descriptor.task, 124 | Keyword: descriptor.keyword, 125 | }; 126 | 127 | if let Some(options) = options { 128 | if let Some(id) = options.activity_id.as_ref() { 129 | activity_id_ptr = id as *const GUID as *const winapi::shared::guiddef::GUID; 130 | } 131 | if let Some(id) = options.related_activity_id.as_ref() { 132 | related_activity_id_ptr = 133 | id as *const GUID as *const winapi::shared::guiddef::GUID; 134 | } 135 | if let Some(level) = options.level { 136 | event_descriptor.Level = level.0; 137 | } 138 | } 139 | 140 | let error = evntprov::EventWriteEx( 141 | self.handle, 142 | &event_descriptor, 143 | 0, // filter 144 | 0, // flags 145 | activity_id_ptr, // activity id 146 | related_activity_id_ptr, // related activity id 147 | data.len() as u32, 148 | data.as_ptr() as *const evntprov::EVENT_DATA_DESCRIPTOR 149 | as *mut evntprov::EVENT_DATA_DESCRIPTOR, 150 | ); 151 | if error != 0 { 152 | write_failed(error) 153 | } 154 | } 155 | } 156 | } 157 | 158 | // write_ex 159 | // write_transfer 160 | 161 | fn is_enabled(&self, level: u8, keyword: u64) -> bool { 162 | #[cfg(target_os = "windows")] 163 | { 164 | unsafe { evntprov::EventProviderEnabled(self.handle, level, keyword) != 0 } 165 | } 166 | #[cfg(not(target_os = "windows"))] 167 | { 168 | false 169 | } 170 | } 171 | 172 | fn is_event_enabled(&self, event_descriptor: &EventDescriptor) -> bool { 173 | #[cfg(target_os = "windows")] 174 | { 175 | if false { 176 | unsafe { 177 | evntprov::EventEnabled( 178 | self.handle, 179 | event_descriptor as *const _ as *const evntprov::EVENT_DESCRIPTOR, 180 | ) != 0 181 | } 182 | } else { 183 | let max_level = self.stable.as_ref().max_level.load(SeqCst); 184 | event_descriptor.level.0 <= max_level 185 | } 186 | } 187 | #[cfg(not(target_os = "windows"))] 188 | { 189 | false 190 | } 191 | } 192 | } 193 | 194 | #[inline(never)] 195 | fn write_failed(_error: u32) { 196 | #[cfg(feature = "dev")] 197 | { 198 | eprintln!("EventWrite failed: {}", _error); 199 | } 200 | } 201 | 202 | #[cfg(target_os = "windows")] 203 | mod win_support { 204 | pub use winapi::shared::evntprov; 205 | pub use winapi::shared::evntrace; 206 | pub use winapi::shared::winerror; 207 | 208 | use super::*; 209 | 210 | /// This data is stored in a Box, so that it has a stable address. 211 | /// It is used to coordinate with ETW; ETW runs callbacks that need a stable pointer. 212 | /// See `EventRegister` and the "enable callback". 213 | pub(crate) struct StableProviderData { 214 | pub(crate) max_level: AtomicU8, 215 | } 216 | 217 | /// See [PENABLECALLBACK](https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nc-evntprov-penablecallback). 218 | pub(crate) unsafe extern "system" fn enable_callback( 219 | _source_id: *const winapi::shared::guiddef::GUID, 220 | is_enabled_code: u32, 221 | level: u8, 222 | _match_any_keyword: u64, 223 | _match_all_keyword: u64, 224 | _filter_data: *mut evntprov::EVENT_FILTER_DESCRIPTOR, 225 | context: *mut winapi::ctypes::c_void, 226 | ) { 227 | // This should never happen. 228 | if context.is_null() { 229 | return; 230 | } 231 | let stable_data: &StableProviderData = &*(context as *const _ as *const StableProviderData); 232 | 233 | let _source_id: GUID = if _source_id.is_null() { 234 | GUID::default() 235 | } else { 236 | (*(_source_id as *const GUID)).clone() 237 | }; 238 | #[cfg(feature = "dev")] 239 | { 240 | eprintln!( 241 | "enable_callback: source_id {} is_enabled {}, level {}, any {:#x} all {:#x} filter? {:?}", 242 | _source_id, is_enabled_code, level, _match_any_keyword, _match_all_keyword, 243 | !_filter_data.is_null() 244 | ); 245 | } 246 | 247 | match is_enabled_code { 248 | evntrace::EVENT_CONTROL_CODE_ENABLE_PROVIDER => { 249 | #[cfg(feature = "dev")] 250 | { 251 | eprintln!("ETW is ENABLING this provider. setting level: {}", level); 252 | } 253 | stable_data.max_level.store(level, SeqCst); 254 | } 255 | evntrace::EVENT_CONTROL_CODE_DISABLE_PROVIDER => { 256 | #[cfg(feature = "dev")] 257 | { 258 | eprintln!("ETW is DISABLING this provider. setting level: {}", level); 259 | } 260 | stable_data.max_level.store(level, SeqCst); 261 | } 262 | evntrace::EVENT_CONTROL_CODE_CAPTURE_STATE => { 263 | // ETW is requesting that the provider log its state information. The meaning of this 264 | // is provider-dependent. Currently, this functionality is not exposed to Rust apps. 265 | #[cfg(feature = "dev")] 266 | { 267 | eprintln!("EVENT_CONTROL_CODE_CAPTURE_STATE"); 268 | } 269 | } 270 | _ => { 271 | // The control code is unrecognized. 272 | #[cfg(feature = "dev")] 273 | { 274 | eprintln!( 275 | "enable_callback: control code {} is not recognized", 276 | is_enabled_code 277 | ); 278 | } 279 | } 280 | } 281 | } 282 | 283 | pub fn new_activity_id() -> Result { 284 | unsafe { 285 | let mut guid: winapi::shared::guiddef::GUID = core::mem::zeroed(); 286 | let error = evntprov::EventActivityIdControl( 287 | evntprov::EVENT_ACTIVITY_CTRL_CREATE_ID, 288 | &mut guid, 289 | ); 290 | if error == 0 { 291 | Ok(guid.into()) 292 | } else { 293 | Err(Error::WindowsError(error)) 294 | } 295 | } 296 | } 297 | } 298 | 299 | impl EtwProvider { 300 | /// Registers an event provider with ETW. 301 | /// 302 | /// The implementation uses `[EventWriteEx](https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwriteex)`. 303 | pub fn new(provider_id: &GUID) -> Result { 304 | #[cfg(target_os = "windows")] 305 | { 306 | unsafe { 307 | let mut stable = Box::pin(StableProviderData { 308 | max_level: AtomicU8::new(0), 309 | }); 310 | let mut handle: evntprov::REGHANDLE = 0; 311 | let stable_ptr: &mut StableProviderData = &mut stable; 312 | let error = evntprov::EventRegister( 313 | provider_id as *const _ as *const winapi::shared::guiddef::GUID, 314 | Some(enable_callback), 315 | stable_ptr as *mut StableProviderData as *mut winapi::ctypes::c_void, 316 | &mut handle, 317 | ); 318 | if error != 0 { 319 | Err(Error::WindowsError(error)) 320 | } else { 321 | Ok(EtwProvider { handle, stable }) 322 | } 323 | } 324 | } 325 | #[cfg(not(target_os = "windows"))] 326 | { 327 | Ok(EtwProvider {}) 328 | } 329 | } 330 | 331 | /// See TraceLoggingRegisterEx in traceloggingprovider.h. 332 | /// This registers provider metadata. 333 | pub fn register_provider_metadata(&mut self, provider_metadata: &[u8]) -> Result<(), Error> { 334 | #[cfg(target_os = "windows")] 335 | { 336 | unsafe { 337 | let error = evntprov::EventSetInformation( 338 | self.handle, 339 | 2, 340 | provider_metadata.as_ptr() as *mut winapi::ctypes::c_void, 341 | u32::try_from(provider_metadata.len()).unwrap(), 342 | ); 343 | if error != 0 { 344 | Err(Error::WindowsError(error)) 345 | } else { 346 | #[cfg(feature = "dev")] 347 | { 348 | eprintln!("register_provider_metadata: succeeded"); 349 | } 350 | Ok(()) 351 | } 352 | } 353 | } 354 | #[cfg(not(target_os = "windows"))] 355 | { 356 | Ok(()) 357 | } 358 | } 359 | 360 | /// Registers provider traits for a provider. 361 | /// 362 | /// ETW providers should not call this function directly. It is automatically 363 | /// called by the provider code that is generated by `win_etw_macros`. 364 | /// 365 | /// See [Provider Traits](https://docs.microsoft.com/en-us/windows/win32/etw/provider-traits). 366 | pub fn set_provider_traits(&mut self, provider_traits: &[u8]) -> Result<(), Error> { 367 | #[cfg(target_os = "windows")] 368 | { 369 | unsafe { 370 | let error = evntprov::EventSetInformation( 371 | self.handle, 372 | evntprov::EventProviderSetTraits, 373 | provider_traits.as_ptr() as *mut u8 as *mut winapi::ctypes::c_void, 374 | u32::try_from(provider_traits.len()).unwrap(), 375 | ); 376 | if error != 0 { 377 | #[cfg(feature = "dev")] 378 | { 379 | eprintln!("EventSetInformation failed for provider traits"); 380 | } 381 | return Err(Error::WindowsError(error)); 382 | } 383 | } 384 | Ok(()) 385 | } 386 | #[cfg(not(target_os = "windows"))] 387 | { 388 | Ok(()) 389 | } 390 | } 391 | } 392 | 393 | impl Drop for EtwProvider { 394 | fn drop(&mut self) { 395 | #[cfg(target_os = "windows")] 396 | { 397 | unsafe { 398 | evntprov::EventUnregister(self.handle); 399 | } 400 | } 401 | } 402 | } 403 | 404 | unsafe impl Send for EtwProvider {} 405 | unsafe impl Sync for EtwProvider {} 406 | 407 | /// Describes parameters for an event. This is an implementation detail, and should not be directly 408 | /// used by applications. 409 | #[repr(C)] 410 | #[allow(missing_docs)] 411 | pub struct EventDescriptor { 412 | pub id: u16, 413 | pub version: u8, 414 | pub channel: u8, 415 | pub level: Level, 416 | pub opcode: u8, 417 | pub task: u16, 418 | pub keyword: u64, 419 | } 420 | 421 | /// Allows an application to enter a nested activity scope. This creates a new activity ID, 422 | /// sets this activity ID as the current activity ID of the current thread, and then runs the 423 | /// provided function. After the function finishes, it restores the activity ID of the calling 424 | /// thread (even if a panic occurs). 425 | /// 426 | /// See `[EventActivityIdControl](https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventactivityidcontrol)`. 427 | #[inline(always)] 428 | pub fn with_activity R, R>(f: F) -> R { 429 | #[cfg(target_os = "windows")] 430 | { 431 | let mut previous_activity_id: GUID = Default::default(); 432 | 433 | let mut restore = RestoreActivityHolder { 434 | previous_activity_id: None, 435 | }; 436 | 437 | unsafe { 438 | let result = evntprov::EventActivityIdControl( 439 | evntprov::EVENT_ACTIVITY_CTRL_CREATE_SET_ID, 440 | &mut previous_activity_id as *mut _ as *mut winapi::shared::guiddef::GUID, 441 | ); 442 | if result == winerror::ERROR_SUCCESS { 443 | restore.previous_activity_id = Some(previous_activity_id); 444 | } else { 445 | // Failed to create/replace the activity ID. There is not much we can do about this. 446 | } 447 | } 448 | 449 | let result = f(); 450 | // RestoreActivityHolder::drop() will run, even if f() panics, and will restore the 451 | // activity ID of the current thread. 452 | drop(restore); 453 | result 454 | } 455 | 456 | #[cfg(not(target_os = "windows"))] 457 | { 458 | f() 459 | } 460 | } 461 | 462 | struct RestoreActivityHolder { 463 | previous_activity_id: Option, 464 | } 465 | 466 | impl Drop for RestoreActivityHolder { 467 | fn drop(&mut self) { 468 | #[cfg(target_os = "windows")] 469 | { 470 | unsafe { 471 | if let Some(previous_activity_id) = self.previous_activity_id.as_ref() { 472 | evntprov::EventActivityIdControl( 473 | evntprov::EVENT_ACTIVITY_CTRL_SET_ID, 474 | previous_activity_id as *const GUID as *const winapi::shared::guiddef::GUID 475 | as *mut _, 476 | ); 477 | } 478 | } 479 | } 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /win_etw_provider/src/types.rs: -------------------------------------------------------------------------------- 1 | //! Contains items that are part of the implementation of `win_etw`, but not intended to be used 2 | //! directly by application code. Only code generated by the `trace_logging_provider` macro 3 | //! should use these types. 4 | #![doc(hidden)] 5 | 6 | pub use widestring::{U16CStr, U16CString}; 7 | 8 | use crate::EventDataDescriptor; 9 | use zerocopy::{FromBytes, Immutable, IntoBytes}; 10 | 11 | /// The value used in `SocketAddrV4::family` to identify IPv4 addresses. 12 | pub const AF_INET: u16 = 2; 13 | 14 | /// The value used in `SocketAddrV6::family` to identify IPv6 addresses. 15 | pub const AF_INET6: u16 = 23; 16 | 17 | /// This has the same in-memory representation as the Win32 `[SOCKADDR_IN]` structure. 18 | /// 19 | /// [SOCKADDR_IN]: https://docs.microsoft.com/en-us/windows/win32/api/ws2def/ns-ws2def-sockaddr_in 20 | #[repr(C)] 21 | #[derive(IntoBytes, Immutable, Clone)] 22 | pub struct SocketAddrV4 { 23 | /// Address family identifier. 24 | pub family: u16, 25 | /// Port identifier, stored in big-endian form. 26 | pub port: [u8; 2], 27 | /// IPv4 address, stored in big-endian form. 28 | pub address: [u8; 4], 29 | /// Zero padding. 30 | pub zero: [u8; 8], 31 | } 32 | 33 | impl From<&core::net::SocketAddrV4> for SocketAddrV4 { 34 | fn from(value: &core::net::SocketAddrV4) -> Self { 35 | let port = value.port(); 36 | Self { 37 | family: AF_INET, 38 | address: value.ip().octets(), 39 | port: port.to_be_bytes(), 40 | zero: [0; 8], 41 | } 42 | } 43 | } 44 | 45 | impl<'a> From<&'a crate::types::SocketAddrV4> for EventDataDescriptor<'a> { 46 | fn from(value: &'a crate::types::SocketAddrV4) -> EventDataDescriptor<'a> { 47 | Self::from(value.as_bytes()) 48 | } 49 | } 50 | 51 | /// See `[SOCKADDR_IN6_LH](https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-sockaddr_in6_lh)`. 52 | #[repr(C)] 53 | #[derive(Clone, IntoBytes, FromBytes, Immutable)] 54 | pub struct SocketAddrV6 { 55 | /// Address family identifier. 56 | pub family: u16, 57 | /// Port identifier, stored in big-endian form. 58 | pub port: [u8; 2], 59 | /// IPv6 flow info. 60 | pub flow_info: [u8; 4], 61 | /// IPv6 address. 62 | pub address: [u8; 16], 63 | /// IPv6 scope. 64 | pub scope_id: [u8; 4], 65 | } 66 | 67 | impl From<&core::net::SocketAddrV6> for SocketAddrV6 { 68 | fn from(value: &core::net::SocketAddrV6) -> Self { 69 | Self { 70 | family: AF_INET6, 71 | port: value.port().to_be_bytes(), 72 | flow_info: value.flowinfo().to_be_bytes(), 73 | address: value.ip().octets(), 74 | scope_id: value.scope_id().to_be_bytes(), 75 | } 76 | } 77 | } 78 | 79 | impl<'a> From<&'a crate::types::SocketAddrV6> for EventDataDescriptor<'a> { 80 | fn from(value: &'a crate::types::SocketAddrV6) -> EventDataDescriptor<'a> { 81 | Self::from(value.as_bytes()) 82 | } 83 | } 84 | 85 | /// See `[FILETIME](https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime)`. 86 | #[repr(transparent)] 87 | #[derive(Copy, Clone, Eq, PartialEq, Hash)] 88 | pub struct FILETIME(pub u64); 89 | 90 | #[cfg(feature = "std")] 91 | mod std_support { 92 | use super::*; 93 | 94 | use core::convert::TryFrom; 95 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; 96 | 97 | /// Time elapsed between the Windows epoch and the UNIX epoch. 98 | const WINDOWS_EPOCH_TO_UNIX_EPOCH: Duration = Duration::from_secs(11_644_473_600); 99 | 100 | pub struct OutOfRangeError; 101 | 102 | impl TryFrom for FILETIME { 103 | type Error = OutOfRangeError; 104 | fn try_from(t: SystemTime) -> Result { 105 | match t.duration_since(UNIX_EPOCH) { 106 | Ok(unix_elapsed) => { 107 | let windows_elapsed: Duration = unix_elapsed + WINDOWS_EPOCH_TO_UNIX_EPOCH; 108 | Ok(FILETIME((windows_elapsed.as_nanos() / 100) as u64)) 109 | } 110 | Err(_) => Err(OutOfRangeError), 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /win_etw_tracing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "win_etw_tracing" 3 | version = "0.1.3" 4 | edition = "2021" 5 | license = "Apache-2.0 OR MIT" 6 | description = "Provides a backend for the `tracing` crate that logs events to ETW (Event Tracing for Windows)." 7 | repository = "https://github.com/microsoft/rust_win_etw" 8 | readme = "../README.md" 9 | 10 | [features] 11 | default = ["tracing-log"] 12 | 13 | [dependencies] 14 | bytes = "1" 15 | tracing = "0.1" 16 | tracing-log = { version = "0.2", optional = true, default-features = false, features = ["log-tracer", "std"] } 17 | tracing-subscriber = { version = "0.3.7", default-features = false, features = ["smallvec", "fmt", "std"] } 18 | win_etw_metadata = { path = "../win_etw_metadata", version = "0.1.2" } 19 | win_etw_provider = { path = "../win_etw_provider", version = "0.1.9" } 20 | 21 | [dev-dependencies] 22 | anyhow = "1" 23 | -------------------------------------------------------------------------------- /win_etw_tracing/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) Microsoft Corporation. All rights reserved. 2 | 3 | //! Subscriber for tracing events that emits Windows ETW tracelogging events. 4 | #![cfg(windows)] 5 | #![forbid(unsafe_code)] 6 | 7 | use bytes::BufMut; 8 | use core::fmt; 9 | use std::io::Write; 10 | use tracing::field::Field; 11 | use tracing::field::Visit; 12 | use tracing::span::Attributes; 13 | use tracing::span::Record; 14 | use tracing::Event; 15 | use tracing::Id; 16 | use tracing::Metadata; 17 | use tracing::Subscriber; 18 | use tracing_subscriber::layer::Context; 19 | use tracing_subscriber::registry::LookupSpan; 20 | use tracing_subscriber::Layer; 21 | use win_etw_metadata::InFlag; 22 | use win_etw_metadata::OutFlag; 23 | use win_etw_provider::Error; 24 | use win_etw_provider::EtwProvider; 25 | use win_etw_provider::EventDataDescriptor; 26 | use win_etw_provider::EventDescriptor; 27 | use win_etw_provider::EventOptions; 28 | use win_etw_provider::Provider; 29 | use win_etw_provider::GUID; 30 | 31 | /// An implementation for [`tracing_subscriber::Layer`] that emits tracelogging 32 | /// events. 33 | pub struct TracelogSubscriber { 34 | provider: EtwProvider, 35 | keyword_mask: u64, 36 | global_fields: EventData, 37 | } 38 | 39 | impl TracelogSubscriber { 40 | /// Creates a new subscriber with provider ID `id` and provider name `name`. 41 | pub fn new(id: impl Into, name: &str) -> Result { 42 | let mut provider_metadata = Vec::new(); 43 | provider_metadata.put_u16_le( 44 | (2 + name.len() + 1) 45 | .try_into() 46 | .expect("provider name too long"), 47 | ); 48 | provider_metadata.put_slice(name.as_bytes()); 49 | provider_metadata.put_u8(0); 50 | 51 | let mut provider = EtwProvider::new(&id.into())?; 52 | provider.register_provider_metadata(provider_metadata.as_slice())?; 53 | Ok(Self { 54 | provider, 55 | keyword_mask: !0_u64, 56 | global_fields: EventData { 57 | metadata: Vec::new(), 58 | data: Vec::new(), 59 | }, 60 | }) 61 | } 62 | 63 | // If some events are by default marked with telemetry keywords, this allows an opt out. 64 | pub fn enable_telemetry_events(&mut self, enabled: bool) { 65 | self.keyword_mask = if enabled { 66 | !0_u64 67 | } else { 68 | !(win_etw_metadata::MICROSOFT_KEYWORD_CRITICAL_DATA 69 | | win_etw_metadata::MICROSOFT_KEYWORD_MEASURES 70 | | win_etw_metadata::MICROSOFT_KEYWORD_TELEMETRY) 71 | }; 72 | } 73 | 74 | pub fn filter_keyword(&self, keyword: u64) -> u64 { 75 | keyword & self.keyword_mask 76 | } 77 | 78 | /// Global fields are automatically included in all events emitted by this 79 | /// layer. They can be set at the time of layer creation, or by using 80 | /// [`tracing_subscriber::reload`] to dynamically reconfigure a registered 81 | /// layer. Note that if the subscriber is registered as the [global 82 | /// default](tracing::dispatcher#setting-the-default-subscriber), thesee 83 | /// fields will be global to the entire process. 84 | /// 85 | /// # Example 86 | /// ``` 87 | /// # use win_etw_tracing::TracelogSubscriber; 88 | /// # use win_etw_provider::GUID; 89 | /// # let provider_guid = GUID { 90 | /// # data1: 0xe1c71d95, 91 | /// # data2: 0x7bbc, 92 | /// # data3: 0x5f48, 93 | /// # data4: [0xa9, 0x2b, 0x8a, 0xaa, 0x0b, 0x52, 0x91, 0x58], 94 | /// # }; 95 | /// let mut layer = TracelogSubscriber::new(provider_guid, "provider_name").unwrap(); 96 | /// let globals = vec![("field name", "my value")]; 97 | /// layer.set_global_fields(&globals); 98 | /// ``` 99 | pub fn set_global_fields(&mut self, fields: &[(&str, &str)]) { 100 | self.global_fields.metadata.clear(); 101 | self.global_fields.data.clear(); 102 | for &(name, value) in fields.iter() { 103 | self.global_fields.record_global(name, value); 104 | } 105 | } 106 | } 107 | 108 | impl TracelogSubscriber { 109 | fn write_event( 110 | &self, 111 | opcode: u8, 112 | options: &EventOptions, 113 | write_target: bool, 114 | meta: &Metadata<'_>, 115 | write_name: impl FnOnce(&mut Vec), 116 | record: impl FnOnce(&mut dyn Visit), 117 | ) { 118 | let level = match *meta.level() { 119 | tracing::Level::ERROR => win_etw_metadata::Level::ERROR, 120 | tracing::Level::WARN => win_etw_metadata::Level::WARN, 121 | tracing::Level::INFO => win_etw_metadata::Level::INFO, 122 | tracing::Level::DEBUG | tracing::Level::TRACE => win_etw_metadata::Level::VERBOSE, 123 | }; 124 | 125 | let event_descriptor = EventDescriptor { 126 | id: 0, 127 | version: 0, 128 | channel: 11, // this value tells older versions of ETW that this is a tracelogging event 129 | level, 130 | opcode, 131 | task: 0, 132 | keyword: self.filter_keyword(0), 133 | }; 134 | 135 | if !self.provider.is_event_enabled(&event_descriptor) { 136 | return; 137 | } 138 | 139 | let mut event_data = EventData { 140 | metadata: Vec::new(), 141 | data: Vec::new(), 142 | }; 143 | event_data.metadata.put_u16_le(0); // reserve space for the size 144 | event_data.metadata.put_u8(0); // no extensions 145 | write_name(&mut event_data.metadata); 146 | event_data.metadata.put_u8(0); // null terminator 147 | 148 | let target_len = if write_target { 149 | // Target field 150 | event_data.metadata.put_slice(b"target\0"); 151 | event_data 152 | .metadata 153 | .put_u8((InFlag::COUNTED_ANSI_STRING | InFlag::CHAIN_FLAG).bits()); 154 | event_data.metadata.put_u8(OutFlag::UTF8.bits()); 155 | meta.target().len() as u16 156 | } else { 157 | 0 158 | }; 159 | 160 | event_data 161 | .metadata 162 | .put_slice(self.global_fields.metadata.as_slice()); 163 | event_data 164 | .data 165 | .put_slice(self.global_fields.data.as_slice()); 166 | record(&mut event_data); 167 | 168 | // Update the length. 169 | let event_metadata_len = event_data.metadata.len() as u16; 170 | (&mut event_data.metadata[0..2]).put_u16_le(event_metadata_len); 171 | 172 | // N.B. Since we pre-registered the provider information when creating 173 | // the provider, there is no need to log it again here. 174 | let (data_descriptors_with_target, data_descriptors_without_target); 175 | let data_descriptors = if write_target { 176 | data_descriptors_with_target = [ 177 | EventDataDescriptor::for_event_metadata(event_data.metadata.as_slice()), 178 | EventDataDescriptor::from(&target_len), 179 | EventDataDescriptor::from(meta.target()), 180 | EventDataDescriptor::for_bytes(&event_data.data), 181 | ]; 182 | &data_descriptors_with_target[..] 183 | } else { 184 | data_descriptors_without_target = [ 185 | EventDataDescriptor::for_event_metadata(event_data.metadata.as_slice()), 186 | EventDataDescriptor::for_bytes(&event_data.data), 187 | ]; 188 | &data_descriptors_without_target[..] 189 | }; 190 | self.provider 191 | .write(Some(options), &event_descriptor, data_descriptors); 192 | } 193 | } 194 | 195 | #[derive(Debug, Clone, Default)] 196 | struct ActivityId(GUID); 197 | 198 | impl ActivityId { 199 | fn new() -> Result { 200 | Ok(Self(win_etw_provider::new_activity_id()?)) 201 | } 202 | } 203 | 204 | const WINEVENT_OPCODE_INFO: u8 = 0; 205 | const WINEVENT_OPCODE_START: u8 = 1; 206 | const WINEVENT_OPCODE_STOP: u8 = 2; 207 | 208 | impl Layer for TracelogSubscriber 209 | where 210 | S: for<'a> LookupSpan<'a>, 211 | { 212 | fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { 213 | let activity_id = ActivityId::new().unwrap_or_default(); 214 | 215 | let related_activity_id = { 216 | if attrs.is_contextual() { 217 | ctx.current_span().id().cloned() 218 | } else { 219 | attrs.parent().cloned() 220 | } 221 | .and_then(|id| { 222 | ctx.span(&id) 223 | .unwrap() 224 | .extensions() 225 | .get::() 226 | .cloned() 227 | }) 228 | .map(|x| x.0) 229 | }; 230 | 231 | // Store the activity ID on the span to look up later. 232 | ctx.span(id) 233 | .unwrap() 234 | .extensions_mut() 235 | .insert(activity_id.clone()); 236 | 237 | self.write_event( 238 | WINEVENT_OPCODE_START, 239 | &EventOptions { 240 | activity_id: Some(activity_id.0), 241 | related_activity_id, 242 | ..Default::default() 243 | }, 244 | true, 245 | attrs.metadata(), 246 | |metadata| metadata.extend(attrs.metadata().name().as_bytes()), 247 | |visit| attrs.record(visit), 248 | ); 249 | } 250 | 251 | fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { 252 | // Defer the recorded value until on_close is called. Ideally we would 253 | // just log the additional data as another event and the data would be 254 | // aggregated with the rest of the activity's data, but WPA and other 255 | // analysis tools don't actually handle this. 256 | let span = ctx.span(id).unwrap(); 257 | let mut extensions = span.extensions_mut(); 258 | let deferred = if let Some(deferred) = extensions.get_mut::() { 259 | deferred 260 | } else { 261 | extensions.insert(DeferredValues::default()); 262 | extensions.get_mut().unwrap() 263 | }; 264 | values.record(deferred); 265 | } 266 | 267 | fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { 268 | #[cfg(feature = "tracing-log")] 269 | let normalized_meta = tracing_log::NormalizeEvent::normalized_metadata(event); 270 | #[cfg(feature = "tracing-log")] 271 | let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); 272 | #[cfg(not(feature = "tracing-log"))] 273 | let meta = event.metadata(); 274 | 275 | let activity_id = ctx 276 | .event_span(event) 277 | .and_then(|span| span.extensions().get::().cloned().map(|x| x.0)); 278 | 279 | self.write_event( 280 | WINEVENT_OPCODE_INFO, 281 | &EventOptions { 282 | activity_id, 283 | ..Default::default() 284 | }, 285 | true, 286 | meta, 287 | // Write the message as the event name. This will not be ideal for 288 | // events with dynamic names, but it should work well for structured 289 | // events, and it follows the precedent set by the tracing-opentelemetry 290 | // crate. 291 | |metadata| event.record(&mut EventName(metadata)), 292 | |visit| event.record(visit), 293 | ); 294 | } 295 | 296 | fn on_close(&self, id: Id, ctx: Context<'_, S>) { 297 | let span = ctx.span(&id).unwrap(); 298 | let extensions = span.extensions(); 299 | let ActivityId(activity_id) = extensions.get::().cloned().unwrap(); 300 | let values = extensions.get::(); 301 | self.write_event( 302 | WINEVENT_OPCODE_STOP, 303 | &EventOptions { 304 | activity_id: Some(activity_id), 305 | ..Default::default() 306 | }, 307 | false, 308 | span.metadata(), 309 | |metadata| metadata.extend(span.metadata().name().as_bytes()), 310 | |visit| { 311 | if let Some(values) = values { 312 | values.record(visit) 313 | }; 314 | }, 315 | ); 316 | } 317 | } 318 | 319 | /// Collection of deferred values to log when the span is closed. 320 | #[derive(Default)] 321 | struct DeferredValues { 322 | values: Vec<(Field, DeferredValue)>, 323 | } 324 | 325 | impl DeferredValues { 326 | fn update(&mut self, field: &Field, value: DeferredValue) { 327 | for (f, v) in &mut self.values { 328 | if f == field { 329 | *v = value; 330 | return; 331 | } 332 | } 333 | self.values.push((field.clone(), value)); 334 | } 335 | 336 | fn record(&self, visit: &mut dyn Visit) { 337 | for (field, v) in &self.values { 338 | match v { 339 | DeferredValue::Unsigned(v) => visit.record_u64(field, *v), 340 | DeferredValue::Signed(v) => visit.record_i64(field, *v), 341 | DeferredValue::Boolean(v) => visit.record_bool(field, *v), 342 | DeferredValue::String(v) => visit.record_str(field, v), 343 | } 344 | } 345 | } 346 | } 347 | 348 | impl Visit for DeferredValues { 349 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { 350 | self.update(field, DeferredValue::String(format!("{:?}", value))); 351 | } 352 | 353 | fn record_i64(&mut self, field: &Field, value: i64) { 354 | self.update(field, DeferredValue::Signed(value)); 355 | } 356 | 357 | fn record_u64(&mut self, field: &Field, value: u64) { 358 | self.update(field, DeferredValue::Unsigned(value)); 359 | } 360 | 361 | fn record_bool(&mut self, field: &Field, value: bool) { 362 | self.update(field, DeferredValue::Boolean(value)); 363 | } 364 | 365 | fn record_str(&mut self, field: &Field, value: &str) { 366 | self.update(field, DeferredValue::String(value.to_string())); 367 | } 368 | } 369 | 370 | enum DeferredValue { 371 | Unsigned(u64), 372 | Signed(i64), 373 | Boolean(bool), 374 | String(String), 375 | } 376 | 377 | struct EventName<'a>(&'a mut Vec); 378 | 379 | impl Visit for EventName<'_> { 380 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { 381 | if field.name() == "message" { 382 | let _ = write!(self.0, "{:?}", value); 383 | } 384 | } 385 | } 386 | 387 | struct EventData { 388 | metadata: Vec, 389 | data: Vec, 390 | } 391 | 392 | impl EventData { 393 | fn write_name(&mut self, name: &str) -> bool { 394 | // Skip the message (used as the event name) as well as any log crate 395 | // metadata (already consumed). 396 | if name == "message" || (cfg!(feature = "tracing-log") && name.starts_with("log.")) { 397 | return false; 398 | } 399 | self.metadata.put_slice(name.as_bytes()); 400 | self.metadata.put_u8(0); // null terminator 401 | true 402 | } 403 | 404 | fn record_global(&mut self, name: &str, value: &str) { 405 | if self.write_name(name) { 406 | self.metadata 407 | .put_u8((InFlag::ANSI_STRING | InFlag::CHAIN_FLAG).bits()); 408 | self.metadata.put_u8(OutFlag::UTF8.bits()); 409 | self.data.extend(value.as_bytes()); 410 | self.data.put_u8(0); // null terminator 411 | } 412 | } 413 | } 414 | 415 | impl Visit for EventData { 416 | fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { 417 | if self.write_name(field.name()) { 418 | self.metadata 419 | .put_u8((InFlag::ANSI_STRING | InFlag::CHAIN_FLAG).bits()); 420 | self.metadata.put_u8(OutFlag::UTF8.bits()); 421 | let _ = write!(&mut self.data, "{:?}\0", value); 422 | } 423 | } 424 | 425 | fn record_i64(&mut self, field: &Field, value: i64) { 426 | if self.write_name(field.name()) { 427 | self.metadata.put_u8(InFlag::INT64.bits()); 428 | self.data.put_i64_le(value); 429 | } 430 | } 431 | 432 | fn record_u64(&mut self, field: &Field, value: u64) { 433 | if self.write_name(field.name()) { 434 | self.metadata 435 | .put_u8((InFlag::UINT64 | InFlag::CHAIN_FLAG).bits()); 436 | self.metadata.put_u8(OutFlag::HEX.bits()); 437 | self.data.put_u64_le(value); 438 | } 439 | } 440 | 441 | fn record_bool(&mut self, field: &Field, value: bool) { 442 | if self.write_name(field.name()) { 443 | // Ideally we would use InFlag::UINT8 and the OutFlag::BOOLEAN, but 444 | // WPA and other tools seem to ignore this. Use the four-byte input 445 | // encoding instead. 446 | self.metadata.put_u8(InFlag::BOOL32.bits()); 447 | self.data.put_u32_le(value.into()); 448 | } 449 | } 450 | 451 | fn record_str(&mut self, field: &Field, value: &str) { 452 | if self.write_name(field.name()) { 453 | self.metadata 454 | .put_u8((InFlag::ANSI_STRING | InFlag::CHAIN_FLAG).bits()); 455 | self.metadata.put_u8(OutFlag::UTF8.bits()); 456 | self.data.extend(value.as_bytes()); 457 | self.data.put_u8(0); // null terminator 458 | } 459 | } 460 | 461 | fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { 462 | if self.write_name(field.name()) { 463 | self.metadata 464 | .put_u8((InFlag::ANSI_STRING | InFlag::CHAIN_FLAG).bits()); 465 | self.metadata.put_u8(OutFlag::UTF8.bits()); 466 | let _ = write!(&mut self.data, "{}", value); 467 | let mut source = value.source(); 468 | while let Some(v) = source.take() { 469 | let _ = write!(&mut self.data, ": {}", v); 470 | source = v.source(); 471 | } 472 | self.data.put_u8(0); // null terminator 473 | } 474 | } 475 | } 476 | 477 | #[cfg(test)] 478 | mod tests { 479 | use crate::TracelogSubscriber; 480 | use tracing_subscriber::prelude::*; 481 | use tracing_subscriber::reload; 482 | use tracing_subscriber::Registry; 483 | use win_etw_provider::GUID; 484 | 485 | static PROVIDER_GUID: GUID = GUID { 486 | data1: 0xe1c71d95, 487 | data2: 0x7bbc, 488 | data3: 0x5f48, 489 | data4: [0xa9, 0x2b, 0x8a, 0xaa, 0x0b, 0x52, 0x91, 0x58], 490 | }; 491 | 492 | static PROVIDER_NAME: &str = "rust-test-provider"; 493 | 494 | #[test] 495 | fn basic() { 496 | let layer = TracelogSubscriber::new(PROVIDER_GUID.clone(), PROVIDER_NAME).unwrap(); 497 | let _x = Registry::default().with(layer).set_default(); 498 | tracing::info!(foo = 123, bar = 456, "hi {baz}", baz = "what"); 499 | tracing::error!(foo = true, bar = ?PROVIDER_GUID); 500 | let err = anyhow::anyhow!("failed") 501 | .context("really failed") 502 | .context("this thing failed"); 503 | tracing::error!(error = &*err as &dyn std::error::Error, "disaster"); 504 | } 505 | 506 | #[test] 507 | fn span() { 508 | let layer = TracelogSubscriber::new(PROVIDER_GUID.clone(), PROVIDER_NAME).unwrap(); 509 | let _x = Registry::default().with(layer).set_default(); 510 | tracing::info_span!("geo", bar = 456).in_scope(|| { 511 | let span = tracing::info_span!("dude", baz = 789, later = tracing::field::Empty); 512 | span.in_scope(|| { 513 | tracing::info!("test"); 514 | span.record("later", true); 515 | span.record("later", "wait no it's a string now"); 516 | }); 517 | }); 518 | } 519 | 520 | #[test] 521 | fn global() { 522 | let (layer, reload_handle) = reload::Layer::new( 523 | TracelogSubscriber::new(PROVIDER_GUID.clone(), PROVIDER_NAME).unwrap(), 524 | ); 525 | let _x = Registry::default().with(layer).set_default(); 526 | tracing::info!(a_field = 123, "test globals"); 527 | let global = vec![("global", "some value")]; 528 | reload_handle 529 | .modify(|layer| layer.set_global_fields(&global)) 530 | .unwrap(); 531 | tracing::info!(a_field = 456, "test globals modify"); 532 | let _s = tracing::info_span!("span with globals", span_field = "abc").entered(); 533 | let global = vec![("global", "new value"), ("global2", "value")]; 534 | reload_handle 535 | .modify(|layer| layer.set_global_fields(&global)) 536 | .unwrap(); 537 | tracing::info!(a_field = 789, "test globals modify again"); 538 | } 539 | } 540 | --------------------------------------------------------------------------------