From c7f50ec3e0d06816cf8248d0b044c0597a4ff4d4 Mon Sep 17 00:00:00 2001
From: Federico Lolli <federico.lolli@skywarder.eu>
Date: Mon, 3 Mar 2025 17:53:27 +0100
Subject: [PATCH] Added profiler

---
 Cargo.lock                                  | 74 +++++++++++++++++++++
 Cargo.toml                                  |  8 +--
 src/main.rs                                 |  5 +-
 src/mavlink/base.rs                         |  1 +
 src/message_broker.rs                       |  6 +-
 src/ui/composable_view.rs                   |  7 ++
 src/ui/panes/default.rs                     |  3 +-
 src/ui/panes/messages_viewer.rs             |  1 +
 src/ui/panes/plot.rs                        |  6 +-
 src/ui/panes/plot/source_window.rs          |  3 +-
 src/ui/persistency/layout_manager.rs        |  3 +
 src/ui/persistency/layout_manager_window.rs |  1 +
 src/ui/widgets/reception_led.rs             |  1 +
 13 files changed, 105 insertions(+), 14 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 009fe97..3e2ead8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1357,6 +1357,19 @@ dependencies = [
  "slab",
 ]
 
+[[package]]
+name = "generator"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "log",
+ "rustversion",
+ "windows",
+]
+
 [[package]]
 name = "generic-array"
 version = "0.14.7"
@@ -1932,6 +1945,19 @@ version = "0.4.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
 
+[[package]]
+name = "loom"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
+dependencies = [
+ "cfg-if",
+ "generator",
+ "scoped-tls",
+ "tracing",
+ "tracing-subscriber",
+]
+
 [[package]]
 name = "mach2"
 version = "0.4.2"
@@ -2625,6 +2651,20 @@ name = "profiling"
 version = "1.0.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
+dependencies = [
+ "profiling-procmacros",
+ "tracy-client",
+]
+
+[[package]]
+name = "profiling-procmacros"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
+dependencies = [
+ "quote",
+ "syn 2.0.98",
+]
 
 [[package]]
 name = "quick-xml"
@@ -2877,6 +2917,7 @@ dependencies = [
  "egui_tiles",
  "enum_dispatch",
  "mavlink-bindgen",
+ "profiling",
  "ring-channel",
  "serde",
  "serde_json",
@@ -2888,6 +2929,7 @@ dependencies = [
  "tokio",
  "tracing",
  "tracing-subscriber",
+ "tracing-tracy",
  "uuid",
 ]
 
@@ -3448,6 +3490,38 @@ dependencies = [
  "tracing-log",
 ]
 
+[[package]]
+name = "tracing-tracy"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eaa1852afa96e0fe9e44caa53dc0bd2d9d05e0f2611ce09f97f8677af56e4ba"
+dependencies = [
+ "tracing-core",
+ "tracing-subscriber",
+ "tracy-client",
+]
+
+[[package]]
+name = "tracy-client"
+version = "0.17.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73202d787346a5418f8222eddb5a00f29ea47caf3c7d38a8f2f69f8455fa7c7e"
+dependencies = [
+ "loom",
+ "once_cell",
+ "tracy-client-sys",
+]
+
+[[package]]
+name = "tracy-client-sys"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f"
+dependencies = [
+ "cc",
+ "windows-targets 0.52.6",
+]
+
 [[package]]
 name = "ttf-parser"
 version = "0.25.1"
diff --git a/Cargo.toml b/Cargo.toml
index 6f2eb0e..60bd536 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,11 +16,7 @@ egui_extras = "0.31"
 egui_plot = "0.31"
 egui_file = "0.22"
 # =========== Asynchronous ===========
-tokio = { version = "1.41", features = [
-    "rt-multi-thread",
-    "net",
-    "sync",
-] }
+tokio = { version = "1.41", features = ["rt-multi-thread", "net", "sync"] }
 # =========== Mavlink ===========
 skyward_mavlink = { git = "https://git.skywarder.eu/avn/swd/mavlink/mavlink-skyward-lib.git", branch = "rust-strum", features = [
     "reflection",
@@ -44,3 +40,5 @@ anyhow = "1.0"
 ring-channel = "0.12.0"
 thiserror = "2.0.7"
 uuid = { version = "1.12.1", features = ["serde", "v7"] }
+profiling = { version = "1.0", features = ["profile-with-tracy"] }
+tracing-tracy = "0.11.4"
diff --git a/src/main.rs b/src/main.rs
index e0c0555..1182687 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,15 +4,15 @@
 
 mod error;
 mod mavlink;
-mod serial;
 mod message_broker;
+mod serial;
 mod ui;
 mod utils;
 
 use std::sync::LazyLock;
 
 use tokio::runtime::Runtime;
-use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer};
+use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt};
 
 use error::ErrInstrument;
 use mavlink::ReflectionContext;
@@ -28,6 +28,7 @@ fn main() -> Result<(), eframe::Error> {
     let env_filter = EnvFilter::builder().from_env_lossy();
     tracing_subscriber::registry()
         .with(tracing_subscriber::fmt::layer().with_filter(env_filter))
+        .with(tracing_tracy::TracyLayer::default())
         .init();
 
     // Start Tokio runtime (TODO: decide whether to use Tokio or a simpler thread-based approach)
diff --git a/src/mavlink/base.rs b/src/mavlink/base.rs
index 0638719..c10d9fe 100644
--- a/src/mavlink/base.rs
+++ b/src/mavlink/base.rs
@@ -38,6 +38,7 @@ impl TimedMessage {
 }
 
 /// Extract fields from a MavLink message using string keys
+#[profiling::function]
 pub fn extract_from_message<K, T>(
     message: &MavMessage,
     fields: impl IntoIterator<Item = K>,
diff --git a/src/message_broker.rs b/src/message_broker.rs
index dd3c7dc..77932cf 100644
--- a/src/message_broker.rs
+++ b/src/message_broker.rs
@@ -12,18 +12,18 @@ use reception_queue::ReceptionQueue;
 
 use crate::{
     error::ErrInstrument,
-    mavlink::{byte_parser, Message, TimedMessage},
+    mavlink::{Message, TimedMessage, byte_parser},
     utils::RingBuffer,
 };
 use anyhow::{Context, Result};
-use ring_channel::{ring_channel, RingReceiver, RingSender};
+use ring_channel::{RingReceiver, RingSender, ring_channel};
 use std::{
     collections::HashMap,
     io::Write,
     num::NonZeroUsize,
     sync::{
-        atomic::{AtomicBool, Ordering},
         Arc, Mutex,
+        atomic::{AtomicBool, Ordering},
     },
     time::{Duration, Instant},
 };
diff --git a/src/ui/composable_view.rs b/src/ui/composable_view.rs
index 4abda19..a97e1fd 100644
--- a/src/ui/composable_view.rs
+++ b/src/ui/composable_view.rs
@@ -82,6 +82,7 @@ impl eframe::App for ComposableView {
             match action {
                 PaneAction::SplitH => {
                     if let Some(hovered_tile) = hovered_pane {
+                        profiling::scope!("split_h");
                         if self.maximized_pane.is_none() {
                             debug!("Called SplitH on tile {:?}", hovered_tile);
                             let hovered_tile_pane = panes_tree
@@ -102,6 +103,7 @@ impl eframe::App for ComposableView {
                     }
                 }
                 PaneAction::SplitV => {
+                    profiling::scope!("split_v");
                     if self.maximized_pane.is_none() {
                         if let Some(hovered_tile) = hovered_pane {
                             debug!("Called SplitV on tile {:?}", hovered_tile);
@@ -240,6 +242,9 @@ impl eframe::App for ComposableView {
             self.behavior.action = Some(action);
         }
 
+        // Used for the profiler
+        profiling::finish_frame!();
+
         // UNCOMMENT THIS TO ENABLE CONTINOUS MODE
         // ctx.request_repaint();
     }
@@ -285,6 +290,7 @@ impl ComposableView {
     }
 
     /// Retrieves new messages from the message broker and dispatches them to the panes.
+    #[profiling::function]
     fn process_messages(&mut self) {
         let start = Instant::now();
 
@@ -398,6 +404,7 @@ struct SourceWindow {
 }
 
 impl SourceWindow {
+    #[profiling::function]
     fn show_window(&mut self, ui: &mut egui::Ui, message_broker: &mut MessageBroker) {
         let mut window_is_open = self.visible;
         let mut can_be_closed = false;
diff --git a/src/ui/panes/default.rs b/src/ui/panes/default.rs
index 7fc0636..0507d4d 100644
--- a/src/ui/panes/default.rs
+++ b/src/ui/panes/default.rs
@@ -4,7 +4,7 @@ use tracing::debug;
 
 use crate::ui::{
     composable_view::{PaneAction, PaneResponse},
-    utils::{vertically_centered, SizingMemo},
+    utils::{SizingMemo, vertically_centered},
 };
 
 #[derive(Clone, Debug, Default, Serialize, Deserialize)]
@@ -22,6 +22,7 @@ impl PartialEq for DefaultPane {
 }
 
 impl PaneBehavior for DefaultPane {
+    #[profiling::function]
     fn ui(&mut self, ui: &mut egui::Ui, tile_id: egui_tiles::TileId) -> PaneResponse {
         let mut response = PaneResponse::default();
 
diff --git a/src/ui/panes/messages_viewer.rs b/src/ui/panes/messages_viewer.rs
index 0925854..f1f63a9 100644
--- a/src/ui/panes/messages_viewer.rs
+++ b/src/ui/panes/messages_viewer.rs
@@ -18,6 +18,7 @@ impl PartialEq for MessagesViewerPane {
 }
 
 impl PaneBehavior for MessagesViewerPane {
+    #[profiling::function]
     fn ui(&mut self, ui: &mut egui::Ui, _tile_id: egui_tiles::TileId) -> PaneResponse {
         let mut response = PaneResponse::default();
         let label = ui.add_sized(ui.available_size(), Label::new("This is a label"));
diff --git a/src/ui/panes/plot.rs b/src/ui/panes/plot.rs
index a7c4b5a..9433ed5 100644
--- a/src/ui/panes/plot.rs
+++ b/src/ui/panes/plot.rs
@@ -2,14 +2,14 @@ mod source_window;
 
 use super::PaneBehavior;
 use crate::{
-    mavlink::{extract_from_message, MessageData, TimedMessage, ROCKET_FLIGHT_TM_DATA},
+    mavlink::{MessageData, ROCKET_FLIGHT_TM_DATA, TimedMessage, extract_from_message},
     ui::composable_view::PaneResponse,
 };
 use egui::{Color32, Vec2b};
 use egui_plot::{Legend, Line, PlotPoints};
 use egui_tiles::TileId;
 use serde::{Deserialize, Serialize};
-use source_window::{sources_window, SourceSettings};
+use source_window::{SourceSettings, sources_window};
 use std::iter::zip;
 
 #[derive(Clone, Default, Debug, Serialize, Deserialize)]
@@ -37,6 +37,7 @@ impl PartialEq for Plot2DPane {
 }
 
 impl PaneBehavior for Plot2DPane {
+    #[profiling::function]
     fn ui(&mut self, ui: &mut egui::Ui, _: TileId) -> PaneResponse {
         let mut response = PaneResponse::default();
 
@@ -86,6 +87,7 @@ impl PaneBehavior for Plot2DPane {
         self.contains_pointer
     }
 
+    #[profiling::function]
     fn update(&mut self, messages: &[TimedMessage]) {
         if !self.state_valid {
             self.line_data.clear();
diff --git a/src/ui/panes/plot/source_window.rs b/src/ui/panes/plot/source_window.rs
index d68fa93..f6def54 100644
--- a/src/ui/panes/plot/source_window.rs
+++ b/src/ui/panes/plot/source_window.rs
@@ -1,12 +1,13 @@
 use crate::{
-    mavlink::{MavMessage, Message},
     MAVLINK_PROFILE,
+    mavlink::{MavMessage, Message},
 };
 
 use crate::error::ErrInstrument;
 
 use super::{LineSettings, MsgSources};
 
+#[profiling::function]
 pub fn sources_window(ui: &mut egui::Ui, plot_settings: &mut SourceSettings) {
     // extract the msg name from the id to show it in the combo box
     let msg_name = MAVLINK_PROFILE
diff --git a/src/ui/persistency/layout_manager.rs b/src/ui/persistency/layout_manager.rs
index 173fea1..c1a0eec 100644
--- a/src/ui/persistency/layout_manager.rs
+++ b/src/ui/persistency/layout_manager.rs
@@ -58,6 +58,7 @@ impl LayoutManager {
     }
 
     /// Scans the layout directory and reloads the layouts
+    #[profiling::function]
     pub fn reload_layouts(&mut self) {
         if let Ok(files) = self.layouts_path.read_dir() {
             trace!("Reloading layouts from {:?}", self.layouts_path);
@@ -85,6 +86,7 @@ impl LayoutManager {
         self.layouts.get(&name.into())
     }
 
+    #[profiling::function]
     pub fn save_layout(&mut self, name: &str, state: &ComposableViewState) -> anyhow::Result<()> {
         let path = self.layouts_path.join(name).with_extension("json");
         state.to_file(&path)?;
@@ -92,6 +94,7 @@ impl LayoutManager {
         Ok(())
     }
 
+    #[profiling::function]
     pub fn load_layout(
         &mut self,
         path: impl AsRef<Path>,
diff --git a/src/ui/persistency/layout_manager_window.rs b/src/ui/persistency/layout_manager_window.rs
index b3cdbdc..0cd38bf 100644
--- a/src/ui/persistency/layout_manager_window.rs
+++ b/src/ui/persistency/layout_manager_window.rs
@@ -34,6 +34,7 @@ impl LayoutManagerWindow {
         }
     }
 
+    #[profiling::function]
     pub fn show(
         &mut self,
         ctx: &Context,
diff --git a/src/ui/widgets/reception_led.rs b/src/ui/widgets/reception_led.rs
index 5ff9e90..4c064ea 100644
--- a/src/ui/widgets/reception_led.rs
+++ b/src/ui/widgets/reception_led.rs
@@ -48,6 +48,7 @@ impl ReceptionLed {
 }
 
 impl Widget for ReceptionLed {
+    #[profiling::function]
     fn ui(self, ui: &mut Ui) -> Response {
         ui.horizontal(|ui| {
             ui.label("Receiving at:");
-- 
GitLab