Skip to content

Commit ed6e075

Browse files
committed
add selective tracing support to SpawnerTraceExt
Add spawn_with_trace(), spawn_no_trace() and named variants to provide fine-grained control over task tracing. Supports both default tracing (rtos-trace) and selective tracing (rtos-trace-selective) modes while maintaining backward compatibility.
1 parent 56572ef commit ed6e075

File tree

4 files changed

+225
-19
lines changed

4 files changed

+225
-19
lines changed

embassy-executor/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,10 @@ executor-thread = []
9595
executor-interrupt = []
9696
## Enable tracing support (adds some overhead)
9797
trace = []
98-
## Enable support for rtos-trace framework
98+
## Enable support for rtos-trace framework (traces all tasks by default)
9999
rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"]
100+
## Only trace tasks that are explicitly marked for tracing (selective mode)
101+
rtos-trace-selective = ["rtos-trace"]
100102

101103
#! ### Timer Item Payload Size
102104
#! Sets the size of the payload for timer items, allowing integrated timer implementors to store

embassy-executor/src/raw/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ pub(crate) struct TaskHeader {
9494
#[cfg(feature = "trace")]
9595
pub(crate) id: u32,
9696
#[cfg(feature = "trace")]
97+
pub(crate) trace_excluded: bool,
98+
#[cfg(feature = "trace")]
9799
all_tasks_next: AtomicPtr<TaskHeader>,
98100
}
99101

@@ -195,6 +197,8 @@ impl<F: Future + 'static> TaskStorage<F> {
195197
#[cfg(feature = "trace")]
196198
id: 0,
197199
#[cfg(feature = "trace")]
200+
trace_excluded: cfg!(feature = "rtos-trace-selective"),
201+
#[cfg(feature = "trace")]
198202
all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
199203
},
200204
future: UninitCell::uninit(),

embassy-executor/src/raw/trace.rs

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl TaskTracker {
141141
}
142142
}
143143
}
144-
144+
145145
/// Performs an operation on each task in the tracker
146146
///
147147
/// This method traverses the entire list of tasks and calls the provided
@@ -181,6 +181,12 @@ pub trait TaskRefTrace {
181181

182182
/// Set the ID for a task
183183
fn set_id(&self, id: u32);
184+
185+
/// Check if a task is excluded from tracing
186+
fn is_trace_excluded(&self) -> bool;
187+
188+
/// Set whether a task should be excluded from tracing
189+
fn set_trace_excluded(&self, excluded: bool);
184190
}
185191

186192
impl TaskRefTrace for TaskRef {
@@ -205,6 +211,41 @@ impl TaskRefTrace for TaskRef {
205211
(*header_ptr).id = id;
206212
}
207213
}
214+
215+
fn is_trace_excluded(&self) -> bool {
216+
self.header().trace_excluded
217+
}
218+
219+
fn set_trace_excluded(&self, excluded: bool) {
220+
unsafe {
221+
let header_ptr = self.ptr.as_ptr() as *mut TaskHeader;
222+
(*header_ptr).trace_excluded = excluded;
223+
}
224+
}
225+
}
226+
227+
/// Helper function to determine if a task should be traced
228+
///
229+
/// This function supports two tracing modes based on feature flags:
230+
/// - `rtos-trace-selective`: Only trace tasks that are explicitly enabled for tracing
231+
/// - Default `rtos-trace`: Trace all tasks by default
232+
#[inline]
233+
fn should_trace_task(task: &TaskRef) -> bool {
234+
#[cfg(feature = "rtos-trace-selective")]
235+
{
236+
// Only trace tasks that are explicitly enabled for tracing
237+
!task.is_trace_excluded()
238+
}
239+
#[cfg(all(feature = "rtos-trace", not(feature = "rtos-trace-selective")))]
240+
{
241+
let _ = task; // Suppress unused parameter warning
242+
true // Trace all tasks when rtos-trace feature is enabled without selective mode
243+
}
244+
#[cfg(not(feature = "rtos-trace"))]
245+
{
246+
let _ = task; // Suppress unused parameter warning
247+
false // No tracing when rtos-trace is not enabled
248+
}
208249
}
209250

210251
#[cfg(not(feature = "rtos-trace"))]
@@ -283,10 +324,10 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
283324
}
284325

285326
#[cfg(feature = "rtos-trace")]
286-
rtos_trace::trace::task_new(task.as_ptr() as u32);
287-
288-
#[cfg(feature = "rtos-trace")]
289-
TASK_TRACKER.add(*task);
327+
if should_trace_task(task) {
328+
rtos_trace::trace::task_new(task.as_ptr() as u32);
329+
TASK_TRACKER.add(*task);
330+
}
290331
}
291332

292333
#[inline]
@@ -304,7 +345,9 @@ pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) {
304345
_embassy_trace_task_ready_begin(executor as *const _ as u32, task.as_ptr() as u32)
305346
}
306347
#[cfg(feature = "rtos-trace")]
307-
rtos_trace::trace::task_ready_begin(task.as_ptr() as u32);
348+
if should_trace_task(task) {
349+
rtos_trace::trace::task_ready_begin(task.as_ptr() as u32);
350+
}
308351
}
309352

310353
#[inline]
@@ -314,7 +357,9 @@ pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) {
314357
_embassy_trace_task_exec_begin(executor as *const _ as u32, task.as_ptr() as u32)
315358
}
316359
#[cfg(feature = "rtos-trace")]
317-
rtos_trace::trace::task_exec_begin(task.as_ptr() as u32);
360+
if should_trace_task(task) {
361+
rtos_trace::trace::task_exec_begin(task.as_ptr() as u32);
362+
}
318363
}
319364

320365
#[inline]
@@ -324,7 +369,9 @@ pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) {
324369
_embassy_trace_task_exec_end(executor as *const _ as u32, task.as_ptr() as u32)
325370
}
326371
#[cfg(feature = "rtos-trace")]
327-
rtos_trace::trace::task_exec_end();
372+
if should_trace_task(task) {
373+
rtos_trace::trace::task_exec_end();
374+
}
328375
}
329376

330377
#[inline]
@@ -384,14 +431,17 @@ where
384431
impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor {
385432
fn task_list() {
386433
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);
434+
// Only send task info for tasks that should be traced
435+
if should_trace_task(&task) {
436+
let name = task.name().unwrap_or("unnamed task\0");
437+
let info = rtos_trace::TaskInfo {
438+
name,
439+
priority: 0,
440+
stack_base: 0,
441+
stack_size: 0,
442+
};
443+
rtos_trace::trace::task_send_info(task.id(), info);
444+
}
395445
});
396446
}
397447
fn time() -> u64 {

embassy-executor/src/spawner.rs

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,13 @@ impl Spawner {
184184

185185
/// Extension trait adding tracing capabilities to the Spawner
186186
///
187-
/// This trait provides an additional method to spawn tasks with an associated name,
188-
/// which can be useful for debugging and tracing purposes.
187+
/// This trait provides additional methods to spawn tasks with tracing controls.
188+
///
189+
/// **Default workflow** (`rtos-trace` feature): All tasks are traced by default.
190+
/// You can use `spawn_no_trace()` methods to exclude specific tasks.
191+
///
192+
/// **Selective workflow** (`rtos-trace-selective` feature): No tasks are traced by default.
193+
/// You must use `spawn_with_trace()` methods to include specific tasks.
189194
pub trait SpawnerTraceExt {
190195
/// Spawns a new task with a specified name.
191196
///
@@ -196,6 +201,50 @@ pub trait SpawnerTraceExt {
196201
/// # Returns
197202
/// Result indicating whether the spawn was successful
198203
fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>;
204+
205+
/// Spawns a new task and explicitly enables it for tracing.
206+
///
207+
/// **In default mode** (`rtos-trace`): This behaves the same as `spawn()` since all tasks are traced.
208+
/// **In selective mode** (`rtos-trace-selective`): Only tasks spawned with this method will be traced.
209+
///
210+
/// # Arguments
211+
/// * `token` - Token representing the task to spawn
212+
///
213+
/// # Returns
214+
/// Result indicating whether the spawn was successful
215+
fn spawn_with_trace<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError>;
216+
217+
/// Spawns a new task with a name and explicitly enables it for tracing.
218+
///
219+
/// # Arguments
220+
/// * `name` - Static string name to associate with the task
221+
/// * `token` - Token representing the task to spawn
222+
///
223+
/// # Returns
224+
/// Result indicating whether the spawn was successful
225+
fn spawn_named_with_trace<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>;
226+
227+
/// Spawns a new task and excludes it from tracing.
228+
///
229+
/// **In default mode** (`rtos-trace`): Use this to exclude specific tasks from tracing.
230+
/// **In selective mode** (`rtos-trace-selective`): This behaves the same as `spawn()` since tasks are excluded by default.
231+
///
232+
/// # Arguments
233+
/// * `token` - Token representing the task to spawn
234+
///
235+
/// # Returns
236+
/// Result indicating whether the spawn was successful
237+
fn spawn_no_trace<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError>;
238+
239+
/// Spawns a new task with a name but excludes it from tracing.
240+
///
241+
/// # Arguments
242+
/// * `name` - Static string name to associate with the task
243+
/// * `token` - Token representing the task to spawn
244+
///
245+
/// # Returns
246+
/// Result indicating whether the spawn was successful
247+
fn spawn_named_no_trace<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>;
199248
}
200249

201250
/// Implementation of the SpawnerTraceExt trait for Spawner when trace is enabled
@@ -212,6 +261,87 @@ impl SpawnerTraceExt for Spawner {
212261
let task_id = task.as_ptr() as u32;
213262
task.set_id(task_id);
214263

264+
// In selective mode, tasks are excluded by default (must use spawn_named_with_trace)
265+
// In default mode, all tasks are traced
266+
#[cfg(feature = "rtos-trace-selective")]
267+
task.set_trace_excluded(true);
268+
#[cfg(not(feature = "rtos-trace-selective"))]
269+
task.set_trace_excluded(false);
270+
271+
unsafe { self.executor.spawn(task) };
272+
Ok(())
273+
}
274+
None => Err(SpawnError::Busy),
275+
}
276+
}
277+
278+
fn spawn_with_trace<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
279+
let task = token.raw_task;
280+
core::mem::forget(token);
281+
282+
match task {
283+
Some(task) => {
284+
// Explicitly enable tracing for this task
285+
task.set_trace_excluded(false);
286+
let task_id = task.as_ptr() as u32;
287+
task.set_id(task_id);
288+
289+
unsafe { self.executor.spawn(task) };
290+
Ok(())
291+
}
292+
None => Err(SpawnError::Busy),
293+
}
294+
}
295+
296+
fn spawn_named_with_trace<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
297+
let task = token.raw_task;
298+
core::mem::forget(token);
299+
300+
match task {
301+
Some(task) => {
302+
// Set the name and explicitly enable tracing
303+
task.set_name(Some(name));
304+
let task_id = task.as_ptr() as u32;
305+
task.set_id(task_id);
306+
task.set_trace_excluded(false);
307+
308+
unsafe { self.executor.spawn(task) };
309+
Ok(())
310+
}
311+
None => Err(SpawnError::Busy),
312+
}
313+
}
314+
315+
fn spawn_no_trace<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
316+
let task = token.raw_task;
317+
core::mem::forget(token);
318+
319+
match task {
320+
Some(task) => {
321+
// Explicitly exclude this task from tracing
322+
task.set_trace_excluded(true);
323+
let task_id = task.as_ptr() as u32;
324+
task.set_id(task_id);
325+
326+
unsafe { self.executor.spawn(task) };
327+
Ok(())
328+
}
329+
None => Err(SpawnError::Busy),
330+
}
331+
}
332+
333+
fn spawn_named_no_trace<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
334+
let task = token.raw_task;
335+
core::mem::forget(token);
336+
337+
match task {
338+
Some(task) => {
339+
// Set the name but exclude from tracing
340+
task.set_name(Some(name));
341+
let task_id = task.as_ptr() as u32;
342+
task.set_id(task_id);
343+
task.set_trace_excluded(true);
344+
215345
unsafe { self.executor.spawn(task) };
216346
Ok(())
217347
}
@@ -227,6 +357,26 @@ impl SpawnerTraceExt for Spawner {
227357
// When trace is disabled, just forward to regular spawn and ignore the name
228358
self.spawn(token)
229359
}
360+
361+
fn spawn_with_trace<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
362+
// When trace is disabled, just forward to regular spawn
363+
self.spawn(token)
364+
}
365+
366+
fn spawn_named_with_trace<S>(&self, _name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
367+
// When trace is disabled, just forward to regular spawn and ignore the name
368+
self.spawn(token)
369+
}
370+
371+
fn spawn_no_trace<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
372+
// When trace is disabled, just forward to regular spawn
373+
self.spawn(token)
374+
}
375+
376+
fn spawn_named_no_trace<S>(&self, _name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
377+
// When trace is disabled, just forward to regular spawn and ignore the name
378+
self.spawn(token)
379+
}
230380
}
231381

232382
/// Handle to spawn tasks into an executor from any thread.

0 commit comments

Comments
 (0)