Skip to content

Commit 1aab2ed

Browse files
author
Memfault Inc.
committed
memfaultd 1.24.0 (Build 3293818)
1 parent 7452689 commit 1aab2ed

File tree

10 files changed

+174
-30
lines changed

10 files changed

+174
-30
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to
77
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88

9+
## [1.24.0] - 2025-08-18
10+
11+
This release adds an example recipe for a `systemd` service that depends on
12+
`memfaultd`. Additionally it changes the query frequency of eMMC lifetime
13+
metrics.
14+
15+
### Added
16+
17+
- Added an example service that depends on `memfaultd`. This is useful for when
18+
you want to run some code after `memfaultd` has finished starting.
19+
20+
### Changed
21+
22+
- Changed the read interval for eMMC lifetime values to only happen once an hour
23+
regardless of configuration. The lifetime values will change very
24+
infrequently. Reading them at a lower frequency can help lower CPU utilization
25+
and reduce eMMC controller wakeups without sacrificing any resolution. This
26+
can improve battery life for battery powered devices.
27+
928
## [1.23.0] - 2025-08-11
1029

1130
This release adds support for Python based stacktraces, as well as a few minor
@@ -1455,3 +1474,5 @@ package][nginx-pid-report] for a discussion on the topic.
14551474
https://github.com/memfault/memfault-linux-sdk/releases/tag/1.22.0-kirkstone
14561475
[1.23.0]:
14571476
https://github.com/memfault/memfault-linux-sdk/releases/tag/1.23.0-kirkstone
1477+
[1.24.0]:
1478+
https://github.com/memfault/memfault-linux-sdk/releases/tag/1.24.0-kirkstone

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

VERSION

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
BUILD ID: 3280185
2-
GIT COMMIT: b851a29751
3-
VERSION: 1.23.0
1+
BUILD ID: 3293818
2+
GIT COMMIT: 1ef1df22e9
3+
VERSION: 1.24.0

memfault-ssf/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "memfault-ssf"
3-
version = "1.23.0"
3+
version = "1.24.0"
44
edition = "2021"
55
description = "Supporting crate for the Memfault memfaultd embedded Linux agent"
66
homepage = "https://github.com/memfault/memfaultd"

memfault-ssf/VERSION

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
BUILD ID: 3280185
2-
GIT COMMIT: b851a29751
3-
VERSION: 1.23.0
1+
BUILD ID: 3293818
2+
GIT COMMIT: 1ef1df22e9
3+
VERSION: 1.24.0

memfaultc-sys/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "memfaultc-sys"
3-
version = "1.23.0"
3+
version = "1.24.0"
44
edition = "2021"
55
autobins = false
66
description = "Supporting crate for the Memfault memfaultd embedded Linux agent"

memfaultc-sys/VERSION

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
BUILD ID: 3280185
2-
GIT COMMIT: b851a29751
3-
VERSION: 1.23.0
1+
BUILD ID: 3293818
2+
GIT COMMIT: 1ef1df22e9
3+
VERSION: 1.24.0

memfaultd/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "memfaultd"
3-
version = "1.23.0"
3+
version = "1.24.0"
44
edition = "2021"
55
autobins = false
66
rust-version = "1.72"
@@ -28,8 +28,8 @@ name= "mfw"
2828
path= "src/bin/mfw.rs"
2929

3030
[dependencies]
31-
memfaultc-sys = { path= "../memfaultc-sys", version = "1.23.0" }
32-
ssf = { package = "memfault-ssf", path= "../memfault-ssf", version = "1.23.0" }
31+
memfaultc-sys = { path= "../memfaultc-sys", version = "1.24.0" }
32+
ssf = { package = "memfault-ssf", path= "../memfault-ssf", version = "1.24.0" }
3333
argh = "0.1.10"
3434
cfg-if = "1.0.0"
3535
chrono = { version = "0.4.23", features = ["serde"]}

memfaultd/VERSION

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
BUILD ID: 3280185
2-
GIT COMMIT: b851a29751
3-
VERSION: 1.23.0
1+
BUILD ID: 3293818
2+
GIT COMMIT: 1ef1df22e9
3+
VERSION: 1.24.0

memfaultd/src/metrics/system_metrics/disk.rs

Lines changed: 133 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::{
66
fs::{read_dir, File},
77
io::{BufRead, BufReader},
88
str::FromStr,
9+
time::{Duration, Instant},
910
};
1011

1112
use eyre::{eyre, Result};
@@ -30,28 +31,26 @@ const SECTOR_SIZE: u64 = 512;
3031
pub struct DiskMetricsCollector<M: Mmc> {
3132
mmc: Vec<M>,
3233
prev_bytes_reading: Option<u64>,
34+
last_lifetime_reading: Option<Instant>,
3335
}
3436

3537
impl<M> DiskMetricsCollector<M>
3638
where
3739
M: Mmc,
3840
{
41+
// Only read lifetime once an hour
42+
const LIFETIME_READING_INTERVAL: Duration = Duration::from_secs(3600);
43+
3944
pub fn new(mmc: Vec<M>) -> Self {
4045
Self {
4146
mmc,
4247
prev_bytes_reading: None,
48+
last_lifetime_reading: None,
4349
}
4450
}
4551

46-
fn get_disk_metrics(
47-
mmc: &M,
48-
disk_stats: Option<&Vec<u64>>,
49-
prev_bytes_reading: &mut Option<u64>,
50-
) -> Result<Vec<KeyedMetricReading>> {
51-
let disk_name = mmc.disk_name();
52-
53-
let mut metrics = vec![];
54-
52+
fn get_lifetime_readings(disk_name: &str, mmc: &M) -> Result<Vec<KeyedMetricReading>> {
53+
let mut metrics = Vec::with_capacity(2);
5554
if let Some(lifetime) = mmc.read_lifetime()? {
5655
match lifetime.lifetime_a_pct {
5756
Some(lifetime_a_pct) => {
@@ -100,6 +99,39 @@ where
10099
}
101100
}
102101

102+
Ok(metrics)
103+
}
104+
105+
fn get_disk_metrics(
106+
mmc: &M,
107+
disk_stats: Option<&Vec<u64>>,
108+
prev_bytes_reading: &mut Option<u64>,
109+
last_lifetime_reading: &mut Option<Instant>,
110+
) -> Result<Vec<KeyedMetricReading>> {
111+
let disk_name = mmc.disk_name();
112+
113+
let mut metrics = vec![];
114+
115+
match last_lifetime_reading {
116+
Some(last_reading) => {
117+
let now = Instant::now();
118+
let get_next_reading =
119+
now.checked_duration_since(*last_reading)
120+
.is_some_and(|duration_since| {
121+
duration_since >= Self::LIFETIME_READING_INTERVAL
122+
});
123+
124+
if get_next_reading {
125+
metrics.extend(Self::get_lifetime_readings(disk_name, mmc)?);
126+
*last_lifetime_reading = Some(now);
127+
}
128+
}
129+
None => {
130+
metrics.extend(Self::get_lifetime_readings(disk_name, mmc)?);
131+
*last_lifetime_reading = Some(Instant::now());
132+
}
133+
}
134+
103135
let bytes_written = disk_stats
104136
.and_then(|disk_stats| disk_stats.get(6))
105137
.map(|sectors_written| *sectors_written * SECTOR_SIZE);
@@ -269,7 +301,12 @@ where
269301
.iter()
270302
.filter_map(|m| {
271303
let disk_stats_line = disk_stats_map.get(m.disk_name());
272-
match Self::get_disk_metrics(m, disk_stats_line, &mut self.prev_bytes_reading) {
304+
match Self::get_disk_metrics(
305+
m,
306+
disk_stats_line,
307+
&mut self.prev_bytes_reading,
308+
&mut self.last_lifetime_reading,
309+
) {
273310
Ok(metrics) => Some(metrics),
274311
Err(e) => {
275312
error!(
@@ -394,11 +431,13 @@ mod test {
394431
let disk_stats = vec![0, 0, 0, 0, 0, 0, 1000, 0, 0, 0, 0];
395432
let mut prev_bytes_reading = None;
396433

434+
let hour_ago = Instant::now() - DiskMetricsCollector::<FakeMmc>::LIFETIME_READING_INTERVAL;
397435
// First call should return MLC and SLC metrics but no bytes written (no previous reading)
398436
let metrics = DiskMetricsCollector::get_disk_metrics(
399437
&fake_mmc,
400438
Some(&disk_stats),
401439
&mut prev_bytes_reading,
440+
&mut Some(hour_ago),
402441
)
403442
.unwrap();
404443

@@ -412,6 +451,7 @@ mod test {
412451
&fake_mmc,
413452
Some(&updated_disk_stats),
414453
&mut prev_bytes_reading,
454+
&mut Some(hour_ago),
415455
)
416456
.unwrap();
417457

@@ -442,12 +482,14 @@ mod test {
442482
// Create disk stats (sectors written = 1000)
443483
let disk_stats = vec![0, 0, 0, 0, 0, 0, 1000, 0, 0, 0, 0];
444484
let mut prev_bytes_reading = None;
485+
let hour_ago = Instant::now() - DiskMetricsCollector::<FakeMmc>::LIFETIME_READING_INTERVAL;
445486

446487
// First call should return MLC and SLC metrics but no bytes written (no previous reading)
447488
let metrics = DiskMetricsCollector::get_disk_metrics(
448489
&fake_mmc,
449490
Some(&disk_stats),
450491
&mut prev_bytes_reading,
492+
&mut Some(hour_ago),
451493
)
452494
.unwrap();
453495

@@ -461,6 +503,7 @@ mod test {
461503
&fake_mmc,
462504
Some(&updated_disk_stats),
463505
&mut prev_bytes_reading,
506+
&mut Some(hour_ago),
464507
)
465508
.unwrap();
466509

@@ -491,12 +534,14 @@ mod test {
491534
// Create disk stats (sectors written = 1000)
492535
let disk_stats = vec![0, 0, 0, 0, 0, 0, 1000, 0, 0, 0, 0];
493536
let mut prev_bytes_reading = None;
537+
let hour_ago = Instant::now() - DiskMetricsCollector::<FakeMmc>::LIFETIME_READING_INTERVAL;
494538

495539
// First call should return MLC and SLC metrics but no bytes written (no previous reading)
496540
let metrics = DiskMetricsCollector::get_disk_metrics(
497541
&fake_mmc,
498542
Some(&disk_stats),
499543
&mut prev_bytes_reading,
544+
&mut Some(hour_ago),
500545
)
501546
.unwrap();
502547

@@ -507,6 +552,84 @@ mod test {
507552
});
508553
}
509554

555+
#[test]
556+
fn test_lifetime_interval_update() {
557+
let fake_mmc = FakeMmc {
558+
disk_name: "mmcblk0".to_string(),
559+
product_name: "SG123".to_string(),
560+
manufacturer_id: "0x00015".to_string(),
561+
lifetime: MmcLifeTime {
562+
lifetime_a_pct: Some(90),
563+
lifetime_b_pct: Some(85),
564+
},
565+
sector_count: 100,
566+
manufacture_date: "11/2023".to_string(),
567+
revision: "1.0".to_string(),
568+
serial: "0x1234567890".to_string(),
569+
};
570+
571+
let mut prev_bytes_reading = None;
572+
let mut last_lifetime_reading = None;
573+
574+
// First reading should set the last reading time
575+
let metrics = DiskMetricsCollector::get_disk_metrics(
576+
&fake_mmc,
577+
None,
578+
&mut prev_bytes_reading,
579+
&mut last_lifetime_reading,
580+
)
581+
.unwrap();
582+
583+
assert_eq!(metrics.len(), 8);
584+
assert!(last_lifetime_reading.is_some());
585+
}
586+
587+
#[test]
588+
fn test_lifetime_interval_read() {
589+
let fake_mmc = FakeMmc {
590+
disk_name: "mmcblk0".to_string(),
591+
product_name: "SG123".to_string(),
592+
manufacturer_id: "0x00015".to_string(),
593+
lifetime: MmcLifeTime {
594+
lifetime_a_pct: Some(90),
595+
lifetime_b_pct: Some(85),
596+
},
597+
sector_count: 100,
598+
manufacture_date: "11/2023".to_string(),
599+
revision: "1.0".to_string(),
600+
serial: "0x1234567890".to_string(),
601+
};
602+
603+
let mut prev_bytes_reading = None;
604+
let mut last_lifetime_reading =
605+
Some(Instant::now() - DiskMetricsCollector::<FakeMmc>::LIFETIME_READING_INTERVAL);
606+
607+
let metrics = DiskMetricsCollector::get_disk_metrics(
608+
&fake_mmc,
609+
None,
610+
&mut prev_bytes_reading,
611+
&mut last_lifetime_reading,
612+
)
613+
.unwrap();
614+
615+
// Should read lifetime metrics again since the interval has passed
616+
assert_eq!(metrics.len(), 8);
617+
618+
let mut prev_bytes_reading = None;
619+
let mut last_lifetime_reading = Some(Instant::now());
620+
621+
let metrics = DiskMetricsCollector::get_disk_metrics(
622+
&fake_mmc,
623+
None,
624+
&mut prev_bytes_reading,
625+
&mut last_lifetime_reading,
626+
)
627+
.unwrap();
628+
629+
// Should not read lifetime metrics again since the interval has not passed
630+
assert_eq!(metrics.len(), 6);
631+
}
632+
510633
#[test]
511634
fn test_calc_bytes_reading_overflow() {
512635
let mut prev_bytes_reading = Some(1000);

0 commit comments

Comments
 (0)