Skip to content

Commit 88f4254

Browse files
committed
feat: add procfs process tracking
1 parent 018a806 commit 88f4254

File tree

2 files changed

+35
-11
lines changed

2 files changed

+35
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
- Lading now built with edition 2024
1010
- Removed use of compromised `tj-actions/changed-files` action from project's GitHub CI configuration
1111
- Fixed devcontainer configuration to ensure the `rust-analyzer` can run successfully within IDEs
12+
- Added a new gauge `processes_found` and a new warning log for processes we skipped
1213

1314
## [0.25.6]
1415
## Fixed

lading/src/observer/linux/procfs.rs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ impl Sampler {
8383
// A tally of the total RSS and PSS consumed by the parent process and
8484
// its children.
8585
let mut aggr = memory::smaps_rollup::Aggregator::default();
86+
let mut processes_found: i32 = 0;
87+
let mut pids_skipped: FxHashSet<i32> = FxHashSet::default();
8688

8789
// Every sample run we collect all the child processes rooted at the
8890
// parent. As noted by the procfs documentation is this done by
@@ -119,9 +121,18 @@ impl Sampler {
119121
}
120122
}
121123

124+
processes_found += 1;
122125
let pid = process.pid();
123-
if let Err(e) = self.handle_process(process, &mut aggr, include_smaps).await {
124-
warn!("Encountered uncaught error when handling `/proc/{pid}/`: {e}");
126+
match self.handle_process(process, &mut aggr, include_smaps).await {
127+
Ok(false) => {
128+
pids_skipped.insert(pid);
129+
}
130+
Err(e) => {
131+
warn!("Encountered uncaught error when handling `/proc/{pid}/`: {e}");
132+
}
133+
_ => {
134+
// handled successfully
135+
}
125136
}
126137
}
127138

@@ -130,10 +141,22 @@ impl Sampler {
130141

131142
gauge!("total_rss_bytes").set(aggr.rss as f64);
132143
gauge!("total_pss_bytes").set(aggr.pss as f64);
144+
gauge!("processes_found").set(processes_found as f64);
145+
146+
// If we skipped any processes, log a warning.
147+
if !pids_skipped.is_empty() {
148+
warn!(
149+
"Skipped {} processes: {:?}",
150+
pids_skipped.len(),
151+
pids_skipped
152+
);
153+
}
133154

134155
Ok(())
135156
}
136157

158+
/// Handle a process. Returns true if the process was handled successfully,
159+
/// false if it was skipped for any reason.
137160
#[allow(
138161
clippy::similar_names,
139162
clippy::too_many_lines,
@@ -146,7 +169,7 @@ impl Sampler {
146169
process: Process,
147170
aggr: &mut memory::smaps_rollup::Aggregator,
148171
include_smaps: bool,
149-
) -> Result<(), Error> {
172+
) -> Result<bool, Error> {
150173
let pid = process.pid();
151174

152175
// `/proc/{pid}/status`
@@ -156,12 +179,12 @@ impl Sampler {
156179
warn!("Couldn't read status: {:?}", e);
157180
// The pid may have exited since we scanned it or we may not
158181
// have sufficient permission.
159-
return Ok(());
182+
return Ok(false);
160183
}
161184
};
162185
if status.tgid != pid {
163186
// This is a thread, not a process and we do not wish to scan it.
164-
return Ok(());
187+
return Ok(false);
165188
}
166189

167190
// If we haven't seen this process before, initialize its ProcessInfo.
@@ -174,7 +197,7 @@ impl Sampler {
174197
warn!("Couldn't read exe for pid {}: {:?}", pid, e);
175198
// The pid may have exited since we scanned it or we may not
176199
// have sufficient permission.
177-
return Ok(());
200+
return Ok(false);
178201
}
179202
};
180203
let comm = match proc_comm(pid).await {
@@ -183,7 +206,7 @@ impl Sampler {
183206
warn!("Couldn't read comm for pid {}: {:?}", pid, e);
184207
// The pid may have exited since we scanned it or we may not
185208
// have sufficient permission.
186-
return Ok(());
209+
return Ok(false);
187210
}
188211
};
189212
let cmdline = match proc_cmdline(pid).await {
@@ -192,7 +215,7 @@ impl Sampler {
192215
warn!("Couldn't read cmdline for pid {}: {:?}", pid, e);
193216
// The pid may have exited since we scanned it or we may not
194217
// have sufficient permission.
195-
return Ok(());
218+
return Ok(false);
196219
}
197220
};
198221
let pid_s = format!("{pid}");
@@ -238,7 +261,7 @@ impl Sampler {
238261
// which will happen if we don't have permissions or, more
239262
// likely, the process has exited.
240263
warn!("Couldn't process `/proc/{pid}/stat`: {e}");
241-
return Ok(());
264+
return Ok(false);
242265
}
243266

244267
if include_smaps {
@@ -317,10 +340,10 @@ impl Sampler {
317340
// which will happen if we don't have permissions or, more
318341
// likely, the process has exited.
319342
warn!("Couldn't process `/proc/{pid}/smaps_rollup`: {err}");
320-
return Ok(());
343+
return Ok(false);
321344
}
322345

323-
Ok(())
346+
Ok(true)
324347
}
325348
}
326349

0 commit comments

Comments
 (0)