Skip to content
Merged
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
67 changes: 67 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,70 @@ jobs:
--object target/debug/pfiles --object target/debug/plgrp --object target/debug/prun \
--object target/debug/psig --object target/debug/pstop --object target/debug/ptree \
--object target/debug/pwait

build-i686:
name: Build (i686)
runs-on: ubuntu-24.04
permissions:
contents: read
env:
PKG_CONFIG_ALLOW_CROSS: 1
PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig
CARGO_BUILD_TARGET: i686-unknown-linux-gnu
steps:
- name: Check out
uses: actions/checkout@v6

- name: Install i686 cross-compilation dependencies
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install -y gcc-multilib libsystemd-dev:i386

- name: Install Rust i686 target
run: rustup target add i686-unknown-linux-gnu

- name: Check formatting
run: cargo fmt --all -- --check

- name: Check Markdown
uses: DavidAnson/markdownlint-cli2-action@v22

- name: Run Clippy
run: cargo clippy --all-targets --all-features

- name: Build
run: cargo build --verbose

- name: Test
run: cargo test --verbose

- name: Install coverage tooling
run: rustup component add llvm-tools-preview

- name: Build instrumented binaries for E2E coverage
run: |
RUSTFLAGS='-C instrument-coverage' \
cargo build --all-features --workspace --bins --examples

- name: Run coverage tests
run: |
mkdir -p target/coverage
RUSTFLAGS='-C instrument-coverage' \
LLVM_PROFILE_FILE='target/coverage/ptools-%p-%m.profraw' \
cargo test --all-features --workspace --tests --bins

- name: Report coverage
shell: bash
run: |
target_dir="target/i686-unknown-linux-gnu/debug"
host_target=$(rustc -vV | awk '/host:/ {print $2}')
llvm_bin="$(rustc --print sysroot)/lib/rustlib/${host_target}/bin"
"${llvm_bin}/llvm-profdata" merge -sparse target/coverage/*.profraw -o target/coverage/ptools.profdata
"${llvm_bin}/llvm-cov" report \
--ignore-filename-regex='/(\.cargo/registry|rustc)/' \
--instr-profile=target/coverage/ptools.profdata \
"${target_dir}/pargs" --object "${target_dir}/pauxv" --object "${target_dir}/penv" \
--object "${target_dir}/pfiles" --object "${target_dir}/plgrp" --object "${target_dir}/prun" \
--object "${target_dir}/psig" --object "${target_dir}/pstop" --object "${target_dir}/ptree" \
--object "${target_dir}/pwait"
8 changes: 6 additions & 2 deletions examples/pargs_penv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ fn apply_test_overrides_from_env() {
let hard = env::var("PTOOLS_TEST_SET_RLIMIT_NOFILE_HARD").ok();
match (soft, hard) {
(Some(soft), Some(hard)) => {
let soft = soft.parse::<u64>().expect("invalid NOFILE soft override");
let hard = hard.parse::<u64>().expect("invalid NOFILE hard override");
let soft = soft
.parse::<nix::libc::rlim_t>()
.expect("invalid NOFILE soft override");
let hard = hard
.parse::<nix::libc::rlim_t>()
.expect("invalid NOFILE hard override");
let lim = nix::libc::rlimit {
rlim_cur: soft,
rlim_max: hard,
Expand Down
6 changes: 3 additions & 3 deletions src/bin/pfiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,6 @@ fn print_open_flags(flags: u64) {
(OFlag::O_DIRECTORY, "O_DIRECTORY"),
(OFlag::O_DSYNC, "O_DSYNC"),
(OFlag::O_EXCL, "O_EXCL"),
(OFlag::O_LARGEFILE, "O_LARGEFILE"),
(OFlag::O_NOATIME, "O_NOATIME"),
(OFlag::O_NOCTTY, "O_NOCTTY"),
(OFlag::O_NOFOLLOW, "O_NOFOLLOW"),
Expand Down Expand Up @@ -1103,6 +1102,7 @@ fn print_fd_details(source: &dyn ProcSource, fd: u64, path: &Path) {
}
}

#[allow(clippy::unnecessary_cast)]
fn print_file(
source: &dyn ProcSource,
fd: u64,
Expand Down Expand Up @@ -1153,8 +1153,8 @@ fn print_file(
FileType::Posix(PosixFileType::Socket) => {
// Socket metadata is resolved from /proc/<pid>/net/*, so inode lookups are
// evaluated in the target process's network namespace.
if let Some(sock_info) = sockets.get(&stat_info.st_ino) {
debug_assert_eq!(sock_info.inode, stat_info.st_ino);
if let Some(sock_info) = sockets.get(&(stat_info.st_ino as u64)) {
debug_assert_eq!(sock_info.inode, stat_info.st_ino as u64);
// pidfd_open / pidfd_getfd for fd duplication -- live-only.
let sock_fd = duplicate_target_fd(pid, fd);
print_sockname(sock_info);
Expand Down
70 changes: 38 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,34 +176,35 @@ pub fn print_env(pid: u64) -> bool {
const AT_RSEQ_FEATURE_SIZE: u64 = 27;
const AT_RSEQ_ALIGN: u64 = 28;

#[allow(clippy::unnecessary_cast)]
const AUX_NAMES: &[(u64, &str)] = &[
(libc::AT_BASE, "AT_BASE"),
(libc::AT_BASE_PLATFORM, "AT_BASE_PLATFORM"),
(libc::AT_CLKTCK, "AT_CLKTCK"),
(libc::AT_EGID, "AT_EGID"),
(libc::AT_ENTRY, "AT_ENTRY"),
(libc::AT_EUID, "AT_EUID"),
(libc::AT_EXECFD, "AT_EXECFD"),
(libc::AT_EXECFN, "AT_EXECFN"),
(libc::AT_FLAGS, "AT_FLAGS"),
(libc::AT_GID, "AT_GID"),
(libc::AT_HWCAP2, "AT_HWCAP2"),
(libc::AT_HWCAP, "AT_HWCAP"),
(libc::AT_IGNORE, "AT_IGNORE"),
(libc::AT_MINSIGSTKSZ, "AT_MINSIGSTKSZ"),
(libc::AT_NOTELF, "AT_NOTELF"),
(libc::AT_NULL, "AT_NULL"),
(libc::AT_PAGESZ, "AT_PAGESZ"),
(libc::AT_PHDR, "AT_PHDR"),
(libc::AT_PHENT, "AT_PHENT"),
(libc::AT_PHNUM, "AT_PHNUM"),
(libc::AT_PLATFORM, "AT_PLATFORM"),
(libc::AT_RANDOM, "AT_RANDOM"),
(libc::AT_BASE as u64, "AT_BASE"),
(libc::AT_BASE_PLATFORM as u64, "AT_BASE_PLATFORM"),
(libc::AT_CLKTCK as u64, "AT_CLKTCK"),
(libc::AT_EGID as u64, "AT_EGID"),
(libc::AT_ENTRY as u64, "AT_ENTRY"),
(libc::AT_EUID as u64, "AT_EUID"),
(libc::AT_EXECFD as u64, "AT_EXECFD"),
(libc::AT_EXECFN as u64, "AT_EXECFN"),
(libc::AT_FLAGS as u64, "AT_FLAGS"),
(libc::AT_GID as u64, "AT_GID"),
(libc::AT_HWCAP2 as u64, "AT_HWCAP2"),
(libc::AT_HWCAP as u64, "AT_HWCAP"),
(libc::AT_IGNORE as u64, "AT_IGNORE"),
(libc::AT_MINSIGSTKSZ as u64, "AT_MINSIGSTKSZ"),
(libc::AT_NOTELF as u64, "AT_NOTELF"),
(libc::AT_NULL as u64, "AT_NULL"),
(libc::AT_PAGESZ as u64, "AT_PAGESZ"),
(libc::AT_PHDR as u64, "AT_PHDR"),
(libc::AT_PHENT as u64, "AT_PHENT"),
(libc::AT_PHNUM as u64, "AT_PHNUM"),
(libc::AT_PLATFORM as u64, "AT_PLATFORM"),
(libc::AT_RANDOM as u64, "AT_RANDOM"),
(AT_RSEQ_FEATURE_SIZE, "AT_RSEQ_FEATURE_SIZE"),
(AT_RSEQ_ALIGN, "AT_RSEQ_ALIGN"),
(libc::AT_SECURE, "AT_SECURE"),
(libc::AT_SYSINFO_EHDR, "AT_SYSINFO_EHDR"),
(libc::AT_UID, "AT_UID"),
(libc::AT_SECURE as u64, "AT_SECURE"),
(libc::AT_SYSINFO_EHDR as u64, "AT_SYSINFO_EHDR"),
(libc::AT_UID as u64, "AT_UID"),
];

fn aux_key_name(key: u64) -> String {
Expand Down Expand Up @@ -319,6 +320,7 @@ fn read_auxv_from(source: &dyn ProcSource) -> Option<Vec<(u64, u64)>> {
}

#[cfg(target_arch = "x86_64")]
#[allow(clippy::unnecessary_cast)]
fn decode_hwcap(key: u64, value: u64) -> Option<String> {
// AT_HWCAP on x86_64: CPUID leaf 1 EDX register bits
const HWCAP_NAMES: &[(u32, &str)] = &[
Expand Down Expand Up @@ -356,9 +358,9 @@ fn decode_hwcap(key: u64, value: u64) -> Option<String> {
// AT_HWCAP2 on x86_64: kernel-defined bits from asm/hwcap2.h
const HWCAP2_NAMES: &[(u32, &str)] = &[(0, "RING3MWAIT"), (1, "FSGSBASE")];

let table = if key == libc::AT_HWCAP {
let table = if key == libc::AT_HWCAP as u64 {
HWCAP_NAMES
} else if key == libc::AT_HWCAP2 {
} else if key == libc::AT_HWCAP2 as u64 {
HWCAP2_NAMES
} else {
return None;
Expand All @@ -378,6 +380,7 @@ fn decode_hwcap(key: u64, value: u64) -> Option<String> {
}

#[cfg(target_arch = "aarch64")]
#[allow(clippy::unnecessary_cast)]
fn decode_hwcap(key: u64, value: u64) -> Option<String> {
// AT_HWCAP on aarch64: bits from arch/arm64/include/uapi/asm/hwcap.h
const HWCAP_NAMES: &[(u32, &str)] = &[
Expand Down Expand Up @@ -464,9 +467,9 @@ fn decode_hwcap(key: u64, value: u64) -> Option<String> {
(44, "HBC"),
];

let table = if key == libc::AT_HWCAP {
let table = if key == libc::AT_HWCAP as u64 {
HWCAP_NAMES
} else if key == libc::AT_HWCAP2 {
} else if key == libc::AT_HWCAP2 as u64 {
HWCAP2_NAMES
} else {
return None;
Expand All @@ -490,12 +493,14 @@ fn decode_hwcap(_key: u64, _value: u64) -> Option<String> {
None
}

#[allow(clippy::unnecessary_cast)]
fn is_uid_auxv_key(key: u64) -> bool {
key == libc::AT_UID || key == libc::AT_EUID
key == libc::AT_UID as u64 || key == libc::AT_EUID as u64
}

#[allow(clippy::unnecessary_cast)]
fn is_gid_auxv_key(key: u64) -> bool {
key == libc::AT_GID || key == libc::AT_EGID
key == libc::AT_GID as u64 || key == libc::AT_EGID as u64
}

pub fn resolve_uid(uid: u32) -> Option<String> {
Expand All @@ -520,11 +525,12 @@ pub fn resolve_gid(gid: u32) -> Option<String> {
name.to_str().ok().map(str::to_string)
}

#[allow(clippy::unnecessary_cast)]
pub fn print_auxv_from(source: &dyn ProcSource) -> bool {
if let Some(auxv) = read_auxv_from(source) {
print_proc_summary_from(source);
for (key, value) in auxv {
if key == libc::AT_EXECFN {
if key == libc::AT_EXECFN as u64 {
let s = source
.read_exe()
.ok()
Expand Down