Skip to content
Open
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
1 change: 1 addition & 0 deletions components/patina_performance/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ zerocopy-derive = { workspace = true }

[dev-dependencies]
patina = { path = "../../sdk/patina", features = ["mockall"] }
patina_dxe_core = { path = "../../patina_dxe_core"}
mockall = { workspace = true }
100 changes: 35 additions & 65 deletions components/patina_performance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,59 +13,48 @@ The Patina performance component maintains the infrastructure to report firmware

## Configuration

- `PerfConfig.enable_component` must be set to enable the component.
- `PerfConfig.enabled_measurements` carries the bitmask of `patina::performance::Measurement` values that should be
recorded.
- Platforms that need runtime configuration can include the `PerformanceConfigurationProvider` component, which reads a
`PerformanceConfigHob` and locks the `PerfConfig` values for the session.
By default (e.g. `Performance::new()`), performance measurements are disabled. Performance measurements can then be
enabled by one of two ways:

To enable performance measurements in Patina, add the `Performance` component and provide a `PerfConfig` using
`configs(mut add: Add<Config>)` in your platform's `ComponentInfo` implementation. For example:
1. Usage of the `Performance::measure(...)` method to specify the bitmask of `Measurement` values that should be
recorded.
2. Production of the `PerformanceConfigHob` prior to Patina DXE Core execution.

If both methods are used, the configuration via `PerformanceConfigHob` takes priority. You can change this behavior
to ignore the HOB configuration using `Performance::ignore_hob()`.

```rust
use patina_performance::component::Performance;
use patina_performance::component::performance_config_provider::PerformanceConfigurationProvider;
use patina_dxe_core::*;

impl ComponentInfo for ExamplePlatform {
fn configs(mut add: Add<Config>) {
add.config(patina_performance::config::PerfConfig {
enable_component: true,
enabled_measurements: {
patina::performance::Measurement::DriverBindingStart
| patina::performance::Measurement::DriverBindingStop
| patina::performance::Measurement::DriverBindingSupport
| patina::performance::Measurement::LoadImage
| patina::performance::Measurement::StartImage
}
});
}

fn components(mut add: Add<Component>) {
// Add your other components as needed, e.g.:
// add.component(AdvancedLoggerComponent::<YourUartType>::new(&LOGGER));
add.component(PerformanceConfigurationProvider);
add.component(Performance);
}
use patina_performance::component::*;

struct ExampleComponent;

impl ComponentInfo for ExampleComponent {
fn components(mut add: Add<Component>) {
// Performance measurements are enabled by default, but can be overridden by a performance config HOB.
add.component(Performance::new().measure(
Measurement::DriverBindingStart
| Measurement::DriverBindingStop
| Measurement::DriverBindingSupport
| Measurement::LoadImage
| Measurement::StartImage
));

// Performance measurements are disabled by default, but can be overridden by a performance config HOB.
add.component(Performance::new());

// Performance measurements are enabled with no ability to override.
add.component(Performance::new().ignore_hob().measure(
Measurement::DriverBindingStart
| Measurement::DriverBindingStop
| Measurement::DriverBindingSupport
| Measurement::LoadImage
| Measurement::StartImage
));
}
}
```

> **Note:** The `PerformanceConfigurationProvider` component is optional. Include it if you want to allow runtime
configuration of performance measurements via a HOB. If you only want static configuration, you can omit it.

### Enabling Performance Measurements During Boot

A component called `PerformanceConfigurationProvider` is used to enable performance measurements during the boot
process. This component depends on a `PerformanceConfigHob` HOB to be produced during boot to determine whether the
performance component should be enabled and which measurements should be active.

If a platform needs to use a single Patina DXE Core and support firmware builds where performance measurements can
be enabled or disabled, it should produce a `PerformanceConfigHob` HOB during the boot process and include the
`PerformanceConfigurationProvider` component in the DXE Core build. The HOB can be populated by any platform-specific
logic, such as a PCD value or a build variable.

> **Note:** `PerformanceConfigurationProvider` will override the enabled measurements based on the HOB value.

## API

| Macro name in EDK II | Function name in Patina component | Description |
Expand Down Expand Up @@ -102,25 +91,6 @@ use mu_rust_helpers::guid::CALLER_ID;
perf_function_begin("foo", &CALLER_ID, create_performance_measurement);
```

*Example of measurement from outside the core:*

```rust,no_run
# extern crate mu_rust_helpers;
# extern crate patina;
# let bs = patina::boot_services::StandardBootServices::new_uninit();
use mu_rust_helpers::guid::CALLER_ID;
use patina::{
boot_services::BootServices,
performance::logging::perf_function_begin,
uefi_protocol::performance_measurement::EdkiiPerformanceMeasurement,
};

let create_performance_measurement = unsafe { bs.locate_protocol::<EdkiiPerformanceMeasurement>(None) }
.map_or(None, |p| Some(p.create_performance_measurement));

create_performance_measurement.inspect(|f| perf_function_begin("foo", &CALLER_ID, *f));
```

## Performance Component Overview

The **Performance Component** provides an API for logging performance measurements during firmware execution. This
Expand Down
5 changes: 3 additions & 2 deletions components/patina_performance/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
//! SPDX-License-Identifier: Apache-2.0
//!
pub mod performance;
pub mod performance_config_provider;
mod performance;
mod protocol;

// Re-export the Performance component for easier access.
pub use performance::Performance;
// Re-export of the Measurement enum for easier access.
pub use patina::performance::Measurement;
99 changes: 87 additions & 12 deletions components/patina_performance/src/component/performance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@
//!
//! SPDX-License-Identifier: Apache-2.0
//!

use crate::{component::protocol::create_performance_measurement_efiapi, config, mm};
use crate::{component::protocol::create_performance_measurement_efiapi, mm};
use alloc::{boxed::Box, string::String, vec::Vec};
use patina::{
boot_services::{BootServices, StandardBootServices, event::EventType, tpl::Tpl},
component::{
component,
hob::Hob,
params::Config,
service::{Service, perf_timer::ArchTimerFunctionality},
},
error::EfiError,
Expand All @@ -38,30 +36,108 @@ use patina::{
use patina_mm::component::communicator::MmCommunication;
use r_efi::system::EVENT_GROUP_READY_TO_BOOT;

pub use mu_rust_helpers::function;
use mu_rust_helpers::function;

use patina::guids::EVENT_GROUP_END_OF_DXE;

/// Context parameter for the Ready-to-Boot event callback that fetches MM performance records.
type MmPerformanceEventContext<B, F> = Box<(B, &'static TplMutex<F, B>, Service<dyn MmCommunication>)>;

use patina::component::hob::FromHob;

/// The configuration for the Patina Performance component.
#[derive(Debug, Clone, Copy, FromHob, zerocopy_derive::FromBytes)]
#[hob = "fd87f2d8-112d-4640-9c00-d37d2a1fb75d"]
#[repr(C, packed)]
struct PerformanceConfig {
/// Indicates whether the Patina Performance component is enabled.
pub enable_component: u8,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not bool?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bool is not a supported zerocopy type

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is for TryFromBytes that will validate the only 0 or 1 semantic you assume below.

Image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, the current design of the FromHob trait is that parsing must be infallible. I actually think that it would make more sense if it was fallible. But at this point in time the FromHob macro expects zerocopy::FromBytes to be implemented. We could do a manual implementation, but we would have to unwrap the result.

/// Bitmask of enabled measurements (see [`patina::performance::Measurement`]).
pub enabled_measurements: u32,
}

impl PerformanceConfig {
/// Constant value indicating that the Patina Performance component is enabled.
pub const ENABLED: u8 = 1;
/// Constant value indicating that the Patina Performance component is disabled.
pub const DISABLED: u8 = 0;
/// Constant value indicating that no performance measurements are enabled.
pub const NO_MEASUREMENTS: u32 = 0;
}

impl Default for PerformanceConfig {
fn default() -> Self {
Self::new()
}
}

impl PerformanceConfig {
/// Creates a new `PerformanceConfig` with the specified settings.
pub const fn new() -> Self {
Self { enable_component: Self::DISABLED, enabled_measurements: Self::NO_MEASUREMENTS }
}
}

/// Performance Component.
pub struct Performance;
///
/// ## Example Usage
///
/// ```rust
/// use patina_performance::component::*;
///
/// // Performance measurements are enabled by default, but can be overridden by a performance config HOB.
/// let enabled_component = Performance::new().measure(Measurement::DriverBindingStart | Measurement::LoadImage);
///
/// // Performance measurements are disabled by default, but can be overridden by a performance config HOB.
/// let disabled_component = Performance::new();
///
/// // Performance measurements are enabled with no ability to override via a HOB.
/// let enabled_no_hob_component = Performance::new().measure(Measurement::DriverBindingStart | Measurement::LoadImage).ignore_hob();
/// ```
#[derive(Default)]
pub struct Performance {
config: PerformanceConfig,
ignore_hob: bool,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HOBs are already dynamically produced by platform logic, for example, in this case whether or not the platform would want to configure performance. The performance_config_provider was more to keep this component free of HOB logic which it now has anyway.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can ignore_hob just be dropped entirely and tell the platform to produce the HOB to override config if they want?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we could absolutely entirely drop ignore_hob if we wanted, and I'm happy to do that. It would technically result in a regression in configurability, however.

Right now, you it is optional to add the PerformanceConfigurationProvider, which means a platform has the possibility of ignoring the HOB even if it exists. Doing the above suggestion removes that possibility. I'm not against it. But it is a thing.

}

#[component]
impl Performance {
/// Creates a new instance of the Performance component with the given configuration.
pub const fn new() -> Self {
Self { config: PerformanceConfig::new(), ignore_hob: false }
}

/// Ignores HOB configuration if present.
pub const fn ignore_hob(mut self) -> Self {
self.ignore_hob = true;
self
}

/// Enables performance measuring with the specified measurements.
pub const fn measure(mut self, measurements: u32) -> Self {
self.config.enable_component = PerformanceConfig::ENABLED;
self.config.enabled_measurements = measurements;
self
}

/// Entry point of [`Performance`]
#[coverage(off)] // This is tested via the generic version, see _entry_point.
pub fn entry_point(
fn entry_point(
self,
config: Config<config::PerfConfig>,
hob: Option<Hob<PerformanceConfig>>,
boot_services: StandardBootServices,
runtime_services: StandardRuntimeServices,
records_buffers_hobs: Option<Hob<HobPerformanceData>>,
timer: Service<dyn ArchTimerFunctionality>,
mm_comm_service: Option<Service<dyn MmCommunication>>,
) -> Result<(), EfiError> {
if !config.enable_component {
// Use HOB config if available, otherwise fall back to component config
let config = match hob {
Some(hob) if !self.ignore_hob => *hob,
_ => self.config,
};

if config.enable_component == PerformanceConfig::DISABLED {
log::warn!("Patina Performance Component is not enabled, skipping entry point.");
return Ok(());
}
Expand All @@ -80,12 +156,11 @@ impl Performance {
return Err(EfiError::Aborted);
};

self._entry_point(boot_services, runtime_services, records_buffers_hobs, mm_comm_service, fbpt, timer)
Self::_entry_point(boot_services, runtime_services, records_buffers_hobs, mm_comm_service, fbpt, timer)
}

/// Entry point that have generic parameter.
fn _entry_point<B, R, P, F>(
self,
boot_services: B,
runtime_services: R,
records_buffers_hobs: Option<P>,
Expand Down Expand Up @@ -570,7 +645,7 @@ mod tests {
// Leak the fbpt to create a 'static reference for testing.
let fbpt = Box::leak(Box::new(fbpt));

let _ = Performance._entry_point(
let _ = Performance::_entry_point(
boot_services,
runtime_services,
Some(hob_perf_data_extractor),
Expand Down Expand Up @@ -634,7 +709,7 @@ mod tests {

let mm_service: Service<dyn MmCommunication> = Service::mock(Box::new(FakeComm));
let timer: Service<dyn ArchTimerFunctionality> = Service::mock(Box::new(MockTimer {}));
let _ = Performance._entry_point(
let _ = Performance::_entry_point(
entry_point_mock,
runtime_services,
Option::<MockHobPerformanceDataExtractor>::None,
Expand Down

This file was deleted.

Loading
Loading