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 = []
## Enable the interrupt-mode executor (available in Cortex-M only)
executor-interrupt = []
## Enable tracing support (adds some overhead)
trace = []
## Enable tracing hooks
trace = ["_any_trace"]
## 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")]
mod state;
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
pub mod trace;
pub(crate) mod util;
#[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.
pub(crate) timer_queue_item: TimerQueueItem,
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
pub(crate) name: Option<&'static str>,
#[cfg(feature = "trace")]
#[cfg(feature = "rtos-trace")]
all_tasks_next: AtomicPtr<TaskHeader>,
}
@ -193,9 +193,9 @@ impl<F: Future + 'static> TaskStorage<F> {
poll_fn: SyncUnsafeCell::new(None),
timer_queue_item: TimerQueueItem::new(),
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
name: None,
#[cfg(feature = "trace")]
#[cfg(feature = "rtos-trace")]
all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
},
future: UninitCell::uninit(),
@ -231,7 +231,7 @@ impl<F: Future + 'static> TaskStorage<F> {
let mut cx = Context::from_waker(&waker);
match future.poll(&mut cx) {
Poll::Ready(_) => {
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
let exec_ptr: *const SyncExecutor = this.raw.executor.load(Ordering::Relaxed);
// 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.
this.raw.state.despawn();
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
trace::task_end(exec_ptr, &p);
}
Poll::Pending => {}
@ -419,7 +419,7 @@ impl SyncExecutor {
/// - `task` must NOT be already enqueued (in this executor or another one).
#[inline(always)]
unsafe fn enqueue(&self, task: TaskRef, l: state::Token) {
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
trace::task_ready_begin(self, &task);
if self.run_queue.enqueue(task, l) {
@ -432,7 +432,7 @@ impl SyncExecutor {
.executor
.store((self as *const Self).cast_mut(), Ordering::Relaxed);
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
trace::task_new(self, &task);
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.
pub(crate) unsafe fn poll(&'static self) {
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
trace::poll_start(self);
self.run_queue.dequeue_all(|p| {
let task = p.header();
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
trace::task_exec_begin(self, &p);
// Run the task
task.poll_fn.get().unwrap_unchecked()(p);
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
trace::task_exec_end(self, &p);
});
#[cfg(feature = "trace")]
#[cfg(feature = "_any_trace")]
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
/// a list of all tasks in the system. It's automatically updated by the
/// 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
///
/// This struct uses an intrusive linked list approach to track all tasks
/// without additional memory allocations. It maintains a global list of
/// 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>,
}
#[cfg(feature = "rtos-trace")]
impl TaskTracker {
/// Creates a new empty task tracker
///
@ -191,7 +194,7 @@ impl TaskRefTrace for TaskRef {
}
}
#[cfg(not(feature = "rtos-trace"))]
#[cfg(feature = "trace")]
extern "Rust" {
/// This callback is called when the executor begins polling. This will always
/// be paired with a later call to `_embassy_trace_executor_idle`.
@ -253,7 +256,7 @@ extern "Rust" {
#[inline]
pub(crate) fn poll_start(executor: &SyncExecutor) {
#[cfg(not(feature = "rtos-trace"))]
#[cfg(feature = "trace")]
unsafe {
_embassy_trace_poll_start(executor as *const _ as u32)
}
@ -261,7 +264,7 @@ pub(crate) fn poll_start(executor: &SyncExecutor) {
#[inline]
pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))]
#[cfg(feature = "trace")]
unsafe {
_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]
pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))]
#[cfg(feature = "trace")]
unsafe {
_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]
pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))]
#[cfg(feature = "trace")]
unsafe {
_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]
pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))]
#[cfg(feature = "trace")]
unsafe {
_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]
pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) {
#[cfg(not(feature = "rtos-trace"))]
#[cfg(feature = "trace")]
unsafe {
_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]
pub(crate) fn executor_idle(executor: &SyncExecutor) {
#[cfg(not(feature = "rtos-trace"))]
#[cfg(feature = "trace")]
unsafe {
_embassy_trace_executor_idle(executor as *const _ as u32)
}
@ -339,6 +342,7 @@ pub(crate) fn executor_idle(executor: &SyncExecutor) {
///
/// # Returns
/// An iterator that yields `TaskRef` items for each task
#[cfg(feature = "rtos-trace")]
fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {
struct TaskIterator<'a> {
tracker: &'a TaskTracker,
@ -367,6 +371,7 @@ fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {
}
/// Perform an action on each active task
#[cfg(feature = "rtos-trace")]
fn with_all_active_tasks<F>(f: F)
where
F: FnMut(TaskRef),