Skip to content

Commit f77789e

Browse files
committed
memory tracking
1 parent 86e814f commit f77789e

File tree

7 files changed

+169
-7
lines changed

7 files changed

+169
-7
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "fls"
3-
version = "0.1.6"
3+
version = "0.1.7"
44
edition = "2021"
55

66
[dependencies]

src/fls/from_url.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub async fn flash_from_url(
8484
let error_processor = tokio::spawn(process_error_messages(error_rx));
8585

8686
// Main download loop with retry logic
87-
let mut progress = ProgressTracker::new(options.newline_progress);
87+
let mut progress = ProgressTracker::new(options.newline_progress, options.show_memory);
8888
// Set whether we're actually decompressing (not using cat for uncompressed files)
8989
progress.set_is_compressed(decompressor_name != "cat");
9090
let update_interval = Duration::from_secs_f64(options.progress_interval_secs);

src/fls/memory.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#[cfg(target_os = "linux")]
2+
use std::fs;
3+
4+
#[derive(Debug, Clone)]
5+
pub struct MemoryStats {
6+
pub process_rss_mb: f64,
7+
pub process_swap_mb: f64,
8+
pub system_free_mb: f64,
9+
pub system_total_mb: f64,
10+
pub system_cached_mb: f64,
11+
}
12+
13+
impl MemoryStats {
14+
/// Format memory stats in a compact format for display
15+
/// Example: "Mem: 1.2G/4G (proc: 250M+50Msw cache: 1.5G)"
16+
pub fn format_compact(&self) -> String {
17+
let system_used_mb = self.system_total_mb - self.system_free_mb - self.system_cached_mb;
18+
19+
format!(
20+
"Mem: {}/{} (proc: {}{} cache: {})",
21+
format_mb(system_used_mb),
22+
format_mb(self.system_total_mb),
23+
format_mb(self.process_rss_mb),
24+
if self.process_swap_mb > 0.0 {
25+
format!("+{}sw", format_mb(self.process_swap_mb))
26+
} else {
27+
String::new()
28+
},
29+
format_mb(self.system_cached_mb),
30+
)
31+
}
32+
}
33+
34+
/// Format MB value to human-readable format (M or G)
35+
fn format_mb(mb: f64) -> String {
36+
if mb >= 1024.0 {
37+
format!("{:.1}G", mb / 1024.0)
38+
} else {
39+
format!("{:.0}M", mb)
40+
}
41+
}
42+
43+
/// Get memory statistics from the system
44+
/// Returns None if reading fails or not on Linux
45+
pub fn get_memory_stats() -> Option<MemoryStats> {
46+
#[cfg(target_os = "linux")]
47+
{
48+
get_memory_stats_linux()
49+
}
50+
51+
#[cfg(not(target_os = "linux"))]
52+
{
53+
None
54+
}
55+
}
56+
57+
#[cfg(target_os = "linux")]
58+
fn get_memory_stats_linux() -> Option<MemoryStats> {
59+
// Read process memory from /proc/self/status
60+
let (process_rss_mb, process_swap_mb) = read_process_memory()?;
61+
62+
// Read system memory from /proc/meminfo
63+
let (system_total_mb, system_free_mb, system_cached_mb) = read_system_memory()?;
64+
65+
Some(MemoryStats {
66+
process_rss_mb,
67+
process_swap_mb,
68+
system_free_mb,
69+
system_total_mb,
70+
system_cached_mb,
71+
})
72+
}
73+
74+
#[cfg(target_os = "linux")]
75+
fn read_process_memory() -> Option<(f64, f64)> {
76+
let status = fs::read_to_string("/proc/self/status").ok()?;
77+
78+
let mut rss_kb = 0u64;
79+
let mut swap_kb = 0u64;
80+
81+
for line in status.lines() {
82+
if line.starts_with("VmRSS:") {
83+
// VmRSS: 123456 kB
84+
rss_kb = line.split_whitespace().nth(1)?.parse().ok()?;
85+
} else if line.starts_with("VmSwap:") {
86+
// VmSwap: 12345 kB
87+
swap_kb = line.split_whitespace().nth(1)?.parse().ok()?;
88+
}
89+
}
90+
91+
let rss_mb = rss_kb as f64 / 1024.0;
92+
let swap_mb = swap_kb as f64 / 1024.0;
93+
94+
Some((rss_mb, swap_mb))
95+
}
96+
97+
#[cfg(target_os = "linux")]
98+
fn read_system_memory() -> Option<(f64, f64, f64)> {
99+
let meminfo = fs::read_to_string("/proc/meminfo").ok()?;
100+
101+
let mut total_kb = 0u64;
102+
let mut free_kb = 0u64;
103+
let mut available_kb = 0u64;
104+
let mut cached_kb = 0u64;
105+
let mut buffers_kb = 0u64;
106+
107+
for line in meminfo.lines() {
108+
let parts: Vec<&str> = line.split_whitespace().collect();
109+
if parts.len() < 2 {
110+
continue;
111+
}
112+
113+
let value: u64 = parts[1].parse().ok()?;
114+
115+
match parts[0] {
116+
"MemTotal:" => total_kb = value,
117+
"MemFree:" => free_kb = value,
118+
"MemAvailable:" => available_kb = value,
119+
"Cached:" => cached_kb = value,
120+
"Buffers:" => buffers_kb = value,
121+
_ => {}
122+
}
123+
}
124+
125+
// Use MemAvailable if present (more accurate), otherwise use MemFree
126+
let effective_free_kb = if available_kb > 0 {
127+
available_kb
128+
} else {
129+
free_kb
130+
};
131+
132+
// Cache includes buffers and cached
133+
let effective_cached_kb = cached_kb + buffers_kb;
134+
135+
let total_mb = total_kb as f64 / 1024.0;
136+
let free_mb = effective_free_kb as f64 / 1024.0;
137+
let cached_mb = effective_cached_kb as f64 / 1024.0;
138+
139+
Some((total_mb, free_mb, cached_mb))
140+
}

src/fls/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod download_error;
55
mod error_handling;
66
mod from_url;
77
mod http;
8+
mod memory;
89
mod options;
910
mod progress;
1011

src/fls/options.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub struct BlockFlashOptions {
1212
pub headers: Vec<(String, String)>,
1313
pub progress_interval_secs: f64,
1414
pub newline_progress: bool,
15+
pub show_memory: bool,
1516
}
1617

1718
impl Default for BlockFlashOptions {
@@ -28,6 +29,7 @@ impl Default for BlockFlashOptions {
2829
headers: Vec::new(),
2930
progress_interval_secs: 0.1,
3031
newline_progress: false,
32+
show_memory: false,
3133
}
3234
}
3335
}

src/fls/progress.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::io::{self, Write};
22
use std::time::{Duration, Instant};
33

4+
use crate::fls::memory;
5+
46
pub struct FinalStats {
57
pub mb_received: f64,
68
pub mb_decompressed: f64,
@@ -34,10 +36,12 @@ pub(crate) struct ProgressTracker {
3436
is_compressed: bool,
3537
// Whether to print on new lines instead of clearing and rewriting
3638
newline_progress: bool,
39+
// Whether to show memory statistics
40+
show_memory: bool,
3741
}
3842

3943
impl ProgressTracker {
40-
pub(crate) fn new(newline_progress: bool) -> Self {
44+
pub(crate) fn new(newline_progress: bool, show_memory: bool) -> Self {
4145
let now = Instant::now();
4246
Self {
4347
bytes_received: 0,
@@ -55,6 +59,7 @@ impl ProgressTracker {
5559
content_length: None,
5660
is_compressed: true,
5761
newline_progress,
62+
show_memory,
5863
}
5964
}
6065

@@ -147,17 +152,26 @@ impl ProgressTracker {
147152
"Progress"
148153
};
149154

155+
// Get memory stats if enabled
156+
let memory_suffix = if self.show_memory {
157+
memory::get_memory_stats()
158+
.map(|stats| format!(" | {}", stats.format_compact()))
159+
.unwrap_or_default()
160+
} else {
161+
String::new()
162+
};
163+
150164
if self.newline_progress {
151165
// Print on a new line
152166
println!(
153-
"Download: {} | {}: {} | Written: {}",
154-
download_status, progress_label, decompress_status, write_status
167+
"Download: {} | {}: {} | Written: {}{}",
168+
download_status, progress_label, decompress_status, write_status, memory_suffix
155169
);
156170
} else {
157171
// Use carriage return and clear line
158172
print!(
159-
"\r\x1b[KDownload: {} | {}: {} | Written: {}",
160-
download_status, progress_label, decompress_status, write_status
173+
"\r\x1b[KDownload: {} | {}: {} | Written: {}{}",
174+
download_status, progress_label, decompress_status, write_status, memory_suffix
161175
);
162176
io::stdout().flush()?;
163177
}

src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ enum Commands {
4949
/// Print progress on new lines instead of clearing and rewriting the same line
5050
#[arg(short = 'n', long)]
5151
newline_progress: bool,
52+
/// Show memory statistics in progress display
53+
#[arg(long)]
54+
show_memory: bool,
5255
},
5356
}
5457

@@ -70,6 +73,7 @@ async fn main() {
7073
headers,
7174
progress_interval,
7275
newline_progress,
76+
show_memory,
7377
} => {
7478
println!("Block flash command:");
7579
println!(" URL: {}", url);
@@ -118,6 +122,7 @@ async fn main() {
118122
headers: parsed_headers,
119123
progress_interval_secs: progress_interval,
120124
newline_progress,
125+
show_memory,
121126
};
122127

123128
match fls::flash_from_url(&url, options).await {

0 commit comments

Comments
 (0)