Skip to content

Drop compatibility workaround and always use the generated taskstats def #13

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

Merged
merged 1 commit into from
Aug 15, 2024
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
208 changes: 5 additions & 203 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
pub(crate) mod netlink;
pub use model::*;

pub use c_headers::taskstats as __bindgen_taskstats;
pub use c_headers::taskstats;
use c_headers::{
__u16, __u32, __u64, __u8, TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, TASKSTATS_CMD_ATTR_PID,

Check warning on line 15 in src/lib.rs

View workflow job for this annotation

GitHub Actions / build

unused imports: `__u16`, `__u32`, `__u64`, and `__u8`
TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, TASKSTATS_CMD_ATTR_TGID, TASKSTATS_CMD_GET,
TASKSTATS_GENL_NAME, TASKSTATS_TYPE_AGGR_PID, TASKSTATS_TYPE_AGGR_TGID, TASKSTATS_TYPE_NULL,
TASKSTATS_TYPE_PID, TASKSTATS_TYPE_STATS, TASKSTATS_TYPE_TGID,
Expand Down Expand Up @@ -279,227 +279,29 @@
}
}

/// This is custom copy of the generated `struct taskstats` from linux version 3.10.0 and in this crate
/// this type is used to read binary data transferred from linux kernel.
/// The reason of doing this despite the `bindgen` generates rust bindings including `struct taskstats`
/// is due to potential corruption of on-memory struct layout likely caused by old clang version.
/// ref: https://github.com/rust-lang/rust-bindgen/issues/867
/// Specifically, when `bindgen` generates `struct taskstats` with older clang version, the resulting
/// struct defined in generated rust source code contains size padding as the last member of the struct
/// causing offset of members after `ac_uid` to shift 4-byte or more and the result data becomes unreliable.
/// The `bindgen` generated definition works well in environment that uses newer clang versions, but I
/// decided to use copied definition of this struct for the time being by following considerations:
/// * The struct definition rarely evolves.
/// * Returning corrupted data silently is critical and much worse than not providing from the beginning.
/// * If user of this crate still needs to access the exactly original definition generated by `bindgen`,
/// it might still be possible by casting type to `__bindgen_taskstats` exported by this crate.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct taskstats {
pub version: __u16,
pub ac_exitcode: __u32,
pub ac_flag: __u8,
pub ac_nice: __u8,
pub cpu_count: __u64,
pub cpu_delay_total: __u64,
pub blkio_count: __u64,
pub blkio_delay_total: __u64,
pub swapin_count: __u64,
pub swapin_delay_total: __u64,
pub cpu_run_real_total: __u64,
pub cpu_run_virtual_total: __u64,
pub ac_comm: [::std::os::raw::c_char; 32usize],
pub ac_sched: __u8,
pub ac_pad: [__u8; 3usize],
pub __unused_padding: u32,
pub ac_uid: __u32,
pub ac_gid: __u32,
pub ac_pid: __u32,
pub ac_ppid: __u32,
pub ac_btime: __u32,
pub ac_etime: __u64,
pub ac_utime: __u64,
pub ac_stime: __u64,
pub ac_minflt: __u64,
pub ac_majflt: __u64,
pub coremem: __u64,
pub virtmem: __u64,
pub hiwater_rss: __u64,
pub hiwater_vm: __u64,
pub read_char: __u64,
pub write_char: __u64,
pub read_syscalls: __u64,
pub write_syscalls: __u64,
pub read_bytes: __u64,
pub write_bytes: __u64,
pub cancelled_write_bytes: __u64,
pub nvcsw: __u64,
pub nivcsw: __u64,
pub ac_utimescaled: __u64,
pub ac_stimescaled: __u64,
pub cpu_scaled_run_real_total: __u64,
pub freepages_count: __u64,
pub freepages_delay_total: __u64,
}

#[cfg(test)]
mod tests {
use super::*;

Check warning on line 284 in src/lib.rs

View workflow job for this annotation

GitHub Actions / build

unused import: `super::*`

#[cfg(test_priv)]

Check warning on line 286 in src/lib.rs

View workflow job for this annotation

GitHub Actions / build

unexpected `cfg` condition name: `test_priv`
#[test]
fn test_pid_stats() {
let client = Client::open().unwrap();
let ts = client.pid_stats(std::process::id()).unwrap();

// Just asserts some fields which do likely have positive values
assert!(ts.cpu.utime_total.as_nanos() > 0);
assert!(ts.memory.rss_total > 0);
assert!(ts.delays.cpu.delay_total.as_nanos() > 0);
assert!(ts.cpu.virtual_time_total.as_nanos() > 0);
}

#[cfg(test_priv)]

Check warning on line 297 in src/lib.rs

View workflow job for this annotation

GitHub Actions / build

unexpected `cfg` condition name: `test_priv`
#[test]
fn test_tgid_stats() {
let client = Client::open().unwrap();
let ts = client.tgid_stats(std::process::id()).unwrap();

// Just asserts some fields which do likely have positive values
assert!(ts.cpu.utime_total.as_nanos() > 0);
assert!(ts.memory.rss_total > 0);
}

#[test]
fn test_struct_taskstats_alignment() {
// Automatically generated by tools/gen_layout_test.sh
assert_eq!(328, std::mem::size_of::<taskstats>());
assert_eq!(0, unsafe {
&(*(std::ptr::null::<taskstats>())).version as *const _ as usize
});
assert_eq!(4, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_exitcode as *const _ as usize
});
assert_eq!(8, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_flag as *const _ as usize
});
assert_eq!(9, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_nice as *const _ as usize
});
assert_eq!(16, unsafe {
&(*(std::ptr::null::<taskstats>())).cpu_count as *const _ as usize
});
assert_eq!(24, unsafe {
&(*(std::ptr::null::<taskstats>())).cpu_delay_total as *const _ as usize
});
assert_eq!(32, unsafe {
&(*(std::ptr::null::<taskstats>())).blkio_count as *const _ as usize
});
assert_eq!(40, unsafe {
&(*(std::ptr::null::<taskstats>())).blkio_delay_total as *const _ as usize
});
assert_eq!(48, unsafe {
&(*(std::ptr::null::<taskstats>())).swapin_count as *const _ as usize
});
assert_eq!(56, unsafe {
&(*(std::ptr::null::<taskstats>())).swapin_delay_total as *const _ as usize
});
assert_eq!(64, unsafe {
&(*(std::ptr::null::<taskstats>())).cpu_run_real_total as *const _ as usize
});
assert_eq!(72, unsafe {
&(*(std::ptr::null::<taskstats>())).cpu_run_virtual_total as *const _ as usize
});
assert_eq!(80, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_comm as *const _ as usize
});
assert_eq!(112, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_sched as *const _ as usize
});
assert_eq!(113, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_pad as *const _ as usize
});
assert_eq!(120, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_uid as *const _ as usize
});
assert_eq!(124, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_gid as *const _ as usize
});
assert_eq!(128, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_pid as *const _ as usize
});
assert_eq!(132, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_ppid as *const _ as usize
});
assert_eq!(136, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_btime as *const _ as usize
});
assert_eq!(144, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_etime as *const _ as usize
});
assert_eq!(152, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_utime as *const _ as usize
});
assert_eq!(160, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_stime as *const _ as usize
});
assert_eq!(168, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_minflt as *const _ as usize
});
assert_eq!(176, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_majflt as *const _ as usize
});
assert_eq!(184, unsafe {
&(*(std::ptr::null::<taskstats>())).coremem as *const _ as usize
});
assert_eq!(192, unsafe {
&(*(std::ptr::null::<taskstats>())).virtmem as *const _ as usize
});
assert_eq!(200, unsafe {
&(*(std::ptr::null::<taskstats>())).hiwater_rss as *const _ as usize
});
assert_eq!(208, unsafe {
&(*(std::ptr::null::<taskstats>())).hiwater_vm as *const _ as usize
});
assert_eq!(216, unsafe {
&(*(std::ptr::null::<taskstats>())).read_char as *const _ as usize
});
assert_eq!(224, unsafe {
&(*(std::ptr::null::<taskstats>())).write_char as *const _ as usize
});
assert_eq!(232, unsafe {
&(*(std::ptr::null::<taskstats>())).read_syscalls as *const _ as usize
});
assert_eq!(240, unsafe {
&(*(std::ptr::null::<taskstats>())).write_syscalls as *const _ as usize
});
assert_eq!(248, unsafe {
&(*(std::ptr::null::<taskstats>())).read_bytes as *const _ as usize
});
assert_eq!(256, unsafe {
&(*(std::ptr::null::<taskstats>())).write_bytes as *const _ as usize
});
assert_eq!(264, unsafe {
&(*(std::ptr::null::<taskstats>())).cancelled_write_bytes as *const _ as usize
});
assert_eq!(272, unsafe {
&(*(std::ptr::null::<taskstats>())).nvcsw as *const _ as usize
});
assert_eq!(280, unsafe {
&(*(std::ptr::null::<taskstats>())).nivcsw as *const _ as usize
});
assert_eq!(288, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_utimescaled as *const _ as usize
});
assert_eq!(296, unsafe {
&(*(std::ptr::null::<taskstats>())).ac_stimescaled as *const _ as usize
});
assert_eq!(304, unsafe {
&(*(std::ptr::null::<taskstats>())).cpu_scaled_run_real_total as *const _ as usize
});
assert_eq!(312, unsafe {
&(*(std::ptr::null::<taskstats>())).freepages_count as *const _ as usize
});
assert_eq!(320, unsafe {
&(*(std::ptr::null::<taskstats>())).freepages_delay_total as *const _ as usize
});
assert!(ts.delays.cpu.delay_total.as_nanos() > 0);
assert!(ts.cpu.virtual_time_total.as_nanos() > 0);
}
}
2 changes: 1 addition & 1 deletion src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub const TASKSTATS_SIZE: usize = const_max(
/// There are more (but may not much interested) fields in the original
/// `struct taskstats` and they are accessible through obtaining the original
/// struct by `TaskStats#inner()`.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
pub struct TaskStats {
pub(crate) inner_buf: [u8; TASKSTATS_SIZE],
/// The target task ID
Expand Down
Loading