Skip to content

Add /proc/pid/syscall parsing support #350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
31 changes: 29 additions & 2 deletions procfs-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ where
let val = expect!(iter.next());
match FromStr::from_str(val) {
Ok(u) => Ok(u),
Err(..) => Err(build_internal_error!("Failed to convert")),
Err(..) => Err(build_internal_error!("Failed to convert", val)),
}
}

Expand All @@ -339,7 +339,34 @@ where
};
match FromStr::from_str(val) {
Ok(u) => Ok(Some(u)),
Err(..) => Err(build_internal_error!("Failed to convert")),
Err(..) => Err(build_internal_error!("Failed to convert (optional)", val)),
}
}

fn from_iter_radix<'a, I, U>(i: I, radix: u32) -> ProcResult<U>
where
I: IntoIterator<Item = &'a str>,
U: FromStrRadix,
{
let mut iter = i.into_iter();
let val = expect!(iter.next());

let val = match radix {
16 => {
if let Some(val) = val.strip_prefix("0x") {
val
} else if let Some(val) = val.strip_prefix("0X") {
val
} else {
val
}
}
_ => val,
};

match FromStrRadix::from_str_radix(val, radix) {
Ok(u) => Ok(u),
Err(..) => Err(build_internal_error!("Failed to convert (radix)", val)),
}
}

Expand Down
3 changes: 3 additions & 0 deletions procfs-core/src/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ pub use schedstat::*;
mod smaps_rollup;
pub use smaps_rollup::*;

mod syscall;
pub use syscall::*;

mod pagemap;
pub use pagemap::*;

Expand Down
62 changes: 62 additions & 0 deletions procfs-core/src/process/syscall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::{from_iter, from_iter_radix, ProcResult};
#[cfg(feature = "serde1")]
use serde::{Deserialize, Serialize};

use std::io::Read;

/// Syscall information about the process, based on the `/proc/<pid>/syscall` file.
///
/// New variants to this enum may be added at any time (even without a major or minor semver bump).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub enum Syscall {
/// The process is running, and so the values are not present
Running,
Blocked {
/// The syscall this process is blocked on.
/// If the syscall_number is -1, then not blocked on a syscall (blocked for another reason).
/// Note that the rest of the values are still filled in.
syscall_number: i64,
/// The argument registers
argument_registers: [u64; 6],
/// e.g. rsp
stack_pointer: u64,
/// e.g. rip
program_counter: u64,
},
}

impl crate::FromRead for Syscall {
fn from_read<R: Read>(mut r: R) -> ProcResult<Self> {
// read in entire thing, this is only going to be 1 line
let mut buf = Vec::with_capacity(512);
r.read_to_end(&mut buf)?;

let line = String::from_utf8_lossy(&buf);
let buf = line.trim();

if buf == "running" {
Ok(Self::Running)
} else {
let mut values = buf.split(' ');

let syscall_number: i64 = expect!(from_iter(&mut values), "failed to read syscall number");

let mut argument_registers: [u64; 6] = [0; 6];
for arg_reg in argument_registers.iter_mut() {
*arg_reg = expect!(from_iter_radix(&mut values, 16), "failed to read argument register");
}

let stack_pointer: u64 = expect!(from_iter_radix(&mut values, 16), "failed to read stack pointer");
let program_counter: u64 = expect!(from_iter_radix(&mut values, 16), "failed to read program counter");

Ok(Self::Blocked {
syscall_number,
argument_registers,
stack_pointer,
program_counter,
})
}
}
}
5 changes: 5 additions & 0 deletions procfs/src/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,11 @@ impl Process {
self.read("schedstat")
}

/// Returns the status info from `/proc/[pid]/syscall`.
pub fn syscall(&self) -> ProcResult<Syscall> {
self.read("syscall")
}

/// Iterate over all the [`Task`]s (aka Threads) in this process
///
/// Note that the iterator does not receive a snapshot of tasks, it is a
Expand Down
12 changes: 10 additions & 2 deletions procfs/src/process/task.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::io::Read;
use std::path::{Path, PathBuf};

use super::{FileWrapper, Io, Schedstat, Stat, Status};
use super::{FileWrapper, Io, Schedstat, Stat, Status, Syscall};
use crate::{ProcError, ProcResult};
use procfs_core::FromRead;
use rustix::fd::{BorrowedFd, OwnedFd};
Expand Down Expand Up @@ -77,6 +77,11 @@ impl Task {
self.read("schedstat")
}

/// Returns the status info from `/proc/<pid>/task/<tid>/syscall`.
pub fn syscall(&self) -> ProcResult<Syscall> {
self.read("syscall")
}

/// Thread children from `/proc/<pid>/task/<tid>/children`
///
/// WARNING:
Expand Down Expand Up @@ -117,7 +122,7 @@ impl Task {

#[cfg(test)]
mod tests {
use crate::process::Io;
use crate::process::{Io, Syscall};
use crate::ProcError;
use rustix;
use std::process;
Expand Down Expand Up @@ -191,6 +196,7 @@ mod tests {
let stat = task.stat().unwrap();
let status = task.status().unwrap();
let io = task.io().unwrap();
let syscall = task.syscall().unwrap();

summed_io.rchar += io.rchar;
summed_io.wchar += io.wchar;
Expand All @@ -204,6 +210,7 @@ mod tests {
found_one = true;
assert!(io.rchar >= bytes_to_read);
assert!(stat.utime >= 50, "utime({}) too small", stat.utime);
assert!(matches!(syscall, Syscall::Blocked { .. }), "{:?}", syscall);
}
if stat.comm == "two" && status.name == "two" {
found_two = true;
Expand All @@ -213,6 +220,7 @@ mod tests {
assert!(io.rchar < bytes_to_read);
assert_eq!(io.wchar, 0);
assert_eq!(stat.utime, 0);
assert!(matches!(syscall, Syscall::Blocked { .. }), "{:?}", syscall);
}
}

Expand Down
3 changes: 2 additions & 1 deletion support.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ This is an approximate list of all the files under the `/proc` mount, and an ind
* [x] `/proc/[pid]/stat`
* [x] `/proc/[pid]/statm`
* [x] `/proc/[pid]/status`
* [ ] `/proc/[pid]/syscall`
* [x] `/proc/[pid]/syscall`
* [ ] `/proc/[pid]/task`
* [x] `/proc/[pid]/task/[tid]/stat`
* [x] `/proc/[pid]/task/[tid]/status`
* [x] `/proc/[pid]/task/[tid]/syscall`
* [x] `/proc/[pid]/task/[tid]/io`
* [x] `/proc/[pid]/task/[tid]/children`
* [ ] `/proc/[pid]/timers`
Expand Down
Loading