diff --git a/README.md b/README.md index bba4b3b..9fa440f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # WORK IN PROGRESS - ETA: December 25th, 2022 ![banner.png](https://github.com/hinto-janaiyo/gupax/blob/main/images/banner.png) -Gupax (*guh-picks*) is a (Windows|macOS|Linux) GUI for mining [**Monero**](https://github.com/monero-project/monero) on [**P2Pool**](https://github.com/SChernykh/p2pool), using [**XMRig**](https://github.com/xmrig/xmrig). +Gupax is a (Windows|macOS|Linux) GUI for mining [**Monero**](https://github.com/monero-project/monero) on [**P2Pool**](https://github.com/SChernykh/p2pool), using [**XMRig**](https://github.com/xmrig/xmrig). **If you just want to see a short 1-minute video on how to download and run Gupax: [click here.](#Video)** diff --git a/src/gupax.rs b/src/gupax.rs index 8cdc6e5..6fecf39 100644 --- a/src/gupax.rs +++ b/src/gupax.rs @@ -83,6 +83,7 @@ pub enum Ratio { impl Gupax { pub fn show(&mut self, og: &Arc>, state_path: &Path, update: &Arc>, file_window: &Arc>, error_state: &mut ErrorState, restart: &Arc>, width: f32, height: f32, frame: &mut eframe::Frame, ctx: &egui::Context, ui: &mut egui::Ui) { // Update button + Progress bar + debug!("Gupax Tab | Rendering [Update] button + progress bar"); ui.group(|ui| { // These are in unnecessary [ui.vertical()]'s // because I need to use [ui.set_enabled]s, but I can't @@ -112,6 +113,7 @@ impl Gupax { }); }); + debug!("Gupax Tab | Rendering bool buttons"); ui.horizontal(|ui| { ui.group(|ui| { let width = (width - SPACE*12.0)/6.0; @@ -133,6 +135,7 @@ impl Gupax { if self.simple { return } + debug!("Gupax Tab | Rendering P2Pool/XMRig path selection"); // P2Pool/XMRig binary path selection ui.add_space(SPACE); ui.style_mut().override_text_style = Some(Monospace); @@ -182,6 +185,7 @@ impl Gupax { drop(guard); // Gupax App resolution sliders + debug!("Gupax Tab | Rendering resolution sliders"); ui.vertical(|ui| { let width = width/10.0; ui.spacing_mut().icon_width = width / 25.0; @@ -229,6 +233,7 @@ impl Gupax { } })}); // Saved [Tab] + debug!("Gupax Tab | Rendering [Tab] selector"); ui.group(|ui| { let height = ui.available_height()/1.85; let width = (width/5.0)-(SPACE*1.93); diff --git a/src/helper.rs b/src/helper.rs index c52ee16..02be2b9 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -251,6 +251,7 @@ impl Helper { // The actual [String] holds 56_000_000, but this allows for some leeway so it doesn't allocate more memory. // This will also append a message showing it was reset. fn check_reset_output_full(output_full: &Arc>, name: ProcessName) { + debug!("{} Watchdog | Resetting [output_full]...", name); let mut output_full = output_full.lock().unwrap(); if output_full.len() > PROCESS_OUTPUT_LEEWAY { info!("{} | Output is nearing {} bytes, resetting!", MAX_PROCESS_OUTPUT_BYTES, name); @@ -258,10 +259,12 @@ impl Helper { output_full.clear(); output_full.push_str(&text); } + debug!("{} Watchdog | Resetting [output_full] ... OK", name); } // For the GUI thread fn check_reset_gui_p2pool_output(gui_api: &Arc>) { + debug!("P2Pool Watchdog | Resetting GUI output..."); let mut gui_api = gui_api.lock().unwrap(); if gui_api.output.len() > GUI_OUTPUT_LEEWAY { info!("P2Pool | Output is nearing {} bytes, resetting!", MAX_GUI_OUTPUT_BYTES); @@ -269,9 +272,11 @@ impl Helper { gui_api.output.clear(); gui_api.output.push_str(&text); } + debug!("P2Pool Watchdog | Resetting GUI output ... OK"); } fn check_reset_gui_xmrig_output(gui_api: &Arc>) { + debug!("XMRig Watchdog | Resetting GUI output..."); let mut gui_api = gui_api.lock().unwrap(); if gui_api.output.len() > GUI_OUTPUT_LEEWAY { info!("XMRig | Output is nearing {} bytes, resetting!", MAX_GUI_OUTPUT_BYTES); @@ -279,6 +284,7 @@ impl Helper { gui_api.output.clear(); gui_api.output.push_str(&text); } + debug!("XMRig Watchdog | Resetting GUI output ... OK"); } //---------------------------------------------------------------------------------------------------- P2Pool specific @@ -306,6 +312,7 @@ impl Helper { thread::sleep(SECOND); } // Ok, process is not alive, start the new one! + info!("P2Pool | Old process seems dead, starting new one!"); Self::start_p2pool(&helper, &state, &path); }); info!("P2Pool | Restart ... OK"); @@ -418,6 +425,7 @@ impl Helper { // The P2Pool watchdog. Spawns 1 OS thread for reading a PTY (STDOUT+STDERR), and combines the [Child] with a PTY so STDIN actually works. fn spawn_p2pool_watchdog(process: Arc>, gui_api: Arc>, pub_api: Arc>, priv_api: Arc>, args: Vec, mut path: std::path::PathBuf) { // 1a. Create PTY + debug!("P2Pool | Creating PTY..."); let pty = portable_pty::native_pty_system(); let pair = pty.openpty(portable_pty::PtySize { rows: 100, @@ -426,13 +434,16 @@ impl Helper { pixel_height: 0, }).unwrap(); // 1b. Create command + debug!("P2Pool | Creating command..."); let mut cmd = portable_pty::CommandBuilder::new(path.as_path()); cmd.args(args); cmd.cwd(path.as_path().parent().unwrap()); // 1c. Create child + debug!("P2Pool | Creating child..."); let child_pty = Arc::new(Mutex::new(pair.slave.spawn_command(cmd).unwrap())); // 2. Set process state + debug!("P2Pool | Setting process state..."); let mut lock = process.lock().unwrap(); lock.state = ProcessState::Alive; lock.signal = ProcessSignal::None; @@ -443,6 +454,7 @@ impl Helper { drop(lock); // 3. Spawn PTY read thread + debug!("P2Pool | Spawning PTY read thread..."); let output_full = Arc::clone(&process.lock().unwrap().output_full); let output_buf = Arc::clone(&process.lock().unwrap().output_buf); thread::spawn(move || { @@ -453,6 +465,8 @@ impl Helper { path.pop(); path.push(P2POOL_API_PATH); + + debug!("P2Pool | Cleaning old API files..."); // Attempt to remove stale API file match std::fs::remove_file(&path) { Ok(_) => info!("P2Pool | Attempting to remove stale API file ... OK"), @@ -475,9 +489,27 @@ impl Helper { loop { // Set timer let now = Instant::now(); + debug!("P2Pool Watchdog | ----------- Start of loop -----------"); + + // Check if the process is secretly died without us knowing :) + if let Ok(Some(code)) = child_pty.lock().unwrap().try_wait() { + debug!("P2Pool Watchdog | Process secretly died! Getting exit status"); + let exit_status = match code.success() { + true => { process.lock().unwrap().state = ProcessState::Dead; "Successful" }, + false => { process.lock().unwrap().state = ProcessState::Failed; "Failed" }, + }; + let uptime = HumanTime::into_human(start.elapsed()); + info!("P2Pool Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); + // This is written directly into the GUI, because sometimes the 900ms event loop can't catch it. + writeln!(gui_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE); + process.lock().unwrap().signal = ProcessSignal::None; + debug!("P2Pool Watchdog | Secret dead process reap OK, breaking"); + break + } // Check SIGNAL if process.lock().unwrap().signal == ProcessSignal::Stop { + debug!("P2Pool Watchdog | Stop SIGNAL caught"); child_pty.lock().unwrap().kill(); // This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool) // Wait to get the exit status let exit_status = match child_pty.lock().unwrap().wait() { @@ -491,13 +523,15 @@ impl Helper { _ => { process.lock().unwrap().state = ProcessState::Failed; "Unknown Error" }, }; let uptime = HumanTime::into_human(start.elapsed()); - info!("P2Pool | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); + info!("P2Pool Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. writeln!(gui_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE); process.lock().unwrap().signal = ProcessSignal::None; + debug!("P2Pool Watchdog | Stop SIGNAL done, breaking"); break // Check RESTART } else if process.lock().unwrap().signal == ProcessSignal::Restart { + debug!("P2Pool Watchdog | Restart SIGNAL caught"); child_pty.lock().unwrap().kill(); // This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool) // Wait to get the exit status let exit_status = match child_pty.lock().unwrap().wait() { @@ -505,22 +539,11 @@ impl Helper { _ => "Unknown Error", }; let uptime = HumanTime::into_human(start.elapsed()); - info!("P2Pool | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); + info!("P2Pool Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. writeln!(gui_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE); process.lock().unwrap().state = ProcessState::Waiting; - break - // Check if the process is secretly died without us knowing :) - } else if let Ok(Some(code)) = child_pty.lock().unwrap().try_wait() { - let exit_status = match code.success() { - true => { process.lock().unwrap().state = ProcessState::Dead; "Successful" }, - false => { process.lock().unwrap().state = ProcessState::Failed; "Failed" }, - }; - let uptime = HumanTime::into_human(start.elapsed()); - info!("P2Pool | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); - // This is written directly into the GUI, because sometimes the 900ms event loop can't catch it. - writeln!(gui_api.lock().unwrap().output, "{}\nP2Pool stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE); - process.lock().unwrap().signal = ProcessSignal::None; + debug!("P2Pool Watchdog | Restart SIGNAL done, breaking"); break } @@ -529,15 +552,18 @@ impl Helper { if !lock.input.is_empty() { let input = std::mem::take(&mut lock.input); for line in input { + debug!("P2Pool Watchdog | User input not empty, writing to STDIN: [{}]", line); writeln!(lock.stdin.as_mut().unwrap(), "{}", line); } } drop(lock); // Always update from output + debug!("P2Pool Watchdog | Starting [update_from_output()]"); PubP2poolApi::update_from_output(&pub_api, &output_full, &output_buf, start.elapsed(), ®ex); // Read API file into string + debug!("P2Pool Watchdog | Attempting API file read"); if let Ok(string) = PrivP2poolApi::read_p2pool_api(&path) { // Deserialize if let Ok(s) = PrivP2poolApi::str_to_priv_p2pool_api(&string) { @@ -547,17 +573,24 @@ impl Helper { } // Check if logs need resetting + debug!("P2Pool Watchdog | Attempting log reset check"); Self::check_reset_output_full(&output_full, ProcessName::P2pool); Self::check_reset_gui_p2pool_output(&gui_api); // Sleep (only if 900ms hasn't passed) let elapsed = now.elapsed().as_millis(); // Since logic goes off if less than 1000, casting should be safe - if elapsed < 900 { std::thread::sleep(std::time::Duration::from_millis((900-elapsed) as u64)); } + if elapsed < 900 { + let sleep = (900-elapsed) as u64; + debug!("P2Pool Watchdog | END OF LOOP - Sleeping for [{}]ms...", sleep); + std::thread::sleep(std::time::Duration::from_millis(sleep)); + } else { + debug!("P2Pool Watchdog | END OF LOOP - Not sleeping!"); + } } // 5. If loop broke, we must be done here. - info!("P2Pool | Watchdog thread exiting... Goodbye!"); + info!("P2Pool Watchdog | Watchdog thread exiting... Goodbye!"); } //---------------------------------------------------------------------------------------------------- XMRig specific, most functions are very similar to P2Pool's @@ -620,6 +653,7 @@ impl Helper { thread::sleep(SECOND); } // Ok, process is not alive, start the new one! + info!("XMRig | Old process seems dead, starting new one!"); Self::start_xmrig(&helper, &state, &path, sudo); }); info!("XMRig | Restart ... OK"); @@ -745,6 +779,7 @@ impl Helper { #[tokio::main] async fn spawn_xmrig_watchdog(process: Arc>, gui_api: Arc>, pub_api: Arc>, priv_api: Arc>, args: Vec, mut path: std::path::PathBuf, sudo: Arc>, api_ip_port: String) { // 1a. Create PTY + debug!("XMRig | Creating PTY..."); let pty = portable_pty::native_pty_system(); let mut pair = pty.openpty(portable_pty::PtySize { rows: 100, @@ -753,15 +788,18 @@ impl Helper { pixel_height: 0, }).unwrap(); // 1b. Create command + debug!("XMRig | Creating command..."); #[cfg(target_os = "windows")] let cmd = Self::create_xmrig_cmd_windows(args, path); #[cfg(target_family = "unix")] let cmd = Self::create_xmrig_cmd_unix(args, path); // 1c. Create child + debug!("XMRig | Creating child..."); let child_pty = Arc::new(Mutex::new(pair.slave.spawn_command(cmd).unwrap())); // 2. Input [sudo] pass, wipe, then drop. if cfg!(unix) { + debug!("XMRig | Inputting [sudo] and wiping..."); // 1d. Sleep to wait for [sudo]'s non-echo prompt (on Unix). // this prevents users pass from showing up in the STDOUT. std::thread::sleep(std::time::Duration::from_secs(3)); @@ -770,6 +808,7 @@ impl Helper { } // 3. Set process state + debug!("XMRig | Setting process state..."); let mut lock = process.lock().unwrap(); lock.state = ProcessState::Alive; lock.signal = ProcessSignal::None; @@ -780,6 +819,7 @@ impl Helper { drop(lock); // 4. Spawn PTY read thread + debug!("XMRig | Spawning PTY read thread..."); let output_full = Arc::clone(&process.lock().unwrap().output_full); let output_buf = Arc::clone(&process.lock().unwrap().output_buf); thread::spawn(move || { @@ -796,9 +836,11 @@ impl Helper { loop { // Set timer let now = Instant::now(); + debug!("XMRig Watchdog | ----------- Start of loop -----------"); // Check if the process secretly died without us knowing :) if let Ok(Some(code)) = child_pty.lock().unwrap().try_wait() { + debug!("XMRig Watchdog | Process secretly died on us! Getting exit status..."); let exit_status = match code.success() { true => { process.lock().unwrap().state = ProcessState::Dead; "Successful" }, false => { process.lock().unwrap().state = ProcessState::Failed; "Failed" }, @@ -807,12 +849,14 @@ impl Helper { info!("XMRig | Stopped ... Uptime was: [{}], Exit status: [{}]", uptime, exit_status); writeln!(gui_api.lock().unwrap().output, "{}\nXMRig stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE); process.lock().unwrap().signal = ProcessSignal::None; + debug!("XMRig Watchdog | Secret dead process reap OK, breaking"); break } // Stop on [Stop/Restart] SIGNAL let signal = process.lock().unwrap().signal; if signal == ProcessSignal::Stop || signal == ProcessSignal::Restart { + debug!("XMRig Watchdog | Stop/Restart SIGNAL caught"); // macOS requires [sudo] again to kill [XMRig] if cfg!(target_os = "macos") { // If we're at this point, that means the user has @@ -851,6 +895,7 @@ impl Helper { ProcessSignal::Restart => process.state = ProcessState::Waiting, _ => (), } + debug!("XMRig Watchdog | Stop/Restart SIGNAL done, breaking"); break } @@ -859,33 +904,44 @@ impl Helper { if !lock.input.is_empty() { let input = std::mem::take(&mut lock.input); for line in input { + debug!("XMRig Watchdog | User input not empty, writing to STDIN: [{}]", line); writeln!(lock.stdin.as_mut().unwrap(), "{}", line); } } drop(lock); // Always update from output + debug!("XMRig Watchdog | Starting [update_from_output()]"); PubXmrigApi::update_from_output(&pub_api, &output_buf, start.elapsed()); // Send an HTTP API request + debug!("XMRig Watchdog | Attempting HTTP API request..."); if let Ok(priv_api) = PrivXmrigApi::request_xmrig_api(client.clone(), &api_ip_port).await { + debug!("XMRig Watchdog | HTTP API request OK, attempting [update_from_priv()]"); PubXmrigApi::update_from_priv(&pub_api, priv_api); } else { - warn!("XMRig | Could not send HTTP API request to: {}", api_ip_port); + warn!("XMRig Watchdog | Could not send HTTP API request to: {}", api_ip_port); } // Check if logs need resetting + debug!("XMRig Watchdog | Attempting log reset check"); Self::check_reset_output_full(&output_full, ProcessName::Xmrig); Self::check_reset_gui_xmrig_output(&gui_api); // Sleep (only if 900ms hasn't passed) let elapsed = now.elapsed().as_millis(); // Since logic goes off if less than 1000, casting should be safe - if elapsed < 900 { std::thread::sleep(std::time::Duration::from_millis((900-elapsed) as u64)); } + if elapsed < 900 { + let sleep = (900-elapsed) as u64; + debug!("XMRig Watchdog | END OF LOOP - Sleeping for [{}]ms...", sleep); + std::thread::sleep(std::time::Duration::from_millis(sleep)); + } else { + debug!("XMRig Watchdog | END OF LOOP - Not sleeping!"); + } } // 5. If loop broke, we must be done here. - info!("XMRig | Watchdog thread exiting... Goodbye!"); + info!("XMRig Watchdog | Watchdog thread exiting... Goodbye!"); } //---------------------------------------------------------------------------------------------------- The "helper" @@ -933,38 +989,56 @@ impl Helper { loop { // 1. Loop init timestamp let start = Instant::now(); + debug!("Helper | ----------- Start of loop -----------"); + + // + // Ignore the invasive [debug!()] messages on the right side of the code. + // The reason why they are there are so that it's extremely easy to track + // down the culprit of an [Arc] deadlock. I know, they're ugly. + // // 2. Lock... EVERYTHING! - let mut lock = helper.lock().unwrap(); + let mut lock = helper.lock().unwrap(); debug!("Helper | Locking (1/8) ... [helper]"); // Calculate Gupax's uptime always. lock.uptime = HumanTime::into_human(lock.instant.elapsed()); - let mut gui_api_p2pool = lock.gui_api_p2pool.lock().unwrap(); - let mut gui_api_xmrig = lock.gui_api_xmrig.lock().unwrap(); - let mut pub_api_p2pool = lock.pub_api_p2pool.lock().unwrap(); - let mut pub_api_xmrig = lock.pub_api_xmrig.lock().unwrap(); - let p2pool = lock.p2pool.lock().unwrap(); - let xmrig = lock.xmrig.lock().unwrap(); - let mut lock_pub_sys = pub_sys.lock().unwrap(); + let mut gui_api_p2pool = lock.gui_api_p2pool.lock().unwrap(); debug!("Helper | Locking (2/8) ... [gui_api_p2pool]"); + let mut gui_api_xmrig = lock.gui_api_xmrig.lock().unwrap(); debug!("Helper | Locking (3/8) ... [gui_api_xmrig]"); + let mut pub_api_p2pool = lock.pub_api_p2pool.lock().unwrap(); debug!("Helper | Locking (4/8) ... [pub_api_p2pool]"); + let mut pub_api_xmrig = lock.pub_api_xmrig.lock().unwrap(); debug!("Helper | Locking (5/8) ... [pub_api_xmrig]"); + let p2pool = lock.p2pool.lock().unwrap(); debug!("Helper | Locking (6/8) ... [p2pool]"); + let xmrig = lock.xmrig.lock().unwrap(); debug!("Helper | Locking (7/8) ... [xmrig]"); + let mut lock_pub_sys = pub_sys.lock().unwrap(); debug!("Helper | Locking (8/8) ... [pub_sys]"); // If [P2Pool] is alive... - if p2pool.is_alive() { PubP2poolApi::combine_gui_pub_api(&mut gui_api_p2pool, &mut pub_api_p2pool); } + if p2pool.is_alive() { + debug!("Helper | P2Pool is alive! Running [combine_gui_pub_api()]"); + PubP2poolApi::combine_gui_pub_api(&mut gui_api_p2pool, &mut pub_api_p2pool); + } else { + debug!("Helper | P2Pool is dead! Skipping..."); + } // If [XMRig] is alive... - if xmrig.is_alive() { PubXmrigApi::combine_gui_pub_api(&mut gui_api_xmrig, &mut pub_api_xmrig); } + if xmrig.is_alive() { + debug!("Helper | XMRig is alive! Running [combine_gui_pub_api()]"); + PubXmrigApi::combine_gui_pub_api(&mut gui_api_xmrig, &mut pub_api_xmrig); + } else { + debug!("Helper | XMRig is dead! Skipping..."); + } // 2. Selectively refresh [sysinfo] for only what we need (better performance). - sysinfo.refresh_cpu_specifics(sysinfo_cpu); - sysinfo.refresh_processes_specifics(sysinfo_processes); - sysinfo.refresh_memory(); + sysinfo.refresh_cpu_specifics(sysinfo_cpu); debug!("Helper | Sysinfo refresh (1/3) ... [cpu]"); + sysinfo.refresh_processes_specifics(sysinfo_processes); debug!("Helper | Sysinfo refresh (2/3) ... [processes]"); + sysinfo.refresh_memory(); debug!("Helper | Sysinfo refresh (3/3) ... [memory]"); + debug!("Helper | Sysinfo OK, running [update_pub_sys_from_sysinfo()]"); Self::update_pub_sys_from_sysinfo(&sysinfo, &mut lock_pub_sys, &pid, &lock, max_threads); // 3. Drop... (almost) EVERYTHING... IN REVERSE! - drop(lock_pub_sys); - drop(xmrig); - drop(p2pool); - drop(pub_api_xmrig); - drop(pub_api_p2pool); - drop(gui_api_xmrig); - drop(gui_api_p2pool); - drop(lock); + drop(lock_pub_sys); debug!("Helper | Unlocking (1/8) ... [pub_sys]"); + drop(xmrig); debug!("Helper | Unlocking (2/8) ... [xmrig]"); + drop(p2pool); debug!("Helper | Unlocking (3/8) ... [p2pool]"); + drop(pub_api_xmrig); debug!("Helper | Unlocking (4/8) ... [pub_api_xmrig]"); + drop(pub_api_p2pool); debug!("Helper | Unlocking (5/8) ... [pub_api_p2pool]"); + drop(gui_api_xmrig); debug!("Helper | Unlocking (6/8) ... [gui_api_xmrig]"); + drop(gui_api_p2pool); debug!("Helper | Unlocking (7/8) ... [gui_api_p2pool]"); + drop(lock); debug!("Helper | Unlocking (8/8) ... [helper]"); // 4. Calculate if we should sleep or not. // If we should sleep, how long? @@ -972,7 +1046,11 @@ impl Helper { if elapsed < 1000 { // Casting from u128 to u64 should be safe here, because [elapsed] // is less than 1000, meaning it can fit into a u64 easy. - std::thread::sleep(std::time::Duration::from_millis((1000-elapsed) as u64)); + let sleep = (1000-elapsed) as u64; + debug!("Helper | END OF LOOP - Sleeping for [{}]ms...", sleep); + std::thread::sleep(std::time::Duration::from_millis(sleep)); + } else { + debug!("Helper | END OF LOOP - Not sleeping!"); } // 5. End loop diff --git a/src/main.rs b/src/main.rs index a6e864b..1828976 100644 --- a/src/main.rs +++ b/src/main.rs @@ -166,6 +166,7 @@ impl App { fn new(now: Instant) -> Self { info!("Initializing App Struct..."); + debug!("App Init | P2Pool & XMRig processes..."); let p2pool = Arc::new(Mutex::new(Process::new(ProcessName::P2pool, String::new(), PathBuf::new()))); let xmrig = Arc::new(Mutex::new(Process::new(ProcessName::Xmrig, String::new(), PathBuf::new()))); let p2pool_api = Arc::new(Mutex::new(PubP2poolApi::new())); @@ -173,6 +174,7 @@ impl App { let p2pool_img = Arc::new(Mutex::new(ImgP2pool::new())); let xmrig_img = Arc::new(Mutex::new(ImgXmrig::new())); + debug!("App Init | Sysinfo..."); // We give this to the [Helper] thread. let mut sysinfo = sysinfo::System::new_with_specifics( sysinfo::RefreshKind::new() @@ -183,6 +185,7 @@ impl App { let pid = sysinfo::get_current_pid().unwrap(); let pub_sys = Arc::new(Mutex::new(Sys::new())); + debug!("App Init | The rest of the [App]..."); let mut app = Self { tab: Tab::default(), ping: Arc::new(Mutex::new(Ping::new())), @@ -232,6 +235,7 @@ impl App { regex: Regexes::new(), }; //---------------------------------------------------------------------------------------------------- App init data that *could* panic + debug!("App Init | Getting EXE path..."); let mut panic = String::new(); // Get exe path app.exe = match get_exe() { @@ -249,6 +253,7 @@ impl App { Err(e) => { panic = format!("get_os_data_path(): {}", e); app.error_state.set(panic.clone(), ErrorFerris::Panic, ErrorButtons::Quit); PathBuf::new() }, }; + debug!("App Init | Setting TOML path..."); // Set [*.toml] path app.state_path = app.os_data_path.clone(); app.state_path.push("state.toml"); @@ -260,9 +265,11 @@ impl App { // Apply arg state // It's not safe to [--reset] if any of the previous variables // are unset (null path), so make sure we just abort if the [panic] String contains something. + debug!("App Init | Applying argument state..."); let mut app = parse_args(app, panic); // Read disk state + debug!("App Init | Reading disk state..."); use TomlError::*; app.state = match State::get(&app.state_path) { Ok(toml) => toml, @@ -281,6 +288,7 @@ impl App { }; app.og = Arc::new(Mutex::new(app.state.clone())); // Read node list + debug!("App Init | Reading node list..."); app.og_node_vec = match Node::get(&app.node_path) { Ok(toml) => toml, Err(err) => { @@ -297,6 +305,7 @@ impl App { }, }; // Read pool list + debug!("App Init | Reading pool list..."); app.og_pool_vec = match Pool::get(&app.pool_path) { Ok(toml) => toml, Err(err) => { @@ -317,6 +326,7 @@ impl App { //---------------------------------------------------------------------------------------------------- let mut og = app.og.lock().unwrap(); // Lock [og] // Handle max threads + debug!("App Init | Handling max thread overflow..."); og.xmrig.max_threads = app.max_threads; let current = og.xmrig.current_threads; let max = og.xmrig.max_threads; @@ -324,6 +334,7 @@ impl App { og.xmrig.current_threads = max; } // Handle [node_vec] overflow + debug!("App Init | Handling [node_vec] overflow"); if og.p2pool.selected_index > app.og_node_vec.len() { warn!("App | Overflowing manual node index [{} > {}], resetting to 1", og.p2pool.selected_index, app.og_node_vec.len()); let (name, node) = app.og_node_vec[0].clone(); @@ -339,6 +350,7 @@ impl App { app.state.p2pool.selected_zmq = node.zmq; } // Handle [pool_vec] overflow + debug!("App Init | Handling [pool_vec] overflow..."); if og.xmrig.selected_index > app.og_pool_vec.len() { warn!("App | Overflowing manual pool index [{} > {}], resetting to 1", og.xmrig.selected_index, app.og_pool_vec.len()); let (name, pool) = app.og_pool_vec[0].clone(); @@ -351,16 +363,24 @@ impl App { app.state.xmrig.selected_ip = pool.ip; app.state.xmrig.selected_port = pool.port; } + // Apply TOML values to [Update] + debug!("App Init | Applying TOML values to [Update]..."); let p2pool_path = og.gupax.absolute_p2pool_path.clone(); let xmrig_path = og.gupax.absolute_xmrig_path.clone(); let tor = og.gupax.update_via_tor; app.update = Arc::new(Mutex::new(Update::new(app.exe.clone(), p2pool_path, xmrig_path, tor))); + + + debug!("App Init | Setting state Gupax version..."); // Set state version as compiled in version og.version.lock().unwrap().gupax = GUPAX_VERSION.to_string(); app.state.version.lock().unwrap().gupax = GUPAX_VERSION.to_string(); + + debug!("App Init | Setting saved [Tab]..."); // Set saved [Tab] app.tab = app.state.gupax.tab; + drop(og); // Unlock [og] info!("App ... OK"); @@ -370,6 +390,7 @@ impl App { info!("Helper ... OK"); // Check for privilege. Should be Admin on [Windows] and NOT root on Unix. + debug!("App Init | Checking for privilege level..."); #[cfg(target_os = "windows")] if is_elevated::is_elevated() { app.admin = true; @@ -835,6 +856,7 @@ impl eframe::App for App { // *-------* // | DEBUG | // *-------* + debug!("App | ----------- Start of [update()] -----------"); // If [F11] was pressed, reverse [fullscreen] bool if ctx.input_mut().consume_key(Modifiers::NONE, Key::F11) { @@ -843,10 +865,12 @@ impl eframe::App for App { } // Refresh AT LEAST once a second + debug!("App | Refreshing frame once per second"); ctx.request_repaint_after(SECOND); // This sets the top level Ui dimensions. // Used as a reference for other uis. + debug!("App | Setting width/height"); CentralPanel::default().show(ctx, |ui| { let available_width = ui.available_width(); if self.width != available_width { @@ -863,6 +887,7 @@ impl eframe::App for App { // while the user was readjusting the frame. It's a pretty heavy operation and looks // buggy when calling it that many times. Looking for a [must_resize] in addtion to // checking if the user is hovering over the app means that we only have call it once. + debug!("App | Checking if we need to resize"); if self.must_resize && ctx.is_pointer_over_area() { self.resizing = true; self.must_resize = false; @@ -899,6 +924,7 @@ impl eframe::App for App { } // If there's an error, display [ErrorState] on the whole screen until user responds + debug!("App | Checking if there is an error in [ErrorState]"); if self.error_state.error { CentralPanel::default().show(ctx, |ui| { ui.vertical_centered(|ui| { @@ -1058,6 +1084,7 @@ impl eframe::App for App { // The struct fields are compared directly because [Version] // contains Arc's that cannot be compared easily. // They don't need to be compared anyway. + debug!("App | Checking diff between [og] & [state]"); let og = self.og.lock().unwrap(); if og.gupax != self.state.gupax || og.p2pool != self.state.p2pool || og.xmrig != self.state.xmrig || self.og_node_vec != self.node_vec { self.diff = true; @@ -1067,6 +1094,7 @@ impl eframe::App for App { drop(og); // Top: Tabs + debug!("App | Rendering TOP tabs"); TopBottomPanel::top("top").show(ctx, |ui| { let width = (self.width - (SPACE*10.0))/5.0; let height = self.height/12.0; @@ -1091,6 +1119,7 @@ impl eframe::App for App { }); // Bottom: app info + state/process buttons + debug!("App | Rendering BOTTOM bar"); TopBottomPanel::bottom("bottom").show(ctx, |ui| { let height = self.height/20.0; ui.style_mut().override_text_style = Some(Name("Bottom".into())); @@ -1308,6 +1337,7 @@ impl eframe::App for App { }); // Middle panel, contents of the [Tab] + debug!("App | Rendering CENTRAL_PANEL (tab contents)"); CentralPanel::default().show(ctx, |ui| { // This sets the Ui dimensions after Top/Bottom are filled self.width = ui.available_width(); @@ -1315,6 +1345,7 @@ impl eframe::App for App { ui.style_mut().override_text_style = Some(TextStyle::Body); match self.tab { Tab::About => { + debug!("App | Entering [About] Tab"); ui.add_space(10.0); ui.vertical_centered(|ui| { // Display [Gupax] banner at max, 1/4 the available length @@ -1336,15 +1367,19 @@ impl eframe::App for App { }); } Tab::Status => { + debug!("App | Entering [Status] Tab"); Status::show(&self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.p2pool_img, &self.xmrig_img, self.p2pool.lock().unwrap().is_alive(), self.xmrig.lock().unwrap().is_alive(), self.width, self.height, ctx, ui); } Tab::Gupax => { + debug!("App | Entering [Gupax] Tab"); Gupax::show(&mut self.state.gupax, &self.og, &self.state_path, &self.update, &self.file_window, &mut self.error_state, &self.restart, self.width, self.height, frame, ctx, ui); } Tab::P2pool => { + debug!("App | Entering [P2Pool] Tab"); P2pool::show(&mut self.state.p2pool, &mut self.node_vec, &self.og, &self.ping, &self.regex, &self.p2pool, &self.p2pool_api, &mut self.p2pool_console, self.width, self.height, ctx, ui); } Tab::Xmrig => { + debug!("App | Entering [XMRig] Tab"); Xmrig::show(&mut self.state.xmrig, &mut self.pool_vec, &self.regex, &self.xmrig, &self.xmrig_api, &mut self.xmrig_console, self.width, self.height, ctx, ui); } } diff --git a/src/node.rs b/src/node.rs index 25642f5..c420bc5 100644 --- a/src/node.rs +++ b/src/node.rs @@ -213,12 +213,14 @@ impl Ping { //---------------------------------------------------------------------------------------------------- Main Ping function // Intermediate function for spawning thread pub fn spawn_thread(ping: &Arc>) { + info!("Spawning ping thread..."); let ping = Arc::clone(ping); std::thread::spawn(move|| { - info!("Spawning ping thread..."); - match Self::ping(ping.clone()) { - Ok(_) => { + let now = Instant::now(); + match Self::ping(&ping) { + Ok(msg) => { info!("Ping ... OK"); + ping.lock().unwrap().msg = msg; ping.lock().unwrap().pinged = true; ping.lock().unwrap().auto_selected = false; }, @@ -228,6 +230,7 @@ impl Ping { ping.lock().unwrap().msg = err.to_string(); }, } + info!("Ping ... Took [{}] seconds...", now.elapsed().as_secs_f32()); ping.lock().unwrap().pinging = false; }); } @@ -250,9 +253,10 @@ impl Ping { // timeout = BLACK // default = GRAY #[tokio::main] - pub async fn ping(ping: Arc>) -> Result<(), anyhow::Error> { + pub async fn ping(ping: &Arc>) -> Result { // Timer let now = Instant::now(); + let ping = Arc::clone(ping); // Start ping ping.lock().unwrap().pinging = true; @@ -268,8 +272,8 @@ impl Ping { // Random User Agent let rand_user_agent = crate::Pkg::get_user_agent(); // Handle vector - let mut handles = vec![]; - let node_vec = Arc::new(Mutex::new(Vec::new())); + let mut handles = Vec::with_capacity(NODE_IPS.len()); + let node_vec = Arc::new(Mutex::new(Vec::with_capacity(NODE_IPS.len()))); for ip in NODE_IPS { let client = client.clone(); @@ -288,18 +292,19 @@ impl Ping { for handle in handles { handle.await?; } - let node_vec = node_vec.lock().unwrap().clone(); - let info = format!("Fastest node: {}ms ... {} @ {}", node_vec[0].ms, node_vec[0].id, node_vec[0].ip); - info!("Ping | {}", info); - info!("Ping | Took [{}] seconds...", now.elapsed().as_secs_f32()); + let node_vec = std::mem::take(&mut *node_vec.lock().unwrap()); + let fastest_info = format!("Fastest node: {}ms ... {} @ {}", node_vec[0].ms, node_vec[0].id, node_vec[0].ip); + + let info = format!("Cleaning up connections"); + info!("Ping | {}...", info); let mut ping = ping.lock().unwrap(); ping.fastest = node_vec[0].id; ping.nodes = node_vec; ping.prog = 100.0; ping.msg = info; drop(ping); - Ok(()) + Ok(fastest_info) } async fn response(client: Client, request: Request, ip: &'static str, ping: Arc>, percent: f32, node_vec: Arc>>) { diff --git a/src/p2pool.rs b/src/p2pool.rs index c9dc667..7448aed 100644 --- a/src/p2pool.rs +++ b/src/p2pool.rs @@ -35,6 +35,7 @@ impl P2pool { pub fn show(&mut self, node_vec: &mut Vec<(String, Node)>, og: &Arc>, ping: &Arc>, regex: &Regexes, process: &Arc>, api: &Arc>, buffer: &mut String, width: f32, height: f32, ctx: &egui::Context, ui: &mut egui::Ui) { let text_edit = height / 25.0; //---------------------------------------------------------------------------------------------------- [Simple] Console + debug!("P2Pool Tab | Rendering [Console]"); ui.group(|ui| { if self.simple { let height = height / 2.4; @@ -71,6 +72,7 @@ impl P2pool { //---------------------------------------------------------------------------------------------------- Args if !self.simple { + debug!("P2Pool Tab | Rendering [Arguments]"); ui.group(|ui| { ui.horizontal(|ui| { let width = (width/10.0) - SPACE; ui.style_mut().override_text_style = Some(Monospace); @@ -82,6 +84,7 @@ impl P2pool { } //---------------------------------------------------------------------------------------------------- Address + debug!("P2Pool Tab | Rendering [Address]"); ui.group(|ui| { let width = width - SPACE; ui.spacing_mut().text_edit_width = (width)-(SPACE*3.0); @@ -117,6 +120,7 @@ impl P2pool { // saves me the hassle of wrapping [state: State] completely // and [.lock().unwrap()]ing it everywhere. // Two atomic bools = enough to represent this data + debug!("P2Pool Tab | Running [auto-select] check"); if self.auto_select { let mut ping = ping.lock().unwrap(); // If we haven't auto_selected yet, auto-select and turn it off @@ -129,6 +133,7 @@ impl P2pool { ui.vertical(|ui| { ui.horizontal(|ui| { + debug!("P2Pool Tab | Rendering [Ping List]"); // [Ping List] let id = self.node; let ip = enum_to_ip(id); @@ -143,6 +148,7 @@ impl P2pool { } } } + debug!("P2Pool Tab | Rendering [ComboBox] of Community Nodes"); let text = RichText::new(format!(" ⏺ {}ms | {} | {}", ms, id, ip)).color(color); ComboBox::from_id_source("community_nodes").selected_text(RichText::text_style(text, Monospace)).show_ui(ui, |ui| { for data in ping.lock().unwrap().nodes.iter() { @@ -156,6 +162,7 @@ impl P2pool { ui.add_space(5.0); + debug!("P2Pool Tab | Rendering [Select fastest ... Ping] buttons"); ui.horizontal(|ui| { let width = (width/2.0)-4.0; // [Select fastest node] @@ -189,15 +196,11 @@ impl P2pool { }); }); + debug!("P2Pool Tab | Rendering [Auto-*] buttons"); ui.group(|ui| { ui.horizontal(|ui| { let width = (width/2.0)-(SPACE*1.75); // [Auto-node] + [Auto-select] -// let mut style = (*ctx.style()).clone(); -// style.spacing.icon_width_inner = width / 16.0; -// style.spacing.icon_width = width / 6.0; -// style.spacing.icon_spacing = 20.0; -// ctx.set_style(style); ui.add_sized([width, height], Checkbox::new(&mut self.auto_select, "Auto-select")).on_hover_text(P2POOL_AUTO_SELECT); ui.separator(); ui.add_sized([width, height], Checkbox::new(&mut self.auto_node, "Auto-node")).on_hover_text(P2POOL_AUTO_NODE); @@ -205,6 +208,7 @@ impl P2pool { //---------------------------------------------------------------------------------------------------- Advanced } else { + debug!("P2Pool Tab | Rendering [Node List] elements"); let mut incorrect_input = false; // This will disable [Add/Delete] on bad input // [Monero node IP/RPC/ZMQ] ui.horizontal(|ui| { @@ -302,6 +306,7 @@ impl P2pool { ui.spacing_mut().slider_width = width - 8.0; ui.spacing_mut().icon_width = width / 25.0; // [Ping List] + debug!("P2Pool Tab | Rendering [Node List]"); let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name)); ComboBox::from_id_source("manual_nodes").selected_text(RichText::text_style(text, Monospace)).show_ui(ui, |ui| { let mut n = 0; @@ -416,6 +421,7 @@ impl P2pool { }); ui.add_space(5.0); + debug!("P2Pool Tab | Rendering [Main/Mini/Peers/Log] elements"); // [Main/Mini] ui.horizontal(|ui| { let height = height/4.0; diff --git a/src/status.rs b/src/status.rs index be2d4fc..fda65bf 100644 --- a/src/status.rs +++ b/src/status.rs @@ -25,6 +25,7 @@ use crate::{ Sys, }; use std::sync::{Arc,Mutex}; +use log::*; use egui::{ containers::*, Label,RichText,TextStyle @@ -42,6 +43,7 @@ pub fn show(sys: &Arc>, p2pool_api: &Arc>, xmrig_ ui.horizontal(|ui| { // [Gupax] ui.group(|ui| { ui.vertical(|ui| { + debug!("Status | Rendering [Gupax]"); ui.set_min_height(min_height); ui.add_sized([width, height*2.0], Label::new(RichText::new("[Gupax]").color(LIGHT_GRAY).text_style(TextStyle::Name("MonospaceLarge".into())))).on_hover_text("Gupax is online"); let sys = sys.lock().unwrap(); @@ -61,6 +63,7 @@ pub fn show(sys: &Arc>, p2pool_api: &Arc>, xmrig_ })}); // [P2Pool] ui.group(|ui| { ui.vertical(|ui| { + debug!("Status Tab | Rendering [P2Pool]"); ui.set_enabled(p2pool_online); ui.set_min_height(min_height); ui.add_sized([width, height*2.0], Label::new(RichText::new("[P2Pool]").color(LIGHT_GRAY).text_style(TextStyle::Name("MonospaceLarge".into())))).on_hover_text("P2Pool is online").on_disabled_hover_text("P2Pool is offline"); @@ -85,6 +88,7 @@ pub fn show(sys: &Arc>, p2pool_api: &Arc>, xmrig_ })}); // [XMRig] ui.group(|ui| { ui.vertical(|ui| { + debug!("Status Tab | Rendering [XMRig]"); ui.set_enabled(xmrig_online); ui.set_min_height(min_height); ui.add_sized([width, height*2.0], Label::new(RichText::new("[XMRig]").color(LIGHT_GRAY).text_style(TextStyle::Name("MonospaceLarge".into())))).on_hover_text("XMRig is online").on_disabled_hover_text("XMRig is offline"); diff --git a/src/xmrig.rs b/src/xmrig.rs index 0e26d6b..d133547 100644 --- a/src/xmrig.rs +++ b/src/xmrig.rs @@ -36,6 +36,7 @@ impl Xmrig { pub fn show(&mut self, pool_vec: &mut Vec<(String, Pool)>, regex: &Regexes, process: &Arc>, api: &Arc>, buffer: &mut String, width: f32, height: f32, ctx: &egui::Context, ui: &mut egui::Ui) { let text_edit = height / 25.0; //---------------------------------------------------------------------------------------------------- [Simple] Console + debug!("XMRig Tab | Rendering [Console]"); ui.group(|ui| { if self.simple { let height = height / 1.5; @@ -70,8 +71,9 @@ impl Xmrig { } }); - //---------------------------------------------------------------------------------------------------- Config + //---------------------------------------------------------------------------------------------------- Arguments if !self.simple { + debug!("XMRig Tab | Rendering [Arguments]"); ui.group(|ui| { ui.horizontal(|ui| { let width = (width/10.0) - SPACE; ui.style_mut().override_text_style = Some(Monospace); @@ -81,6 +83,7 @@ impl Xmrig { })}); ui.set_enabled(self.arguments.is_empty()); //---------------------------------------------------------------------------------------------------- Address + debug!("XMRig Tab | Rendering [Address]"); ui.group(|ui| { let width = width - SPACE; ui.spacing_mut().text_edit_width = (width)-(SPACE*3.0); @@ -106,6 +109,7 @@ impl Xmrig { //---------------------------------------------------------------------------------------------------- Threads if self.simple { ui.add_space(SPACE); } + debug!("XMRig Tab | Rendering [Threads]"); ui.vertical(|ui| { let width = width/10.0; ui.spacing_mut().icon_width = width / 25.0; @@ -129,7 +133,8 @@ impl Xmrig { // }); } else { - let _height = height / 10.0; + debug!("XMRig Tab | Rendering [Pool List] elements"); +// let _height = height / 10.0; let width = ui.available_width() - 10.0; let mut incorrect_input = false; // This will disable [Add/Delete] on bad input // [Pool IP/Port] @@ -226,7 +231,8 @@ impl Xmrig { // [Manual node selection] ui.spacing_mut().slider_width = width - 8.0; ui.spacing_mut().icon_width = width / 25.0; - // [Ping List] + // [Node List] + debug!("XMRig Tab | Rendering [Node List] ComboBox"); let text = RichText::new(format!("{}. {}", self.selected_index+1, self.selected_name)); ComboBox::from_id_source("manual_pool").selected_text(RichText::text_style(text, Monospace)).show_ui(ui, |ui| { let mut n = 0; @@ -345,7 +351,8 @@ impl Xmrig { }); ui.add_space(5.0); - // [HTTP API IP/Port] + [TLS + Keepalive] + debug!("XMRig Tab | Rendering [API] TextEdits"); + // [HTTP API IP/Port] ui.group(|ui| { ui.horizontal(|ui| { ui.vertical(|ui| { let width = width/10.0; @@ -396,6 +403,7 @@ impl Xmrig { ui.separator(); + debug!("XMRig Tab | Rendering [TLS/Keepalive] buttons"); ui.vertical(|ui| { // TLS/Keepalive ui.horizontal(|ui| {