diff options
Diffstat (limited to 'embassy-executor/src')
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 12 | ||||
| -rw-r--r-- | embassy-executor/src/raw/trace.rs | 149 |
2 files changed, 161 insertions, 0 deletions
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e38a2af66..73f4f00ee 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -138,6 +138,12 @@ impl TaskRef { | |||
| 138 | pub(crate) fn as_ptr(self) -> *const TaskHeader { | 138 | pub(crate) fn as_ptr(self) -> *const TaskHeader { |
| 139 | self.ptr.as_ptr() | 139 | self.ptr.as_ptr() |
| 140 | } | 140 | } |
| 141 | |||
| 142 | /// Get the ID for a task | ||
| 143 | #[cfg(feature = "trace")] | ||
| 144 | pub fn as_id(self) -> u32 { | ||
| 145 | self.ptr.as_ptr() as u32 | ||
| 146 | } | ||
| 141 | } | 147 | } |
| 142 | 148 | ||
| 143 | /// Raw storage in which a task can be spawned. | 149 | /// Raw storage in which a task can be spawned. |
| @@ -224,6 +230,9 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 224 | // Make sure we despawn last, so that other threads can only spawn the task | 230 | // Make sure we despawn last, so that other threads can only spawn the task |
| 225 | // after we're done with it. | 231 | // after we're done with it. |
| 226 | this.raw.state.despawn(); | 232 | this.raw.state.despawn(); |
| 233 | |||
| 234 | #[cfg(feature = "trace")] | ||
| 235 | trace::task_end(self, &task); | ||
| 227 | } | 236 | } |
| 228 | Poll::Pending => {} | 237 | Poll::Pending => {} |
| 229 | } | 238 | } |
| @@ -420,6 +429,9 @@ impl SyncExecutor { | |||
| 420 | /// | 429 | /// |
| 421 | /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. | 430 | /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. |
| 422 | pub(crate) unsafe fn poll(&'static self) { | 431 | pub(crate) unsafe fn poll(&'static self) { |
| 432 | #[cfg(feature = "trace")] | ||
| 433 | trace::poll_start(self); | ||
| 434 | |||
| 423 | self.run_queue.dequeue_all(|p| { | 435 | self.run_queue.dequeue_all(|p| { |
| 424 | let task = p.header(); | 436 | let task = p.header(); |
| 425 | 437 | ||
diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index b34387b58..57222d60b 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs | |||
| @@ -1,16 +1,157 @@ | |||
| 1 | #![allow(unused)] | 1 | #![allow(unused)] |
| 2 | use crate::raw::{SyncExecutor, TaskRef}; | 2 | use crate::raw::{SyncExecutor, TaskRef}; |
| 3 | 3 | ||
| 4 | //! # Tracing | ||
| 5 | //! | ||
| 6 | //! The `trace` feature enables a number of callbacks that can be used to track the | ||
| 7 | //! lifecycle of tasks and/or executors. | ||
| 8 | //! | ||
| 9 | //! Callbacks will have one or both of the following IDs passed to them: | ||
| 10 | //! | ||
| 11 | //! 1. A `task_id`, a `u32` value unique to a task for the duration of the time it is valid | ||
| 12 | //! 2. An `executor_id`, a `u32` value unique to an executor for the duration of the time it is | ||
| 13 | //! valid | ||
| 14 | //! | ||
| 15 | //! Today, both `task_id` and `executor_id` are u32s containing the least significant 32 bits of | ||
| 16 | //! the address of the task or executor, however this is NOT a stable guarantee, and MAY change | ||
| 17 | //! at any time. | ||
| 18 | //! | ||
| 19 | //! IDs are only guaranteed to be unique for the duration of time the item is valid. If a task | ||
| 20 | //! ends, and is respond, it MAY or MAY NOT have the same ID. For tasks, this time is defined | ||
| 21 | //! as the time between `_embassy_trace_task_new` and `_embassy_trace_task_end` for a given task. | ||
| 22 | //! For executors, this time is not defined, but is often "forever" for practical embedded | ||
| 23 | //! programs. | ||
| 24 | //! | ||
| 25 | //! Callbacks can be used by enabling the `trace` feature, and providing implementations of the | ||
| 26 | //! `extern "Rust"` functions below. All callbacks must be implemented. | ||
| 27 | //! | ||
| 28 | //! ## Stability | ||
| 29 | //! | ||
| 30 | //! The `trace` interface is considered unstable. Callbacks may change, be added, or be removed | ||
| 31 | //! in any minor or trivial release. | ||
| 32 | //! | ||
| 33 | //! ## Task Tracing lifecycle | ||
| 34 | //! | ||
| 35 | //! ```text | ||
| 36 | //! ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ | ||
| 37 | //! │(1) │ | ||
| 38 | //! │ │ | ||
| 39 | //! ╔════▼════╗ (2) ┌─────────┐ (3) ┌─────────┐ │ | ||
| 40 | //! │ ║ SPAWNED ║────▶│ WAITING │────▶│ RUNNING │ | ||
| 41 | //! ╚═════════╝ └─────────┘ └─────────┘ │ | ||
| 42 | //! │ ▲ ▲ │ │ │ | ||
| 43 | //! │ (4) │ │(6) │ | ||
| 44 | //! │ │ └ ─ ─ ┘ │ │ | ||
| 45 | //! │ │ │ │ | ||
| 46 | //! │ ┌──────┐ (5) │ │ ┌─────┐ | ||
| 47 | //! │ IDLE │◀────────────────┘ └─▶│ END │ │ | ||
| 48 | //! │ └──────┘ └─────┘ | ||
| 49 | //! ┌──────────────────────┐ │ | ||
| 50 | //! └ ┤ Task Trace Lifecycle │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ | ||
| 51 | //! └──────────────────────┘ | ||
| 52 | //! ``` | ||
| 53 | //! | ||
| 54 | //! 1. A task is spawned. `_embassy_trace_task_new` is called | ||
| 55 | //! 2. A task is enqueued for the first time, `_embassy_trace_task_ready_begin` is called | ||
| 56 | //! 3. A task is polled, `_embassy_trace_task_exec_begin` is called | ||
| 57 | //! 4. WHILE a task is polled, the task is re-awoken, and `_embassy_trace_task_ready_begin` is | ||
| 58 | //! called. The task does not IMMEDIATELY move state, until polling is complete and the | ||
| 59 | //! RUNNING state is existed. `_embassy_trace_task_exec_end` is called when polling is | ||
| 60 | //! complete, marking the transition to WAITING | ||
| 61 | //! 5. Polling is complete, `_embassy_trace_task_exec_end` is called | ||
| 62 | //! 6. The task has completed, and `_embassy_trace_task_end` is called. | ||
| 63 | //! | ||
| 64 | //! ## Executor Tracing lifecycle | ||
| 65 | //! | ||
| 66 | //! ```text | ||
| 67 | //! ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ | ||
| 68 | //! │(1) │ | ||
| 69 | //! │ │ | ||
| 70 | //! ╔═══▼══╗ (2) ┌────────────┐ (3) ┌─────────┐ │ | ||
| 71 | //! │ ║ IDLE ║──────────▶│ SCHEDULING │──────▶│ POLLING │ | ||
| 72 | //! ╚══════╝ └────────────┘ └─────────┘ │ | ||
| 73 | //! │ ▲ │ ▲ │ | ||
| 74 | //! │ (5) │ │ (4) │ │ | ||
| 75 | //! │ └──────────────┘ └────────────┘ | ||
| 76 | //! ┌──────────────────────────┐ │ | ||
| 77 | //! └ ┤ Executor Trace Lifecycle │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ | ||
| 78 | //! └──────────────────────────┘ | ||
| 79 | //! ``` | ||
| 80 | //! | ||
| 81 | //! 1. The executor is started (no associated trace) | ||
| 82 | //! 2. A task on this executor is awoken. `_embassy_trace_task_ready_begin` is called | ||
| 83 | //! when this occurs, and `_embassy_trace_poll_start` is called when the executor | ||
| 84 | //! actually begins running. | ||
| 85 | //! 3. The executor has decided a task to poll. `_embassy_trace_task_exec_begin` is called. | ||
| 86 | //! 4. The executor finishes polling the task. `_embassy_trace_task_exec_end` is called. | ||
| 87 | //! 5. The executor has finished polling tasks. `_embassy_trace_executor_idle` is called. | ||
| 88 | |||
| 4 | #[cfg(not(feature = "rtos-trace"))] | 89 | #[cfg(not(feature = "rtos-trace"))] |
| 5 | extern "Rust" { | 90 | extern "Rust" { |
| 91 | /// This callback is called when the executor begins polling. This will always | ||
| 92 | /// be paired with a later call to `_embassy_trace_executor_idle`. | ||
| 93 | /// | ||
| 94 | /// This marks the EXECUTOR state transition from IDLE -> SCHEDULING. | ||
| 95 | fn _embassy_trace_poll_start(executor_id: u32); | ||
| 96 | |||
| 97 | /// This callback is called AFTER a task is initialized/allocated, and BEFORE | ||
| 98 | /// it is enqueued to run for the first time. If the task ends (and does not | ||
| 99 | /// loop "forever"), there will be a matching call to `_embassy_trace_task_end`. | ||
| 100 | /// | ||
| 101 | /// Tasks start life in the SPAWNED state. | ||
| 6 | fn _embassy_trace_task_new(executor_id: u32, task_id: u32); | 102 | fn _embassy_trace_task_new(executor_id: u32, task_id: u32); |
| 103 | |||
| 104 | /// This callback is called AFTER a task is destructed/freed. This will always | ||
| 105 | /// have a prior matching call to `_embassy_trace_task_new`. | ||
| 106 | fn _embassy_trace_task_end(executor_id: u32, task_id: u32); | ||
| 107 | |||
| 108 | /// This callback is called AFTER a task has been dequeued from the runqueue, | ||
| 109 | /// and BEFORE the task is polled. There will always be a matching call to | ||
| 110 | /// `_embassy_trace_task_exec_end`. | ||
| 111 | /// | ||
| 112 | /// This marks the TASK state transition from WAITING -> RUNNING | ||
| 113 | /// This marks the EXECUTOR state transition from SCHEDULING -> POLLING | ||
| 7 | fn _embassy_trace_task_exec_begin(executor_id: u32, task_id: u32); | 114 | fn _embassy_trace_task_exec_begin(executor_id: u32, task_id: u32); |
| 115 | |||
| 116 | /// This callback is called AFTER a task has completed polling. There will | ||
| 117 | /// always be a matching call to `_embassy_trace_task_exec_begin`. | ||
| 118 | /// | ||
| 119 | /// This marks the TASK state transition from either: | ||
| 120 | /// * RUNNING -> IDLE - if there were no `_embassy_trace_task_ready_begin` events | ||
| 121 | /// for this task since the last `_embassy_trace_task_exec_begin` for THIS task | ||
| 122 | /// * RUNNING -> WAITING - if there WAS a `_embassy_trace_task_ready_begin` event | ||
| 123 | /// for this task since the last `_embassy_trace_task_exec_begin` for THIS task | ||
| 124 | /// | ||
| 125 | /// This marks the EXECUTOR state transition from POLLING -> SCHEDULING | ||
| 8 | fn _embassy_trace_task_exec_end(excutor_id: u32, task_id: u32); | 126 | fn _embassy_trace_task_exec_end(excutor_id: u32, task_id: u32); |
| 127 | |||
| 128 | /// This callback is called AFTER the waker for a task is awoken, and BEFORE it | ||
| 129 | /// is added to the run queue. | ||
| 130 | /// | ||
| 131 | /// If the given task is currently RUNNING, this marks no state change, BUT the | ||
| 132 | /// RUNNING task will then move to the WAITING stage when polling is complete. | ||
| 133 | /// | ||
| 134 | /// If the given task is currently IDLE, this marks the TASK state transition | ||
| 135 | /// from IDLE -> WAITING. | ||
| 9 | fn _embassy_trace_task_ready_begin(executor_id: u32, task_id: u32); | 136 | fn _embassy_trace_task_ready_begin(executor_id: u32, task_id: u32); |
| 137 | |||
| 138 | /// This callback is called AFTER all dequeued tasks in a single call to poll | ||
| 139 | /// have been processed. This will always be paired with a call to | ||
| 140 | /// `_embassy_trace_executor_idle`. | ||
| 141 | /// | ||
| 142 | /// This marks the EXECUTOR state transition from | ||
| 10 | fn _embassy_trace_executor_idle(executor_id: u32); | 143 | fn _embassy_trace_executor_idle(executor_id: u32); |
| 11 | } | 144 | } |
| 12 | 145 | ||
| 13 | #[inline] | 146 | #[inline] |
| 147 | pub(crate) fn poll_start(executor: &SyncExecutor) { | ||
| 148 | #[cfg(not(feature = "rtos-trace"))] | ||
| 149 | unsafe { | ||
| 150 | _embassy_trace_poll_start(executor as *const _ as u32) | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | #[inline] | ||
| 14 | pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { | 155 | pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { |
| 15 | #[cfg(not(feature = "rtos-trace"))] | 156 | #[cfg(not(feature = "rtos-trace"))] |
| 16 | unsafe { | 157 | unsafe { |
| @@ -22,6 +163,14 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { | |||
| 22 | } | 163 | } |
| 23 | 164 | ||
| 24 | #[inline] | 165 | #[inline] |
| 166 | pub(crate) fn task_end(executor: &SyncExecutor, task: &TaskRef) { | ||
| 167 | #[cfg(not(feature = "rtos-trace"))] | ||
| 168 | unsafe { | ||
| 169 | _embassy_trace_task_end(executor as *const _ as u32, task.as_ptr() as u32) | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | #[inline] | ||
| 25 | pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) { | 174 | pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) { |
| 26 | #[cfg(not(feature = "rtos-trace"))] | 175 | #[cfg(not(feature = "rtos-trace"))] |
| 27 | unsafe { | 176 | unsafe { |
