mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-26 23:24:20 +02:00
bug: fix uptime calculation for Linux (#1410)
* bug: fix uptime for linux Use another calculation to determine the uptime of a process on Linux. * appease clippy * changelog * edit add
This commit is contained in:
parent
0068f8a744
commit
3ee883420a
@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- [#1314](https://github.com/ClementTsang/bottom/pull/1314): Fix fat32 mounts not showing up in macOS.
|
- [#1314](https://github.com/ClementTsang/bottom/pull/1314): Fix fat32 mounts not showing up in macOS.
|
||||||
- [#1355](https://github.com/ClementTsang/bottom/pull/1355): Reduce chances of non-D0 devices waking up due to temperature checks on Linux.
|
- [#1355](https://github.com/ClementTsang/bottom/pull/1355): Reduce chances of non-D0 devices waking up due to temperature checks on Linux.
|
||||||
|
- [#1410](https://github.com/ClementTsang/bottom/pull/1410): Fix uptime calculation for Linux.
|
||||||
|
|
||||||
## [0.9.6] - 2023-08-26
|
## [0.9.6] - 2023-08-26
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ pub struct ProcessHarvest {
|
|||||||
/// The current state of the process (e.g. zombie, asleep).
|
/// The current state of the process (e.g. zombie, asleep).
|
||||||
pub process_state: (String, char),
|
pub process_state: (String, char),
|
||||||
|
|
||||||
/// Cumulative total CPU time used.
|
/// Cumulative process uptime.
|
||||||
pub time: Duration,
|
pub time: Duration,
|
||||||
|
|
||||||
/// This is the *effective* user ID of the process. This is only used on Unix platforms.
|
/// This is the *effective* user ID of the process. This is only used on Unix platforms.
|
||||||
@ -109,7 +109,7 @@ impl ProcessHarvest {
|
|||||||
self.write_bytes_per_sec += rhs.write_bytes_per_sec;
|
self.write_bytes_per_sec += rhs.write_bytes_per_sec;
|
||||||
self.total_read_bytes += rhs.total_read_bytes;
|
self.total_read_bytes += rhs.total_read_bytes;
|
||||||
self.total_write_bytes += rhs.total_write_bytes;
|
self.total_write_bytes += rhs.total_write_bytes;
|
||||||
self.time += rhs.time;
|
self.time = self.time.max(rhs.time);
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
{
|
{
|
||||||
self.gpu_mem += rhs.gpu_mem;
|
self.gpu_mem += rhs.gpu_mem;
|
||||||
|
@ -132,9 +132,7 @@ fn get_linux_cpu_usage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_proc(
|
fn read_proc(
|
||||||
prev_proc: &PrevProcDetails, process: Process, cpu_usage: f64, cpu_fraction: f64,
|
prev_proc: &PrevProcDetails, process: Process, args: ReadProcArgs, user_table: &mut UserTable,
|
||||||
use_current_cpu_total: bool, time_difference_in_secs: u64, total_memory: u64,
|
|
||||||
user_table: &mut UserTable,
|
|
||||||
) -> error::Result<(ProcessHarvest, u64)> {
|
) -> error::Result<(ProcessHarvest, u64)> {
|
||||||
let Process {
|
let Process {
|
||||||
pid: _,
|
pid: _,
|
||||||
@ -144,6 +142,15 @@ fn read_proc(
|
|||||||
cmdline,
|
cmdline,
|
||||||
} = process;
|
} = process;
|
||||||
|
|
||||||
|
let ReadProcArgs {
|
||||||
|
use_current_cpu_total,
|
||||||
|
cpu_usage,
|
||||||
|
cpu_fraction,
|
||||||
|
total_memory,
|
||||||
|
time_difference_in_secs,
|
||||||
|
uptime,
|
||||||
|
} = args;
|
||||||
|
|
||||||
let (command, name) = {
|
let (command, name) = {
|
||||||
let truncated_name = stat.comm.as_str();
|
let truncated_name = stat.comm.as_str();
|
||||||
if let Ok(cmdline) = cmdline {
|
if let Ok(cmdline) = cmdline {
|
||||||
@ -231,7 +238,7 @@ fn read_proc(
|
|||||||
if ticks_per_sec == 0 {
|
if ticks_per_sec == 0 {
|
||||||
Duration::ZERO
|
Duration::ZERO
|
||||||
} else {
|
} else {
|
||||||
Duration::from_secs(stat.utime + stat.stime) / ticks_per_sec
|
Duration::from_secs(uptime.saturating_sub(stat.start_time / ticks_per_sec as u64))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Duration::ZERO
|
Duration::ZERO
|
||||||
@ -279,6 +286,17 @@ fn is_str_numeric(s: &str) -> bool {
|
|||||||
s.chars().all(|c| c.is_ascii_digit())
|
s.chars().all(|c| c.is_ascii_digit())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// General args to keep around for reading proc data.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(crate) struct ReadProcArgs {
|
||||||
|
pub(crate) use_current_cpu_total: bool,
|
||||||
|
pub(crate) cpu_usage: f64,
|
||||||
|
pub(crate) cpu_fraction: f64,
|
||||||
|
pub(crate) total_memory: u64,
|
||||||
|
pub(crate) time_difference_in_secs: u64,
|
||||||
|
pub(crate) uptime: u64,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn linux_process_data(
|
pub(crate) fn linux_process_data(
|
||||||
collector: &mut DataCollector, time_difference_in_secs: u64,
|
collector: &mut DataCollector, time_difference_in_secs: u64,
|
||||||
) -> error::Result<Vec<ProcessHarvest>> {
|
) -> error::Result<Vec<ProcessHarvest>> {
|
||||||
@ -329,6 +347,15 @@ pub(crate) fn linux_process_data(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let args = ReadProcArgs {
|
||||||
|
use_current_cpu_total,
|
||||||
|
cpu_usage,
|
||||||
|
cpu_fraction,
|
||||||
|
total_memory,
|
||||||
|
time_difference_in_secs,
|
||||||
|
uptime: sysinfo::System::uptime(),
|
||||||
|
};
|
||||||
|
|
||||||
let process_vector: Vec<ProcessHarvest> = pids
|
let process_vector: Vec<ProcessHarvest> = pids
|
||||||
.filter_map(|pid_path| {
|
.filter_map(|pid_path| {
|
||||||
if let Ok(process) = Process::from_path(pid_path) {
|
if let Ok(process) = Process::from_path(pid_path) {
|
||||||
@ -336,16 +363,9 @@ pub(crate) fn linux_process_data(
|
|||||||
let prev_proc_details = pid_mapping.entry(pid).or_default();
|
let prev_proc_details = pid_mapping.entry(pid).or_default();
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
if let Ok((mut process_harvest, new_process_times)) = read_proc(
|
if let Ok((mut process_harvest, new_process_times)) =
|
||||||
prev_proc_details,
|
read_proc(prev_proc_details, process, args, user_table)
|
||||||
process,
|
{
|
||||||
cpu_usage,
|
|
||||||
cpu_fraction,
|
|
||||||
use_current_cpu_total,
|
|
||||||
time_difference_in_secs,
|
|
||||||
total_memory,
|
|
||||||
user_table,
|
|
||||||
) {
|
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
if let Some(gpus) = &collector.gpu_pids {
|
if let Some(gpus) = &collector.gpu_pids {
|
||||||
gpus.iter().for_each(|gpu| {
|
gpus.iter().for_each(|gpu| {
|
||||||
|
@ -26,7 +26,8 @@ fn next_part<'a>(iter: &mut impl Iterator<Item = &'a str>) -> Result<&'a str, io
|
|||||||
.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))
|
.ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper around the data in `/proc/<PID>/stat`. For documentation, see [here](https://man7.org/linux/man-pages/man5/proc.5.html).
|
/// A wrapper around the data in `/proc/<PID>/stat`. For documentation, see
|
||||||
|
/// [here](https://man7.org/linux/man-pages/man5/proc.5.html).
|
||||||
///
|
///
|
||||||
/// Note this does not necessarily get all fields, only the ones we use in bottom.
|
/// Note this does not necessarily get all fields, only the ones we use in bottom.
|
||||||
pub(crate) struct Stat {
|
pub(crate) struct Stat {
|
||||||
@ -47,6 +48,9 @@ pub(crate) struct Stat {
|
|||||||
|
|
||||||
/// The resident set size, or the number of pages the process has in real memory.
|
/// The resident set size, or the number of pages the process has in real memory.
|
||||||
pub rss: u64,
|
pub rss: u64,
|
||||||
|
|
||||||
|
/// The start time of the process, represented in clock ticks.
|
||||||
|
pub start_time: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stat {
|
impl Stat {
|
||||||
@ -76,18 +80,19 @@ impl Stat {
|
|||||||
.chars()
|
.chars()
|
||||||
.next()
|
.next()
|
||||||
.ok_or_else(|| anyhow!("missing state"))?;
|
.ok_or_else(|| anyhow!("missing state"))?;
|
||||||
|
|
||||||
let ppid: Pid = next_part(&mut rest)?.parse()?;
|
let ppid: Pid = next_part(&mut rest)?.parse()?;
|
||||||
|
|
||||||
// Skip 9 fields until utime (pgrp, session, tty_nr, tpgid, flags, minflt, cminflt, majflt, cmajflt).
|
// Skip 9 fields until utime (pgrp, session, tty_nr, tpgid, flags, minflt, cminflt, majflt, cmajflt).
|
||||||
let mut rest = rest.skip(9);
|
let mut rest = rest.skip(9);
|
||||||
|
|
||||||
let utime: u64 = next_part(&mut rest)?.parse()?;
|
let utime: u64 = next_part(&mut rest)?.parse()?;
|
||||||
let stime: u64 = next_part(&mut rest)?.parse()?;
|
let stime: u64 = next_part(&mut rest)?.parse()?;
|
||||||
|
|
||||||
// Skip 8 fields until rss (cutime, cstime, priority, nice, num_threads, itrealvalue, starttime, vsize).
|
// Skip 6 fields until starttime (cutime, cstime, priority, nice, num_threads, itrealvalue).
|
||||||
let mut rest = rest.skip(8);
|
let mut rest = rest.skip(6);
|
||||||
|
let start_time: u64 = next_part(&mut rest)?.parse()?;
|
||||||
|
|
||||||
|
// Skip one field until rss (vsize)
|
||||||
|
let mut rest = rest.skip(1);
|
||||||
let rss: u64 = next_part(&mut rest)?.parse()?;
|
let rss: u64 = next_part(&mut rest)?.parse()?;
|
||||||
|
|
||||||
Ok(Stat {
|
Ok(Stat {
|
||||||
@ -97,6 +102,7 @@ impl Stat {
|
|||||||
utime,
|
utime,
|
||||||
stime,
|
stime,
|
||||||
rss,
|
rss,
|
||||||
|
start_time,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,6 +263,7 @@ impl ProcWidgetData {
|
|||||||
self.wps += other.wps;
|
self.wps += other.wps;
|
||||||
self.total_read += other.total_read;
|
self.total_read += other.total_read;
|
||||||
self.total_write += other.total_write;
|
self.total_write += other.total_write;
|
||||||
|
self.time = self.time.max(other.time);
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
{
|
{
|
||||||
self.gpu_mem_usage = match (&self.gpu_mem_usage, &other.gpu_mem_usage) {
|
self.gpu_mem_usage = match (&self.gpu_mem_usage, &other.gpu_mem_usage) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user