aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-executor/src/raw/mod.rs20
-rw-r--r--embassy-executor/src/raw/trace.rs183
-rw-r--r--embassy-executor/src/spawner.rs53
3 files changed, 245 insertions, 11 deletions
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index 88d839e07..e7a27035a 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -18,7 +18,7 @@ mod state;
18 18
19pub mod timer_queue; 19pub mod timer_queue;
20#[cfg(feature = "trace")] 20#[cfg(feature = "trace")]
21mod trace; 21pub mod trace;
22pub(crate) mod util; 22pub(crate) mod util;
23#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] 23#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
24mod waker; 24mod waker;
@@ -89,6 +89,12 @@ pub(crate) struct TaskHeader {
89 89
90 /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. 90 /// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
91 pub(crate) timer_queue_item: timer_queue::TimerQueueItem, 91 pub(crate) timer_queue_item: timer_queue::TimerQueueItem,
92 #[cfg(feature = "trace")]
93 pub(crate) name: Option<&'static str>,
94 #[cfg(feature = "trace")]
95 pub(crate) id: u32,
96 #[cfg(feature = "trace")]
97 all_tasks_next: AtomicPtr<TaskHeader>,
92} 98}
93 99
94/// This is essentially a `&'static TaskStorage<F>` where the type of the future has been erased. 100/// This is essentially a `&'static TaskStorage<F>` where the type of the future has been erased.
@@ -143,12 +149,6 @@ impl TaskRef {
143 pub(crate) fn as_ptr(self) -> *const TaskHeader { 149 pub(crate) fn as_ptr(self) -> *const TaskHeader {
144 self.ptr.as_ptr() 150 self.ptr.as_ptr()
145 } 151 }
146
147 /// Get the ID for a task
148 #[cfg(feature = "trace")]
149 pub fn as_id(self) -> u32 {
150 self.ptr.as_ptr() as u32
151 }
152} 152}
153 153
154/// Raw storage in which a task can be spawned. 154/// Raw storage in which a task can be spawned.
@@ -190,6 +190,12 @@ impl<F: Future + 'static> TaskStorage<F> {
190 poll_fn: SyncUnsafeCell::new(None), 190 poll_fn: SyncUnsafeCell::new(None),
191 191
192 timer_queue_item: timer_queue::TimerQueueItem::new(), 192 timer_queue_item: timer_queue::TimerQueueItem::new(),
193 #[cfg(feature = "trace")]
194 name: None,
195 #[cfg(feature = "trace")]
196 id: 0,
197 #[cfg(feature = "trace")]
198 all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
193 }, 199 },
194 future: UninitCell::uninit(), 200 future: UninitCell::uninit(),
195 } 201 }
diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs
index aba519c8f..6c9cfda25 100644
--- a/embassy-executor/src/raw/trace.rs
+++ b/embassy-executor/src/raw/trace.rs
@@ -81,7 +81,131 @@
81 81
82#![allow(unused)] 82#![allow(unused)]
83 83
84use crate::raw::{SyncExecutor, TaskRef}; 84use core::cell::UnsafeCell;
85use core::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
86
87use rtos_trace::TaskInfo;
88
89use crate::raw::{SyncExecutor, TaskHeader, TaskRef};
90use crate::spawner::{SpawnError, SpawnToken, Spawner};
91
92/// Global task tracker instance
93///
94/// This static provides access to the global task tracker which maintains
95/// a list of all tasks in the system. It's automatically updated by the
96/// task lifecycle hooks in the trace module.
97pub static TASK_TRACKER: TaskTracker = TaskTracker::new();
98
99/// A thread-safe tracker for all tasks in the system
100///
101/// This struct uses an intrusive linked list approach to track all tasks
102/// without additional memory allocations. It maintains a global list of
103/// tasks that can be traversed to find all currently existing tasks.
104pub struct TaskTracker {
105 head: AtomicPtr<TaskHeader>,
106}
107
108impl TaskTracker {
109 /// Creates a new empty task tracker
110 ///
111 /// Initializes a tracker with no tasks in its list.
112 pub const fn new() -> Self {
113 Self {
114 head: AtomicPtr::new(core::ptr::null_mut()),
115 }
116 }
117
118 /// Adds a task to the tracker
119 ///
120 /// This method inserts a task at the head of the intrusive linked list.
121 /// The operation is thread-safe and lock-free, using atomic operations
122 /// to ensure consistency even when called from different contexts.
123 ///
124 /// # Arguments
125 /// * `task` - The task reference to add to the tracker
126 pub fn add(&self, task: TaskRef) {
127 let task_ptr = task.as_ptr() as *mut TaskHeader;
128
129 loop {
130 let current_head = self.head.load(Ordering::Acquire);
131 unsafe {
132 (*task_ptr).all_tasks_next.store(current_head, Ordering::Relaxed);
133 }
134
135 if self
136 .head
137 .compare_exchange(current_head, task_ptr, Ordering::Release, Ordering::Relaxed)
138 .is_ok()
139 {
140 break;
141 }
142 }
143 }
144
145 /// Performs an operation on each task in the tracker
146 ///
147 /// This method traverses the entire list of tasks and calls the provided
148 /// function for each task. This allows inspecting or processing all tasks
149 /// in the system without modifying the tracker's structure.
150 ///
151 /// # Arguments
152 /// * `f` - A function to call for each task in the tracker
153 pub fn for_each<F>(&self, mut f: F)
154 where
155 F: FnMut(TaskRef),
156 {
157 let mut current = self.head.load(Ordering::Acquire);
158 while !current.is_null() {
159 let task = unsafe { TaskRef::from_ptr(current) };
160 f(task);
161
162 current = unsafe { (*current).all_tasks_next.load(Ordering::Acquire) };
163 }
164 }
165}
166
167/// Extension trait for `TaskRef` that provides tracing functionality.
168///
169/// This trait is only available when the `trace` feature is enabled.
170/// It extends `TaskRef` with methods for accessing and modifying task identifiers
171/// and names, which are useful for debugging, logging, and performance analysis.
172pub trait TaskRefTrace {
173 /// Get the name for a task
174 fn name(&self) -> Option<&'static str>;
175
176 /// Set the name for a task
177 fn set_name(&self, name: Option<&'static str>);
178
179 /// Get the ID for a task
180 fn id(&self) -> u32;
181
182 /// Set the ID for a task
183 fn set_id(&self, id: u32);
184}
185
186impl TaskRefTrace for TaskRef {
187 fn name(&self) -> Option<&'static str> {
188 self.header().name
189 }
190
191 fn set_name(&self, name: Option<&'static str>) {
192 unsafe {
193 let header_ptr = self.ptr.as_ptr() as *mut TaskHeader;
194 (*header_ptr).name = name;
195 }
196 }
197
198 fn id(&self) -> u32 {
199 self.header().id
200 }
201
202 fn set_id(&self, id: u32) {
203 unsafe {
204 let header_ptr = self.ptr.as_ptr() as *mut TaskHeader;
205 (*header_ptr).id = id;
206 }
207 }
208}
85 209
86#[cfg(not(feature = "rtos-trace"))] 210#[cfg(not(feature = "rtos-trace"))]
87extern "Rust" { 211extern "Rust" {
@@ -160,6 +284,9 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
160 284
161 #[cfg(feature = "rtos-trace")] 285 #[cfg(feature = "rtos-trace")]
162 rtos_trace::trace::task_new(task.as_ptr() as u32); 286 rtos_trace::trace::task_new(task.as_ptr() as u32);
287
288 #[cfg(feature = "rtos-trace")]
289 TASK_TRACKER.add(*task);
163} 290}
164 291
165#[inline] 292#[inline]
@@ -210,10 +337,62 @@ pub(crate) fn executor_idle(executor: &SyncExecutor) {
210 rtos_trace::trace::system_idle(); 337 rtos_trace::trace::system_idle();
211} 338}
212 339
340/// Returns an iterator over all active tasks in the system
341///
342/// This function provides a convenient way to iterate over all tasks
343/// that are currently tracked in the system. The returned iterator
344/// yields each task in the global task tracker.
345///
346/// # Returns
347/// An iterator that yields `TaskRef` items for each task
348fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {
349 struct TaskIterator<'a> {
350 tracker: &'a TaskTracker,
351 current: *mut TaskHeader,
352 }
353
354 impl<'a> Iterator for TaskIterator<'a> {
355 type Item = TaskRef;
356
357 fn next(&mut self) -> Option<Self::Item> {
358 if self.current.is_null() {
359 return None;
360 }
361
362 let task = unsafe { TaskRef::from_ptr(self.current) };
363 self.current = unsafe { (*self.current).all_tasks_next.load(Ordering::Acquire) };
364
365 Some(task)
366 }
367 }
368
369 TaskIterator {
370 tracker: &TASK_TRACKER,
371 current: TASK_TRACKER.head.load(Ordering::Acquire),
372 }
373}
374
375/// Perform an action on each active task
376fn with_all_active_tasks<F>(f: F)
377where
378 F: FnMut(TaskRef),
379{
380 TASK_TRACKER.for_each(f);
381}
382
213#[cfg(feature = "rtos-trace")] 383#[cfg(feature = "rtos-trace")]
214impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor { 384impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor {
215 fn task_list() { 385 fn task_list() {
216 // We don't know what tasks exist, so we can't send them. 386 with_all_active_tasks(|task| {
387 let name = task.name().unwrap_or("unnamed task\0");
388 let info = rtos_trace::TaskInfo {
389 name,
390 priority: 0,
391 stack_base: 0,
392 stack_size: 0,
393 };
394 rtos_trace::trace::task_send_info(task.id(), info);
395 });
217 } 396 }
218 fn time() -> u64 { 397 fn time() -> u64 {
219 const fn gcd(a: u64, b: u64) -> u64 { 398 const fn gcd(a: u64, b: u64) -> u64 {
diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs
index ff243081c..522d97db3 100644
--- a/embassy-executor/src/spawner.rs
+++ b/embassy-executor/src/spawner.rs
@@ -5,6 +5,8 @@ use core::sync::atomic::Ordering;
5use core::task::Poll; 5use core::task::Poll;
6 6
7use super::raw; 7use super::raw;
8#[cfg(feature = "trace")]
9use crate::raw::trace::TaskRefTrace;
8 10
9/// Token to spawn a newly-created task in an executor. 11/// Token to spawn a newly-created task in an executor.
10/// 12///
@@ -22,7 +24,7 @@ use super::raw;
22/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. 24/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
23#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] 25#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
24pub struct SpawnToken<S> { 26pub struct SpawnToken<S> {
25 raw_task: Option<raw::TaskRef>, 27 pub(crate) raw_task: Option<raw::TaskRef>,
26 phantom: PhantomData<*mut S>, 28 phantom: PhantomData<*mut S>,
27} 29}
28 30
@@ -103,7 +105,7 @@ impl core::error::Error for SpawnError {}
103/// If you want to spawn tasks from another thread, use [SendSpawner]. 105/// If you want to spawn tasks from another thread, use [SendSpawner].
104#[derive(Copy, Clone)] 106#[derive(Copy, Clone)]
105pub struct Spawner { 107pub struct Spawner {
106 executor: &'static raw::Executor, 108 pub(crate) executor: &'static raw::Executor,
107 not_send: PhantomData<*mut ()>, 109 not_send: PhantomData<*mut ()>,
108} 110}
109 111
@@ -180,6 +182,53 @@ impl Spawner {
180 } 182 }
181} 183}
182 184
185/// Extension trait adding tracing capabilities to the Spawner
186///
187/// This trait provides an additional method to spawn tasks with an associated name,
188/// which can be useful for debugging and tracing purposes.
189pub trait SpawnerTraceExt {
190 /// Spawns a new task with a specified name.
191 ///
192 /// # Arguments
193 /// * `name` - Static string name to associate with the task
194 /// * `token` - Token representing the task to spawn
195 ///
196 /// # Returns
197 /// Result indicating whether the spawn was successful
198 fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>;
199}
200
201/// Implementation of the SpawnerTraceExt trait for Spawner when trace is enabled
202#[cfg(feature = "trace")]
203impl SpawnerTraceExt for Spawner {
204 fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
205 let task = token.raw_task;
206 core::mem::forget(token);
207
208 match task {
209 Some(task) => {
210 // Set the name and ID when trace is enabled
211 task.set_name(Some(name));
212 let task_id = task.as_ptr() as u32;
213 task.set_id(task_id);
214
215 unsafe { self.executor.spawn(task) };
216 Ok(())
217 }
218 None => Err(SpawnError::Busy),
219 }
220 }
221}
222
223/// Implementation of the SpawnerTraceExt trait for Spawner when trace is disabled
224#[cfg(not(feature = "trace"))]
225impl SpawnerTraceExt for Spawner {
226 fn spawn_named<S>(&self, _name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
227 // When trace is disabled, just forward to regular spawn and ignore the name
228 self.spawn(token)
229 }
230}
231
183/// Handle to spawn tasks into an executor from any thread. 232/// Handle to spawn tasks into an executor from any thread.
184/// 233///
185/// This Spawner can be used from any thread (it is Send), but it can 234/// This Spawner can be used from any thread (it is Send), but it can