diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-08-21 13:18:04 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-08-21 13:18:04 +0000 |
| commit | 1b9599025868d3a5d0d8e773593b05df8b2fecf2 (patch) | |
| tree | 97643e046656bdd8297b07dea9a62160a9ecce82 | |
| parent | b7d77985cf230416e53190c4edde4030e42266ed (diff) | |
| parent | 614b894ff871add9f0394fcf9ef220f133c4aae4 (diff) | |
Merge #897
897: Add support for `rtos-trace` behind a feature flag r=lulf a=quentinmit
This allows SystemView to be used to profile the behavior of the user's application.
Co-authored-by: Quentin Smith <[email protected]>
| -rw-r--r-- | embassy-executor/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-executor/src/lib.rs | 25 | ||||
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 35 | ||||
| -rw-r--r-- | embassy-macros/src/macros/cortex_m_interrupt_take.rs | 8 | ||||
| -rw-r--r-- | examples/nrf-rtos-trace/.cargo/config.toml | 9 | ||||
| -rw-r--r-- | examples/nrf-rtos-trace/Cargo.toml | 35 | ||||
| -rw-r--r-- | examples/nrf-rtos-trace/build.rs | 36 | ||||
| -rw-r--r-- | examples/nrf-rtos-trace/memory.x | 7 | ||||
| -rw-r--r-- | examples/nrf-rtos-trace/src/bin/rtos_trace.rs | 69 |
9 files changed, 227 insertions, 1 deletions
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 25c3f0abd..7d5c4a045 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -30,9 +30,13 @@ nightly = [] | |||
| 30 | 30 | ||
| 31 | integrated-timers = ["dep:embassy-time"] | 31 | integrated-timers = ["dep:embassy-time"] |
| 32 | 32 | ||
| 33 | # Trace interrupt invocations with rtos-trace. | ||
| 34 | rtos-trace-interrupt = ["rtos-trace"] | ||
| 35 | |||
| 33 | [dependencies] | 36 | [dependencies] |
| 34 | defmt = { version = "0.3", optional = true } | 37 | defmt = { version = "0.3", optional = true } |
| 35 | log = { version = "0.4.14", optional = true } | 38 | log = { version = "0.4.14", optional = true } |
| 39 | rtos-trace = { version = "0.1.2", optional = true } | ||
| 36 | 40 | ||
| 37 | futures-util = { version = "0.3.17", default-features = false } | 41 | futures-util = { version = "0.3.17", default-features = false } |
| 38 | embassy-macros = { version = "0.1.0", path = "../embassy-macros"} | 42 | embassy-macros = { version = "0.1.0", path = "../embassy-macros"} |
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 9328a7378..93f2eaa6d 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs | |||
| @@ -38,6 +38,31 @@ cfg_if::cfg_if! { | |||
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | #[doc(hidden)] | ||
| 42 | /// Implementation details for embassy macros. DO NOT USE. | ||
| 43 | pub mod export { | ||
| 44 | #[cfg(feature = "rtos-trace")] | ||
| 45 | pub use rtos_trace::trace; | ||
| 46 | |||
| 47 | /// Expands the given block of code when `embassy-executor` is compiled with | ||
| 48 | /// the `rtos-trace-interrupt` feature. | ||
| 49 | #[doc(hidden)] | ||
| 50 | #[macro_export] | ||
| 51 | #[cfg(feature = "rtos-trace-interrupt")] | ||
| 52 | macro_rules! rtos_trace_interrupt { | ||
| 53 | ($($tt:tt)*) => { $($tt)* }; | ||
| 54 | } | ||
| 55 | |||
| 56 | /// Does not expand the given block of code when `embassy-executor` is | ||
| 57 | /// compiled without the `rtos-trace-interrupt` feature. | ||
| 58 | #[doc(hidden)] | ||
| 59 | #[macro_export] | ||
| 60 | #[cfg(not(feature = "rtos-trace-interrupt"))] | ||
| 61 | macro_rules! rtos_trace_interrupt { | ||
| 62 | ($($tt:tt)*) => {}; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 41 | pub mod raw; | 66 | pub mod raw; |
| 42 | 67 | ||
| 43 | mod spawner; | 68 | mod spawner; |
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index afe67decb..c55d10699 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -26,6 +26,8 @@ use critical_section::CriticalSection; | |||
| 26 | use embassy_time::driver::{self, AlarmHandle}; | 26 | use embassy_time::driver::{self, AlarmHandle}; |
| 27 | #[cfg(feature = "integrated-timers")] | 27 | #[cfg(feature = "integrated-timers")] |
| 28 | use embassy_time::Instant; | 28 | use embassy_time::Instant; |
| 29 | #[cfg(feature = "rtos-trace")] | ||
| 30 | use rtos_trace::trace; | ||
| 29 | 31 | ||
| 30 | use self::run_queue::{RunQueue, RunQueueItem}; | 32 | use self::run_queue::{RunQueue, RunQueueItem}; |
| 31 | use self::util::UninitCell; | 33 | use self::util::UninitCell; |
| @@ -306,6 +308,9 @@ impl Executor { | |||
| 306 | /// - `task` must NOT be already enqueued (in this executor or another one). | 308 | /// - `task` must NOT be already enqueued (in this executor or another one). |
| 307 | #[inline(always)] | 309 | #[inline(always)] |
| 308 | unsafe fn enqueue(&self, cs: CriticalSection, task: NonNull<TaskHeader>) { | 310 | unsafe fn enqueue(&self, cs: CriticalSection, task: NonNull<TaskHeader>) { |
| 311 | #[cfg(feature = "rtos-trace")] | ||
| 312 | trace::task_ready_begin(task.as_ptr() as u32); | ||
| 313 | |||
| 309 | if self.run_queue.enqueue(cs, task) { | 314 | if self.run_queue.enqueue(cs, task) { |
| 310 | (self.signal_fn)(self.signal_ctx) | 315 | (self.signal_fn)(self.signal_ctx) |
| 311 | } | 316 | } |
| @@ -323,6 +328,9 @@ impl Executor { | |||
| 323 | pub(super) unsafe fn spawn(&'static self, task: NonNull<TaskHeader>) { | 328 | pub(super) unsafe fn spawn(&'static self, task: NonNull<TaskHeader>) { |
| 324 | task.as_ref().executor.set(self); | 329 | task.as_ref().executor.set(self); |
| 325 | 330 | ||
| 331 | #[cfg(feature = "rtos-trace")] | ||
| 332 | trace::task_new(task.as_ptr() as u32); | ||
| 333 | |||
| 326 | critical_section::with(|cs| { | 334 | critical_section::with(|cs| { |
| 327 | self.enqueue(cs, task); | 335 | self.enqueue(cs, task); |
| 328 | }) | 336 | }) |
| @@ -365,9 +373,15 @@ impl Executor { | |||
| 365 | return; | 373 | return; |
| 366 | } | 374 | } |
| 367 | 375 | ||
| 376 | #[cfg(feature = "rtos-trace")] | ||
| 377 | trace::task_exec_begin(p.as_ptr() as u32); | ||
| 378 | |||
| 368 | // Run the task | 379 | // Run the task |
| 369 | task.poll_fn.read()(p as _); | 380 | task.poll_fn.read()(p as _); |
| 370 | 381 | ||
| 382 | #[cfg(feature = "rtos-trace")] | ||
| 383 | trace::task_exec_end(); | ||
| 384 | |||
| 371 | // Enqueue or update into timer_queue | 385 | // Enqueue or update into timer_queue |
| 372 | #[cfg(feature = "integrated-timers")] | 386 | #[cfg(feature = "integrated-timers")] |
| 373 | self.timer_queue.update(p); | 387 | self.timer_queue.update(p); |
| @@ -381,6 +395,9 @@ impl Executor { | |||
| 381 | let next_expiration = self.timer_queue.next_expiration(); | 395 | let next_expiration = self.timer_queue.next_expiration(); |
| 382 | driver::set_alarm(self.alarm, next_expiration.as_ticks()); | 396 | driver::set_alarm(self.alarm, next_expiration.as_ticks()); |
| 383 | } | 397 | } |
| 398 | |||
| 399 | #[cfg(feature = "rtos-trace")] | ||
| 400 | trace::system_idle(); | ||
| 384 | } | 401 | } |
| 385 | 402 | ||
| 386 | /// Get a spawner that spawns tasks in this executor. | 403 | /// Get a spawner that spawns tasks in this executor. |
| @@ -426,3 +443,21 @@ unsafe fn _embassy_time_schedule_wake(at: Instant, waker: &core::task::Waker) { | |||
| 426 | let expires_at = task.expires_at.get(); | 443 | let expires_at = task.expires_at.get(); |
| 427 | task.expires_at.set(expires_at.min(at)); | 444 | task.expires_at.set(expires_at.min(at)); |
| 428 | } | 445 | } |
| 446 | |||
| 447 | #[cfg(feature = "rtos-trace")] | ||
| 448 | impl rtos_trace::RtosTraceOSCallbacks for Executor { | ||
| 449 | fn task_list() { | ||
| 450 | // We don't know what tasks exist, so we can't send them. | ||
| 451 | } | ||
| 452 | #[cfg(feature = "integrated-timers")] | ||
| 453 | fn time() -> u64 { | ||
| 454 | Instant::now().as_micros() | ||
| 455 | } | ||
| 456 | #[cfg(not(feature = "integrated-timers"))] | ||
| 457 | fn time() -> u64 { | ||
| 458 | 0 | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | #[cfg(feature = "rtos-trace")] | ||
| 463 | rtos_trace::global_os_callbacks! {Executor} | ||
diff --git a/embassy-macros/src/macros/cortex_m_interrupt_take.rs b/embassy-macros/src/macros/cortex_m_interrupt_take.rs index 9e40a56f1..f6e41bcb4 100644 --- a/embassy-macros/src/macros/cortex_m_interrupt_take.rs +++ b/embassy-macros/src/macros/cortex_m_interrupt_take.rs | |||
| @@ -19,7 +19,13 @@ pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> { | |||
| 19 | let func = HANDLER.func.load(interrupt::_export::atomic::Ordering::Relaxed); | 19 | let func = HANDLER.func.load(interrupt::_export::atomic::Ordering::Relaxed); |
| 20 | let ctx = HANDLER.ctx.load(interrupt::_export::atomic::Ordering::Relaxed); | 20 | let ctx = HANDLER.ctx.load(interrupt::_export::atomic::Ordering::Relaxed); |
| 21 | let func: fn(*mut ()) = ::core::mem::transmute(func); | 21 | let func: fn(*mut ()) = ::core::mem::transmute(func); |
| 22 | func(ctx) | 22 | ::embassy_executor::rtos_trace_interrupt! { |
| 23 | ::embassy_executor::export::trace::isr_enter(); | ||
| 24 | } | ||
| 25 | func(ctx); | ||
| 26 | ::embassy_executor::rtos_trace_interrupt! { | ||
| 27 | ::embassy_executor::export::trace::isr_exit(); | ||
| 28 | } | ||
| 23 | } | 29 | } |
| 24 | 30 | ||
| 25 | static TAKEN: interrupt::_export::atomic::AtomicBool = interrupt::_export::atomic::AtomicBool::new(false); | 31 | static TAKEN: interrupt::_export::atomic::AtomicBool = interrupt::_export::atomic::AtomicBool::new(false); |
diff --git a/examples/nrf-rtos-trace/.cargo/config.toml b/examples/nrf-rtos-trace/.cargo/config.toml new file mode 100644 index 000000000..8ca28df39 --- /dev/null +++ b/examples/nrf-rtos-trace/.cargo/config.toml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace nRF82840_xxAA with your chip as listed in `probe-run --list-chips` | ||
| 3 | runner = "probe-run --chip nRF52840_xxAA" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv7em-none-eabi" | ||
| 7 | |||
| 8 | [env] | ||
| 9 | DEFMT_LOG = "trace" | ||
diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml new file mode 100644 index 000000000..b0907f92c --- /dev/null +++ b/examples/nrf-rtos-trace/Cargo.toml | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-nrf-rtos-trace-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | |||
| 6 | [features] | ||
| 7 | default = ["log", "nightly"] | ||
| 8 | nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-nrf/unstable-traits"] | ||
| 9 | log = [ | ||
| 10 | "dep:log", | ||
| 11 | "embassy-util/log", | ||
| 12 | "embassy-executor/log", | ||
| 13 | "embassy-time/log", | ||
| 14 | "embassy-nrf/log", | ||
| 15 | ] | ||
| 16 | |||
| 17 | [dependencies] | ||
| 18 | embassy-util = { version = "0.1.0", path = "../../embassy-util" } | ||
| 19 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features=["rtos-trace", "rtos-trace-interrupt", "integrated-timers"] } | ||
| 20 | embassy-time = { version = "0.1.0", path = "../../embassy-time" } | ||
| 21 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } | ||
| 22 | |||
| 23 | cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | ||
| 24 | cortex-m-rt = "0.7.0" | ||
| 25 | panic-probe = { version = "0.3" } | ||
| 26 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||
| 27 | rand = { version = "0.8.4", default-features = false } | ||
| 28 | serde = { version = "1.0.136", default-features = false } | ||
| 29 | rtos-trace = "0.1.3" | ||
| 30 | systemview-target = { version = "0.1.2", features = ["callbacks-app", "callbacks-os", "log", "cortex-m"] } | ||
| 31 | log = { version = "0.4.17", optional = true } | ||
| 32 | |||
| 33 | [[bin]] | ||
| 34 | name = "rtos_trace" | ||
| 35 | required-features = ["nightly"] | ||
diff --git a/examples/nrf-rtos-trace/build.rs b/examples/nrf-rtos-trace/build.rs new file mode 100644 index 000000000..36cdb65a8 --- /dev/null +++ b/examples/nrf-rtos-trace/build.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | //! This build script copies the `memory.x` file from the crate root into | ||
| 2 | //! a directory where the linker can always find it at build time. | ||
| 3 | //! For many projects this is optional, as the linker always searches the | ||
| 4 | //! project root directory -- wherever `Cargo.toml` is. However, if you | ||
| 5 | //! are using a workspace or have a more complicated build setup, this | ||
| 6 | //! build script becomes required. Additionally, by requesting that | ||
| 7 | //! Cargo re-run the build script whenever `memory.x` is changed, | ||
| 8 | //! updating `memory.x` ensures a rebuild of the application with the | ||
| 9 | //! new memory settings. | ||
| 10 | |||
| 11 | use std::env; | ||
| 12 | use std::fs::File; | ||
| 13 | use std::io::Write; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | fn main() { | ||
| 17 | // Put `memory.x` in our output directory and ensure it's | ||
| 18 | // on the linker search path. | ||
| 19 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 20 | File::create(out.join("memory.x")) | ||
| 21 | .unwrap() | ||
| 22 | .write_all(include_bytes!("memory.x")) | ||
| 23 | .unwrap(); | ||
| 24 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 25 | |||
| 26 | // By default, Cargo will re-run a build script whenever | ||
| 27 | // any file in the project changes. By specifying `memory.x` | ||
| 28 | // here, we ensure the build script is only re-run when | ||
| 29 | // `memory.x` is changed. | ||
| 30 | println!("cargo:rerun-if-changed=memory.x"); | ||
| 31 | |||
| 32 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 33 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 34 | #[cfg(feature = "defmt")] | ||
| 35 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 36 | } | ||
diff --git a/examples/nrf-rtos-trace/memory.x b/examples/nrf-rtos-trace/memory.x new file mode 100644 index 000000000..9b04edec0 --- /dev/null +++ b/examples/nrf-rtos-trace/memory.x | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | MEMORY | ||
| 2 | { | ||
| 3 | /* NOTE 1 K = 1 KiBi = 1024 bytes */ | ||
| 4 | /* These values correspond to the NRF52840 with Softdevices S140 7.0.1 */ | ||
| 5 | FLASH : ORIGIN = 0x00000000, LENGTH = 1024K | ||
| 6 | RAM : ORIGIN = 0x20000000, LENGTH = 256K | ||
| 7 | } | ||
diff --git a/examples/nrf-rtos-trace/src/bin/rtos_trace.rs b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs new file mode 100644 index 000000000..7d1ad87c8 --- /dev/null +++ b/examples/nrf-rtos-trace/src/bin/rtos_trace.rs | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::task::Poll; | ||
| 6 | |||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_time::{Duration, Instant, Timer}; | ||
| 9 | #[cfg(feature = "log")] | ||
| 10 | use log::*; | ||
| 11 | use panic_probe as _; | ||
| 12 | // N.B. systemview_target cannot be used at the same time as defmt_rtt. | ||
| 13 | use rtos_trace; | ||
| 14 | use systemview_target::SystemView; | ||
| 15 | |||
| 16 | static LOGGER: systemview_target::SystemView = systemview_target::SystemView::new(); | ||
| 17 | rtos_trace::global_trace! {SystemView} | ||
| 18 | |||
| 19 | struct TraceInfo(); | ||
| 20 | |||
| 21 | impl rtos_trace::RtosTraceApplicationCallbacks for TraceInfo { | ||
| 22 | fn system_description() {} | ||
| 23 | fn sysclock() -> u32 { | ||
| 24 | 64000000 | ||
| 25 | } | ||
| 26 | } | ||
| 27 | rtos_trace::global_application_callbacks! {TraceInfo} | ||
| 28 | |||
| 29 | #[embassy_executor::task] | ||
| 30 | async fn run1() { | ||
| 31 | loop { | ||
| 32 | #[cfg(feature = "log")] | ||
| 33 | info!("DING DONG"); | ||
| 34 | #[cfg(not(feature = "log"))] | ||
| 35 | rtos_trace::trace::marker(13); | ||
| 36 | Timer::after(Duration::from_ticks(16000)).await; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn run2() { | ||
| 42 | loop { | ||
| 43 | Timer::at(Instant::from_ticks(0)).await; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | #[embassy_executor::task] | ||
| 48 | async fn run3() { | ||
| 49 | futures::future::poll_fn(|cx| { | ||
| 50 | cx.waker().wake_by_ref(); | ||
| 51 | Poll::<()>::Pending | ||
| 52 | }) | ||
| 53 | .await; | ||
| 54 | } | ||
| 55 | |||
| 56 | #[embassy_executor::main] | ||
| 57 | async fn main(spawner: Spawner) { | ||
| 58 | let _p = embassy_nrf::init(Default::default()); | ||
| 59 | LOGGER.init(); | ||
| 60 | #[cfg(feature = "log")] | ||
| 61 | { | ||
| 62 | ::log::set_logger(&LOGGER).ok(); | ||
| 63 | ::log::set_max_level(::log::LevelFilter::Trace); | ||
| 64 | } | ||
| 65 | |||
| 66 | spawner.spawn(run1()).unwrap(); | ||
| 67 | spawner.spawn(run2()).unwrap(); | ||
| 68 | spawner.spawn(run3()).unwrap(); | ||
| 69 | } | ||
