Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "async-debugger"
version = "0.1.0"
edition = "2024"

[dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "tracing", "net" ] }
console-subscriber = "0.4.1"
3 changes: 1 addition & 2 deletions src-tauri/src/backend/core/connection_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ impl ConnectionManager {
Self::check_app_stats(&mut sys, pid).await
} {
updates_sender.send((cloned_id, Event::ApplicationUpdated(app_update))).await.ok();
} else if let Some(new_pid) = get_pid_hosting_at(url.clone()){
} else if let Ok(new_pid) = get_pid_hosting_at(url.clone()){
if new_pid != pid {
pid = new_pid;
updates_sender.send((cloned_id, Event::PidChanged(new_pid))).await.ok();
Expand Down Expand Up @@ -334,7 +334,6 @@ impl ConnectionManager {
/// Returns `None` if the process no longer exists.
async fn check_app_stats(sys: &mut System, pid: u32) -> Option<AppUpdate> {
let cpu_count = sys.cpus().len() as f32;
// println!("CPUS: {cpu_count}");
let process = sys.process(Pid::from_u32(pid))?;
let cpu_per_core = process.cpu_usage() / cpu_count;
let memory_mb = process.memory() / 1000000;
Expand Down
12 changes: 6 additions & 6 deletions src-tauri/src/backend/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,36 +148,36 @@ impl StateManager {
};

tokio::join!(task_future, resource_future, async_op_future);
println!("{:?}", warnings);
info!("{:?}", warnings);
},

Event::ApplicationUpdated(update) => {
self.state.handle_app_update(app_id, update).await;
},

Event::Connecting => {
println!("Connecting..");
info!("Connecting..");
self.state.handle_app_conn_update(app_id, ConnectionStatus::Connecting).await;
},

Event::Connected => {
println!("Connected");
info!("Connected");
self.state.handle_app_conn_update(app_id, ConnectionStatus::Connected).await;
},

Event::Disconnected => {
println!("Disconnected app");
info!("Disconnected app");
self.delete_connection(app_id).await;
self.state.handle_app_conn_update(app_id, ConnectionStatus::Disconnected).await;
},

Event::Error(err) => {
println!("Error with app connection: {err:?}");
info!("Error with app connection: {err:?}");
self.state.handle_app_conn_update(app_id, ConnectionStatus::Error(err.to_string())).await;
}

Event::PidChanged(new_pid) => {
println!("PID changed to {new_pid}");
info!("PID changed to {new_pid}");
self.state.handle_pid_changed(app_id, new_pid).await;
}
}
Expand Down
125 changes: 104 additions & 21 deletions src-tauri/src/backend/core/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ use super::database::Database;
use crate::backend::core::warnings::TaskWarnings;
use crate::backend::domain::application::{ApplicationState, ConnectionStatus};
use crate::backend::domain::async_op::{CPUOverview, TaskOp};
use crate::backend::domain::has_app_name::HasAppName;
use crate::backend::domain::resource::ResourceStatus;
use crate::backend::domain::TaskState;
use crate::backend::infra::guard::DataBaseWrite;
use crate::backend::infra::guard::{DataBaseWrite, WriteableDataBaseGuard};
use crate::backend::infra::storage::Storage;
use crate::utils::common::{
get_pid_hosting_at, rename_database_keys, rename_database_keys_and_app_name,
};
use crate::utils::common::get_pid_hosting_at;
use std::fmt::Debug;

use crate::utils::error::Error as TraceError;
use crate::{
backend::domain::{application::Application, poll::Poll, resource::Resource, Task},
Expand All @@ -23,6 +24,8 @@ use console_api::async_ops::AsyncOpUpdate;
use console_api::resources::ResourceUpdate;
use console_api::tasks::TaskUpdate;
use log::{debug, error, info, warn};
use serde::Serialize;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
Expand Down Expand Up @@ -91,7 +94,7 @@ impl State {
// Refresh PIDs for applications whose host process may have changed
let mut guard = database.applications_write().await;
for (_uuid, app) in guard.iter_mut() {
if let Some(pid) = get_pid_hosting_at(app.url().clone()) {
if let Ok(pid) = get_pid_hosting_at(app.url().clone()) {
if app.pid() != pid {
debug!("Updating the PID for app {} to {}", app.title(), pid);
app.writeable().set_pid(pid);
Expand Down Expand Up @@ -211,12 +214,12 @@ impl State {
pub async fn edit_app(&self, new_title: String, old_title: String) -> Result<(), TraceError> {
{
let mut async_guard = self.database.async_ops_write().await;
rename_database_keys(&mut async_guard, new_title.clone(), old_title.clone());
Self::rename_database_keys(&mut async_guard, new_title.clone(), old_title.clone());
}

{
let mut resource_guard = self.database.resources_write().await;
rename_database_keys_and_app_name(
Self::rename_database_keys_and_app_name(
&mut resource_guard,
new_title.clone(),
old_title.clone(),
Expand All @@ -225,7 +228,7 @@ impl State {

{
let mut tasks_guard = self.database.tasks_write().await;
rename_database_keys_and_app_name(
Self::rename_database_keys_and_app_name(
&mut tasks_guard,
new_title.clone(),
old_title.clone(),
Expand All @@ -234,7 +237,7 @@ impl State {

{
let mut tasks_ops_guard = self.database.tasks_ops_write().await;
rename_database_keys(&mut tasks_ops_guard, new_title.clone(), old_title.clone());
Self::rename_database_keys(&mut tasks_ops_guard, new_title.clone(), old_title.clone());
}

{
Expand Down Expand Up @@ -291,7 +294,7 @@ impl State {

// debug for missed task_updates
if task_update.dropped_events > 0 {
println!("missed task updates: {:?}", task_update.dropped_events);
info!("missed task updates: {:?}", task_update.dropped_events);
}

if let Some(app) = self.database.applications_read().await.get(&app_id) {
Expand Down Expand Up @@ -556,7 +559,6 @@ impl State {
if let Some(app) = apps.get_mut(&app_id) {
app.writeable().set_pid(new_pid);
}
// drop guard
}

/// Lookup the current PID for an application.
Expand Down Expand Up @@ -669,7 +671,7 @@ impl State {
) {
// debug for missed resources_updates
if resources_update.dropped_events > 0 {
println!(
info!(
"missed resources updates: {:?}",
resources_update.dropped_events
);
Expand Down Expand Up @@ -887,7 +889,7 @@ impl State {
/// task metadata (name/color) and the new CPU overview.
pub async fn handle_async_op_update(&self, app_id: Uuid, async_op_update: AsyncOpUpdate) {
if async_op_update.dropped_events > 0 {
println!(
warn!(
"missed async_op updates: {:?}",
async_op_update.dropped_events
);
Expand Down Expand Up @@ -920,15 +922,18 @@ impl State {

// Insert any new async-ops
for raw in async_op_update.new_async_ops {
if let Some(mut domain_async_op) = map_to_domain_async_op(&raw) {
let key = format!("{}.{}", app.title(), domain_async_op.resource_id);
if let Some(resource) = self.database.resources_read().await.get(&key) {
domain_async_op.resource_target = resource.target.clone();
self.database.async_ops_write().await.insert(
format!("{}.{}", app.title(), domain_async_op.id),
Arc::new(domain_async_op),
);
match map_to_domain_async_op(&raw) {
Ok(mut domain_async_op) => {
let key = format!("{}.{}", app.title(), domain_async_op.resource_id);
if let Some(resource) = self.database.resources_read().await.get(&key) {
domain_async_op.resource_target = resource.target.clone();
self.database.async_ops_write().await.insert(
format!("{}.{}", app.title(), domain_async_op.id),
Arc::new(domain_async_op),
);
}
}
Err(err) => error!("Error at inserting new async-ops, error: {err:?}"),
}
}

Expand Down Expand Up @@ -1110,6 +1115,84 @@ impl State {
}

// endregion

/// This is used when the user renames an application
///
/// Renames keys in the given database guard by replacing the `old_title` prefix
/// in keys with the `new_title` prefix. The function iterates over all keys,
/// collects the keys that start with `old_title.`, and renames them accordiungly.
///
/// # Arguments
///
/// * `guard` - A mutable reference to a writable database guard holding a `HashMap`
/// where keys are strings and values are of generic type `T`.
/// * `new_title` - The new title string to replace the old title prefix in keys.
/// * `old_title` - The old title string prefix to be replaced in keys.
///
/// # Type Parameters
///
/// * `T` - The type of the values in the hashmap. Must implement `Serialize` and `Debug`.
fn rename_database_keys<T: Serialize + Debug>(
guard: &mut WriteableDataBaseGuard<'_, HashMap<String, T>>,
new_title: String,
old_title: String,
) {
let mut changes = Vec::new();
for key in guard.keys() {
if let Some(rest) = key.strip_prefix(&format!("{}.", old_title)) {
let new_key = format!("{}.{}", new_title, rest);
changes.push((key.clone(), new_key));
}
}

for (old, new) in changes {
if let Some(val) = guard.remove(&old) {
guard.insert(new, val);
}
}
}

/// This is used when the user renames an application
///
/// Renames keys in the given database guard by replacing the `old_title` prefix
/// in keys with the `new_title` prefix. In addition, it updates the `app_name`
/// attribute of the value associated with each renamed key.
///
/// This function works on database guards containing `HashMap<String, Arc<T>>`,
/// where `T` must implement `Serialize`, `Debug`, `HasAppName`, and `Clone`.
///
/// # Arguments
///
/// * `guard` - A mutable reference to a writable database guard holding a `HashMap`
/// where keys are strings and values are `Arc` wrapped generic type `T`.
/// * `new_title` - The new title string to replace the old title prefix in keys.
/// * `old_title` - The old title string prefix to be replaced in keys.
///
/// # Type Parameters
///
/// * `T` - The type of the values inside the Arc in the hashmap. Must implement
/// Implements `Serialize`, `Debug`, `HasAppName` (a trait providing `set_app_name`), and `Clone`.
fn rename_database_keys_and_app_name<T: Serialize + Debug + HasAppName + Clone>(
guard: &mut WriteableDataBaseGuard<'_, HashMap<String, Arc<T>>>,
new_title: String,
old_title: String,
) {
let mut changes = Vec::new();
for key in guard.keys() {
if let Some(rest) = key.strip_prefix(&format!("{}.", old_title)) {
let new_key = format!("{}.{}", new_title, rest);
changes.push((key.clone(), new_key));
}
}

for (old, new) in changes {
if let Some(mut val_arc) = guard.remove(&old) {
let val = Arc::make_mut(&mut val_arc);
val.set_app_name(new_title.clone());
guard.insert(new, Arc::new(val.clone()));
}
}
}
}

#[cfg(test)]
Expand Down
5 changes: 2 additions & 3 deletions src-tauri/src/backend/domain/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ impl Application {
/// be retrieved.
pub fn new(title: String, url: Url) -> Result<Application, TraceError> {
// Find the PID of the app
let pid =
get_pid_hosting_at(url.clone()).ok_or(TraceError::PIDNotFound { url: url.clone() })?;
let pid = get_pid_hosting_at(url.clone())?;
let start_time =
get_process_start_time(pid).ok_or(TraceError::PIDNotFound { url: url.clone() })?;

Expand Down Expand Up @@ -193,7 +192,7 @@ impl Storable<HashMap<Uuid, Application>> for Application {
#[cfg(test)]
mod tests {
use super::*;
use crate::core::connection_manager::Command;
use crate::backend::core::connection_manager::Command;
use serde_json::to_string_pretty;
use std::collections::HashMap;
use std::fs;
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/backend/domain/async_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,14 @@ impl Storable<HashMap<String, TaskOp>> for TaskOp {
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::error::Error as TraceError;
use chrono::TimeZone;
use serde_json::to_string_pretty;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::Write;
use tempfile::tempdir;
use tokio;
use utils::error::Error as TraceError;

#[tokio::test]
async fn asyncop_load_all_success() {
Expand Down
12 changes: 7 additions & 5 deletions src-tauri/src/backend/mappers/async_ops.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
//! Convert from `console_api::async_ops::AsyncOp` to our domain `AsyncOp`.

use crate::backend::domain::async_op::AsyncOp;
use crate::{backend::domain::async_op::AsyncOp, utils::error::Error as TraceError};
use console_api::async_ops;

/// Maps a protobuf‐style `console_api` AsyncOp into the domain `AsyncOp`.
///
/// Returns `None` if either `id` or `resource_id` is missing.
pub fn map_to_domain_async_op(async_op: &async_ops::AsyncOp) -> Option<AsyncOp> {
/// Returns `TraceError` if either `id` or `resource_id` is missing.
pub fn map_to_domain_async_op(async_op: &async_ops::AsyncOp) -> Result<AsyncOp, TraceError> {
match (async_op.id, async_op.resource_id) {
(Some(id), Some(resource_id)) => Some(AsyncOp {
(Some(id), Some(resource_id)) => Ok(AsyncOp {
id: id.id,
resource_id: resource_id.id,
resource_target: None,
}),
_ => None,
(None, Some(_resource_id)) => Err(TraceError::IDNotFound),
(Some(_id), None) => Err(TraceError::ResourceIDNotFound),
(None, None) => Err(TraceError::IDAndResourceIDNotFound),
}
}
16 changes: 9 additions & 7 deletions src-tauri/src/features/applications.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::backend::core::StateManager;
use crate::utils::error::Error;
use log::info;
use log::{error, info};
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::sync::Arc;
Expand Down Expand Up @@ -164,11 +164,10 @@ pub async fn get_app_pid(
state_manager: TauriState<'_, Arc<StateManager>>,
uuid: Uuid,
) -> Result<u32, Error> {
state_manager
.state
.get_pid_for(uuid)
.await
.ok_or_else(|| Error::Anyhow(anyhow::anyhow!("App {uuid} not found")))
state_manager.state.get_pid_for(uuid).await.ok_or_else(|| {
error!("Error at fetching the current PID of the appliction with uuid: {uuid}");
Error::Anyhow(anyhow::anyhow!("App {uuid} not found"))
})
}

#[tauri::command]
Expand All @@ -183,7 +182,10 @@ pub async fn export_app_instance(
.export_app_instance(title, name)
.await
.map(|p| p.to_string_lossy().to_string())
.map_err(|e| format!("Export error: {}", e))
.map_err(|e| {
error!("Export error: {e}");
format!("Export error: {e}")
})
}

#[tauri::command]
Expand Down
3 changes: 2 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use env_logger as _;
use log::info;

#[tokio::main]
async fn main() {
env_logger::init();

println!("Starting");
info!("Starting");
tokio_display_lib::run().await
}
Loading