Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions crates/pyetwkit-core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ pub struct EtwEvent {
pub properties: HashMap<String, EventValue>,
/// Raw event data (if schema parsing failed)
pub raw_data: Option<Vec<u8>>,
/// TDH's own display strings, populated only when property formatting is on
pub formatted_properties: HashMap<String, String>,
/// Stack trace (if enabled)
pub stack_trace: Option<Vec<u64>>,
}
Expand Down Expand Up @@ -93,6 +95,7 @@ impl EtwEvent {
channel: 0,
properties: HashMap::new(),
raw_data: None,
formatted_properties: HashMap::new(),
stack_trace: None,
}
}
Expand Down Expand Up @@ -290,6 +293,39 @@ impl PyEtwEvent {
self.inner.channel
}

/// Undecoded payload, present only when TDH could not describe the event.
///
/// WPP events with no matching TMF, and manifest providers whose manifest is
/// not installed on this machine, have no property names to report a value
/// under. `properties` is empty for those, and this carries the bytes so the
/// payload is still recoverable. `None` whenever the event decoded normally.
#[getter]
fn raw_data(&self, py: Python<'_>) -> PyResult<Option<Py<PyAny>>> {
use pyo3::IntoPyObject;
match &self.inner.raw_data {
Some(bytes) => Ok(Some(
bytes.as_slice().into_pyobject(py)?.into_any().unbind(),
)),
None => Ok(None),
}
}

/// TDH's own display strings for each property, name to text.
///
/// Empty unless property formatting has been switched on, and empty for
/// events TDH has no schema for. These differ from `properties` in that TDH
/// resolves the manifest's value maps, so a bare number becomes the name the
/// manifest gives it. Struct-typed properties are absent: their members are
/// values in their own right and `properties` already reports them.
#[getter]
fn formatted_properties(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
for (key, value) in &self.inner.formatted_properties {
dict.set_item(key, value)?;
}
Ok(dict.into())
}

/// Get event properties as a dictionary
#[getter]
fn properties(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
Expand Down
20 changes: 20 additions & 0 deletions crates/pyetwkit-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,26 @@ fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
wrap_pyfunction!(discovery::py_get_provider_info, m)?,
)?;

// Property formatting toggle
m.add(
"set_property_formatting",
wrap_pyfunction!(tdh::py_set_property_formatting, m)?,
)?;
m.add(
"property_formatting",
wrap_pyfunction!(tdh::py_property_formatting, m)?,
)?;

// WPP decoding needs a directory of .tmf files to consult
m.add(
"set_wpp_tmf_search_path",
wrap_pyfunction!(tdh::py_set_wpp_tmf_search_path, m)?,
)?;
m.add(
"wpp_tmf_search_path",
wrap_pyfunction!(tdh::py_wpp_tmf_search_path, m)?,
)?;

// Register ETL reader
m.add_class::<etl_reader::PyEtlReader>()?;

Expand Down
12 changes: 11 additions & 1 deletion crates/pyetwkit-core/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,17 @@ pub fn parse_event_record(record: &EventRecord, schema: Option<&Schema>) -> EtwE
if let Some(schema) = schema {
event.provider_name = Some(schema.provider_name().to_string());
}
// Note: raw_data extraction removed as user_buffer is private in ferrisetw 1.2

// When TDH has no schema -- WPP with no matching TMF, or a manifest that is
// not installed here -- there are no names to report a payload under, and
// the payload used to be dropped entirely. Keep the bytes instead, so the
// event is at least recoverable. Only in that case: carrying a second copy
// of every decoded event's data would be a memory cost for nothing.
event.formatted_properties = crate::tdh::format_properties(record);

if event.properties.is_empty() && !crate::tdh::has_schema(record) {
event.raw_data = crate::tdh::raw_user_data(record);
}

event
}
Expand Down
Loading
Loading