executor: allow trace and rtos-trace to coexist additively.

Before, enabling `trace` would enable embassy-native tracing, and
enabling *both* would *disable* embassy-native tracing.
This commit is contained in:
Dario Nieuwenhuis 2025-07-08 23:36:51 +02:00 committed by diondokter
parent 658a52fb99
commit 2ba34ce217
3 changed files with 48 additions and 26 deletions

View File

@ -115,7 +115,24 @@ arch-spin = ["_arch"]
executor-thread = [] executor-thread = []
## Enable the interrupt-mode executor (available in Cortex-M only) ## Enable the interrupt-mode executor (available in Cortex-M only)
executor-interrupt = [] executor-interrupt = []
## Enable tracing support (adds some overhead) ## Enable tracing hooks
trace = [] trace = ["_any_trace"]
## Enable support for rtos-trace framework ## Enable support for rtos-trace framework
rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"] rtos-trace = ["dep:rtos-trace", "_any_trace", "dep:embassy-time-driver"]
_any_trace = []
#! ### Timer Item Payload Size
#! Sets the size of the payload for timer items, allowing integrated timer implementors to store
#! additional data in the timer item. The payload field will be aligned to this value as well.
#! If these features are not defined, the timer item will contain no payload field.
_timer-item-payload = [] # A size was picked
## 1 bytes
timer-item-payload-size-1 = ["_timer-item-payload"]
## 2 bytes
timer-item-payload-size-2 = ["_timer-item-payload"]
## 4 bytes
timer-item-payload-size-4 = ["_timer-item-payload"]
## 8 bytes
timer-item-payload-size-8 = ["_timer-item-payload"]

View File

@ -16,7 +16,7 @@ mod run_queue;
#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")] #[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
mod state; mod state;
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
pub mod trace; pub mod trace;
pub(crate) mod util; pub(crate) mod util;
#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
@ -94,9 +94,9 @@ pub(crate) struct TaskHeader {
/// Integrated timer queue storage. This field should not be accessed outside of the timer queue. /// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
pub(crate) timer_queue_item: TimerQueueItem, pub(crate) timer_queue_item: TimerQueueItem,
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
pub(crate) name: Option<&'static str>, pub(crate) name: Option<&'static str>,
#[cfg(feature = "trace")] #[cfg(feature = "rtos-trace")]
all_tasks_next: AtomicPtr<TaskHeader>, all_tasks_next: AtomicPtr<TaskHeader>,
} }
@ -193,9 +193,9 @@ impl<F: Future + 'static> TaskStorage<F> {
poll_fn: SyncUnsafeCell::new(None), poll_fn: SyncUnsafeCell::new(None),
timer_queue_item: TimerQueueItem::new(), timer_queue_item: TimerQueueItem::new(),
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
name: None, name: None,
#[cfg(feature = "trace")] #[cfg(feature = "rtos-trace")]
all_tasks_next: AtomicPtr::new(core::ptr::null_mut()), all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
}, },
future: UninitCell::uninit(), future: UninitCell::uninit(),
@ -231,7 +231,7 @@ impl<F: Future + 'static> TaskStorage<F> {
let mut cx = Context::from_waker(&waker); let mut cx = Context::from_waker(&waker);
match future.poll(&mut cx) { match future.poll(&mut cx) {
Poll::Ready(_) => { Poll::Ready(_) => {
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
let exec_ptr: *const SyncExecutor = this.raw.executor.load(Ordering::Relaxed); let exec_ptr: *const SyncExecutor = this.raw.executor.load(Ordering::Relaxed);
// As the future has finished and this function will not be called // As the future has finished and this function will not be called
@ -246,7 +246,7 @@ impl<F: Future + 'static> TaskStorage<F> {
// after we're done with it. // after we're done with it.
this.raw.state.despawn(); this.raw.state.despawn();
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
trace::task_end(exec_ptr, &p); trace::task_end(exec_ptr, &p);
} }
Poll::Pending => {} Poll::Pending => {}
@ -419,7 +419,7 @@ impl SyncExecutor {
/// - `task` must NOT be already enqueued (in this executor or another one). /// - `task` must NOT be already enqueued (in this executor or another one).
#[inline(always)] #[inline(always)]
unsafe fn enqueue(&self, task: TaskRef, l: state::Token) { unsafe fn enqueue(&self, task: TaskRef, l: state::Token) {
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
trace::task_ready_begin(self, &task); trace::task_ready_begin(self, &task);
if self.run_queue.enqueue(task, l) { if self.run_queue.enqueue(task, l) {
@ -432,7 +432,7 @@ impl SyncExecutor {
.executor .executor
.store((self as *const Self).cast_mut(), Ordering::Relaxed); .store((self as *const Self).cast_mut(), Ordering::Relaxed);
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
trace::task_new(self, &task); trace::task_new(self, &task);
state::locked(|l| { state::locked(|l| {
@ -444,23 +444,23 @@ impl SyncExecutor {
/// ///
/// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
pub(crate) unsafe fn poll(&'static self) { pub(crate) unsafe fn poll(&'static self) {
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
trace::poll_start(self); trace::poll_start(self);
self.run_queue.dequeue_all(|p| { self.run_queue.dequeue_all(|p| {
let task = p.header(); let task = p.header();
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
trace::task_exec_begin(self, &p); trace::task_exec_begin(self, &p);
// Run the task // Run the task
task.poll_fn.get().unwrap_unchecked()(p); task.poll_fn.get().unwrap_unchecked()(p);
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
trace::task_exec_end(self, &p); trace::task_exec_end(self, &p);
}); });
#[cfg(feature = "trace")] #[cfg(feature = "_any_trace")]
trace::executor_idle(self) trace::executor_idle(self)
} }
} }

View File

@ -95,17 +95,20 @@ use crate::spawner::{SpawnError, SpawnToken, Spawner};
/// This static provides access to the global task tracker which maintains /// This static provides access to the global task tracker which maintains
/// a list of all tasks in the system. It's automatically updated by the /// a list of all tasks in the system. It's automatically updated by the
/// task lifecycle hooks in the trace module. /// task lifecycle hooks in the trace module.
pub static TASK_TRACKER: TaskTracker = TaskTracker::new(); #[cfg(feature = "rtos-trace")]
pub(crate) static TASK_TRACKER: TaskTracker = TaskTracker::new();
/// A thread-safe tracker for all tasks in the system /// A thread-safe tracker for all tasks in the system
/// ///
/// This struct uses an intrusive linked list approach to track all tasks /// This struct uses an intrusive linked list approach to track all tasks
/// without additional memory allocations. It maintains a global list of /// without additional memory allocations. It maintains a global list of
/// tasks that can be traversed to find all currently existing tasks. /// tasks that can be traversed to find all currently existing tasks.
pub struct TaskTracker { #[cfg(feature = "rtos-trace")]
pub(crate) struct TaskTracker {
head: AtomicPtr<TaskHeader>, head: AtomicPtr<TaskHeader>,
} }
#[cfg(feature = "rtos-trace")]
impl TaskTracker { impl TaskTracker {
/// Creates a new empty task tracker /// Creates a new empty task tracker
/// ///
@ -191,7 +194,7 @@ impl TaskRefTrace for TaskRef {
} }
} }
#[cfg(not(feature = "rtos-trace"))] #[cfg(feature = "trace")]
extern "Rust" { extern "Rust" {
/// This callback is called when the executor begins polling. This will always /// This callback is called when the executor begins polling. This will always
/// be paired with a later call to `_embassy_trace_executor_idle`. /// be paired with a later call to `_embassy_trace_executor_idle`.
@ -253,7 +256,7 @@ extern "Rust" {
#[inline] #[inline]
pub(crate) fn poll_start(executor: &SyncExecutor) { pub(crate) fn poll_start(executor: &SyncExecutor) {
#[cfg(not(feature = "rtos-trace"))] #[cfg(feature = "trace")]
unsafe { unsafe {
_embassy_trace_poll_start(executor as *const _ as u32) _embassy_trace_poll_start(executor as *const _ as u32)
} }
@ -261,7 +264,7 @@ pub(crate) fn poll_start(executor: &SyncExecutor) {
#[inline] #[inline]
pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))] #[cfg(feature = "trace")]
unsafe { unsafe {
_embassy_trace_task_new(executor as *const _ as u32, task.as_ptr() as u32) _embassy_trace_task_new(executor as *const _ as u32, task.as_ptr() as u32)
} }
@ -285,7 +288,7 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
#[inline] #[inline]
pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) { pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))] #[cfg(feature = "trace")]
unsafe { unsafe {
_embassy_trace_task_end(executor as u32, task.as_ptr() as u32) _embassy_trace_task_end(executor as u32, task.as_ptr() as u32)
} }
@ -293,7 +296,7 @@ pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) {
#[inline] #[inline]
pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) { pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))] #[cfg(feature = "trace")]
unsafe { unsafe {
_embassy_trace_task_ready_begin(executor as *const _ as u32, task.as_ptr() as u32) _embassy_trace_task_ready_begin(executor as *const _ as u32, task.as_ptr() as u32)
} }
@ -303,7 +306,7 @@ pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) {
#[inline] #[inline]
pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) { pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))] #[cfg(feature = "trace")]
unsafe { unsafe {
_embassy_trace_task_exec_begin(executor as *const _ as u32, task.as_ptr() as u32) _embassy_trace_task_exec_begin(executor as *const _ as u32, task.as_ptr() as u32)
} }
@ -313,7 +316,7 @@ pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) {
#[inline] #[inline]
pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) { pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))] #[cfg(feature = "trace")]
unsafe { unsafe {
_embassy_trace_task_exec_end(executor as *const _ as u32, task.as_ptr() as u32) _embassy_trace_task_exec_end(executor as *const _ as u32, task.as_ptr() as u32)
} }
@ -323,7 +326,7 @@ pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) {
#[inline] #[inline]
pub(crate) fn executor_idle(executor: &SyncExecutor) { pub(crate) fn executor_idle(executor: &SyncExecutor) {
#[cfg(not(feature = "rtos-trace"))] #[cfg(feature = "trace")]
unsafe { unsafe {
_embassy_trace_executor_idle(executor as *const _ as u32) _embassy_trace_executor_idle(executor as *const _ as u32)
} }
@ -339,6 +342,7 @@ pub(crate) fn executor_idle(executor: &SyncExecutor) {
/// ///
/// # Returns /// # Returns
/// An iterator that yields `TaskRef` items for each task /// An iterator that yields `TaskRef` items for each task
#[cfg(feature = "rtos-trace")]
fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static { fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {
struct TaskIterator<'a> { struct TaskIterator<'a> {
tracker: &'a TaskTracker, tracker: &'a TaskTracker,
@ -367,6 +371,7 @@ fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {
} }
/// Perform an action on each active task /// Perform an action on each active task
#[cfg(feature = "rtos-trace")]
fn with_all_active_tasks<F>(f: F) fn with_all_active_tasks<F>(f: F)
where where
F: FnMut(TaskRef), F: FnMut(TaskRef),