1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! Safe wrappers over the EVENT_RECORD type

use windows::Win32::System::Diagnostics::Etw::EVENT_RECORD;
use windows::core::GUID;

use crate::native::etw_types::extended_data::EventHeaderExtendedDataItem;

/// A read-only wrapper over an [EVENT_RECORD](https://docs.microsoft.com/en-us/windows/win32/api/evntcons/ns-evntcons-event_record)
#[repr(transparent)]
pub struct EventRecord(EVENT_RECORD);

impl EventRecord {
    /// Create a `&self` from a Windows pointer.
    ///
    /// # Safety
    ///
    /// 1. Once an instance of `Self` is created, one should make sure the pointed data does not get modified (or dealloc'ed).
    /// 2. The returned lifetime is arbitray. To restrict the use of the returned reference (and to ensure the first safety guarantee), simply pass it to a sub-function whose signature has no explicit lifetime.
    ///    Thus, the sub-function will not be able to leak this reference.
    pub(crate) unsafe fn from_ptr<'a>(p: *const EVENT_RECORD) -> Option<&'a Self> {
        let s = p as *const Self;
        s.as_ref()
    }

    /// Get the wrapped `EVENT_RECORD` (usually to feed Windows API functions)
    ///
    /// # Safety
    ///
    /// Obviously, the returned pointer is only valid as long `self` is valid and not modified.
    pub(crate) fn as_raw_ptr(&self) -> *const EVENT_RECORD {
        &self.0 as *const EVENT_RECORD
    }

    /// The `UserContext` field from the wrapped `EVENT_RECORD`
    ///
    /// In this crate, it is always populated to point to a valid [`CallbackData`](crate::trace::CallbackData)
    pub(crate) fn user_context(&self) -> *const std::ffi::c_void {
        self.0.UserContext as *const _
    }

    /// The `ProviderId` field from the wrapped `EVENT_RECORD`
    pub fn provider_id(&self) -> GUID {
        self.0.EventHeader.ProviderId
    }

    /// The `Id` field from the wrapped `EVENT_RECORD`
    pub fn event_id(&self) -> u16 {
        self.0.EventHeader.EventDescriptor.Id
    }

    /// The `Opcode` field from the wrapped `EVENT_RECORD`
    pub fn opcode(&self) -> u8 {
        self.0.EventHeader.EventDescriptor.Opcode
    }

    /// The `Version` field from the wrapped `EVENT_RECORD`
    pub fn version(&self) -> u8 {
        self.0.EventHeader.EventDescriptor.Version
    }

    /// The `Level` field from the wrapped `EVENT_RECORD`
    pub fn level(&self) -> u8 {
        self.0.EventHeader.EventDescriptor.Level
    }

    /// The `Flags` field from the wrapped `EVENT_RECORD`
    pub fn event_flags(&self) -> u16 {
        self.0.EventHeader.Flags
    }

    /// The `ProcessId` field from the wrapped `EVENT_RECORD`
    pub fn process_id(&self) -> u32 {
        self.0.EventHeader.ProcessId
    }

    /// The `ThreadId` field from the wrapped `EVENT_RECORD`
    pub fn thread_id(&self) -> u32 {
        self.0.EventHeader.ThreadId
    }

    /// The `ActivityId` field from the wrapped `EVENT_RECORD`
    pub fn activity_id(&self) -> GUID {
        self.0.EventHeader.ActivityId
    }

    /// The `TimeStamp` field from the wrapped `EVENT_RECORD`
    ///
    /// As per [Microsoft's documentation](https://docs.microsoft.com/en-us/windows/win32/api/evntcons/ns-evntcons-event_header):
    /// > Contains the time that the event occurred.<br/>
    /// > The resolution is system time unless the `ProcessTraceMode member` of `EVENT_TRACE_LOGFILE`
    /// > contains the `PROCESS_TRACE_MODE_RAW_TIMESTAMP` flag, in which case the resolution depends
    /// > on the value of the `Wnode.ClientContext` member of `EVENT_TRACE_PROPERTIES` at the time
    /// > the controller created the session.
    ///
    /// Note: the `time_rs` Cargo feature enables to convert this into strongly-typed values
    pub fn raw_timestamp(&self) -> i64 {
        self.0.EventHeader.TimeStamp
    }

    /// The `TimeStamp` field from the wrapped `EVENT_RECORD`, as a strongly-typed `time::OffsetDateTime`
    #[cfg(feature = "time_rs")]
    pub fn timestamp(&self) -> time::OffsetDateTime {
        // "system time" means the count of hundreds of nanoseconds since midnight, January 1, 1601
        let system_time = self.0.EventHeader.TimeStamp;

        const SECONDS_BETWEEN_1601_AND_1970: i128 = 11_644_473_600;
        const HUNDREDS_OF_NANOS_IN_SECOND: i128 = 10_000_000;
        const HUNDREDS_OF_NANOSECONDS_BETWEEN_1601_AND_1970: i128 =
            SECONDS_BETWEEN_1601_AND_1970 * HUNDREDS_OF_NANOS_IN_SECOND;

        let unix_as_hundreds_of_nano_seconds = (system_time as i128) - HUNDREDS_OF_NANOSECONDS_BETWEEN_1601_AND_1970;
        let unix_as_nano_seconds = unix_as_hundreds_of_nano_seconds * 100;

        // Can't panic.
        // A filetime can go from 1601 to 30828.
        // OffsetDateTime (with the 'large-dates' feature) can represent any time from year -999_999 to +999_999. Meanwhile, .
        time::OffsetDateTime::from_unix_timestamp_nanos(unix_as_nano_seconds).unwrap()
    }

    pub(crate) fn user_buffer(&self) -> &[u8] {
        unsafe {
            std::slice::from_raw_parts(
                self.0.UserData as *mut _,
                self.0.UserDataLength.into(),
            )
        }
    }

    /// Returns the `ExtendedData` from the ETW Event
    ///
    /// Their availability is mostly determined by the flags passed to [`Provider::trace_flags`](crate::provider::Provider::trace_flags)
    ///
    /// # Example
    /// ```
    /// # use ferrisetw::EventRecord;
    /// # use ferrisetw::schema_locator::SchemaLocator;
    /// use windows::Win32::System::Diagnostics::Etw::EVENT_HEADER_EXT_TYPE_RELATED_ACTIVITYID;
    ///
    /// let my_callback = |record: &EventRecord, schema_locator: &SchemaLocator| {
    ///     let schema = schema_locator.event_schema(record).unwrap();
    ///     let activity_id = record
    ///         .extended_data()
    ///         .iter()
    ///         .find(|edata| edata.data_type() as u32 == EVENT_HEADER_EXT_TYPE_RELATED_ACTIVITYID)
    ///         .map(|edata| edata.to_extended_data_item());
    /// };
    /// ```
    pub fn extended_data(&self) -> &[EventHeaderExtendedDataItem] {
        let n_extended_data = self.0.ExtendedDataCount;
        let p_ed_array = self.0.ExtendedData;
        if n_extended_data == 0 || p_ed_array.is_null() {
            return &[];
        }

        // Safety: * we're building a slice from an array pointer size given by Windows
        //         * the pointed data is not supposed to be mutated during the lifetime of `Self`
        unsafe {
            std::slice::from_raw_parts(
                p_ed_array as *const EventHeaderExtendedDataItem,
                n_extended_data as usize)
        }
    }
}