bug: use cmdline for Linux proc name if >=15 chars (#261)

This was the cause of some process names getting cut off and looking weird for Linux (and Linux only, I'm not directly responsible for the other OSes).

This also adds spaces in between command line flags. Before, they were usually separated by either spaces (which looked fine) or null terminators (which meant it looked like something was broken).
This commit is contained in:
Clement Tsang 2020-10-01 01:46:09 -04:00 committed by GitHub
parent 5b33e8d6b4
commit a5b95ae8b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 10 deletions

View File

@ -40,6 +40,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#253](https://github.com/ClementTsang/bottom/pull/253): Expanding a widget no longer overrides the widget title colour.
- [#261](https://github.com/ClementTsang/bottom/pull/261): Fixed process names occasionally showing up as truncated, due to only using `/proc/<PID>/stat` as our data source.
## [0.4.7] - 2020-08-26
### Bug Fixes

View File

@ -122,8 +122,10 @@ impl Default for DataCollector {
impl DataCollector {
pub fn init(&mut self) {
self.mem_total_kb = self.sys.get_total_memory();
trace!("Total memory in KB: {}", self.mem_total_kb);
if self.widgets_to_harvest.use_battery {
trace!("First run battery vec creation.");
if let Ok(battery_manager) = Manager::new() {
if let Ok(batteries) = battery_manager.batteries() {
let battery_list: Vec<Battery> = batteries.filter_map(Result::ok).collect();

View File

@ -11,6 +11,9 @@ use std::collections::{hash_map::RandomState, HashMap};
#[cfg(not(target_os = "linux"))]
use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt};
/// Maximum character length of a /proc/<PID>/stat process name.
const MAX_STAT_NAME_LEN: usize = 15;
// TODO: Add value so we know if it's sorted ascending or descending by default?
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum ProcessSorting {
@ -251,7 +254,9 @@ fn read_proc<S: core::hash::BuildHasher>(
.entry(pid)
.or_insert_with(|| PrevProcDetails::new(pid));
let stat_results = read_path_contents(&pid_stat.proc_stat_path)?;
let name = stat_results
// truncated_name may potentially be cut! Hence why we do the bit of code after...
let truncated_name = stat_results
.splitn(2, '(')
.collect::<Vec<_>>()
.last()
@ -261,12 +266,36 @@ fn read_proc<S: core::hash::BuildHasher>(
.last()
.ok_or(BottomError::MinorError)?
.to_string();
let command = {
let cmd = read_path_contents(&pid_stat.proc_cmdline_path)?; // FIXME: [PROC] Use full proc name all the time
if cmd.trim().is_empty() {
format!("[{}]", name)
let (command, name) = {
let cmd = read_path_contents(&pid_stat.proc_cmdline_path)?;
let trimmed_cmd = cmd.trim();
if trimmed_cmd.is_empty() {
(format!("[{}]", truncated_name), truncated_name)
} else {
cmd
// We split by spaces and null terminators.
let separated_strings = trimmed_cmd
.split_terminator(|c| c == '\0' || c == ' ')
.collect::<Vec<&str>>();
(
separated_strings.join(" "),
if truncated_name.len() >= MAX_STAT_NAME_LEN {
if let Some(first_part) = separated_strings.first() {
// We're only interested in the executable part... not the file path.
// That's for command.
first_part
.split('/')
.collect::<Vec<_>>()
.last()
.unwrap_or(&truncated_name.as_str())
.to_string()
} else {
truncated_name
}
} else {
truncated_name
},
)
}
};
let stat = stat_results

View File

@ -42,8 +42,10 @@ fn main() -> Result<()> {
let config_path = read_config(matches.value_of("config_location"))
.context("Unable to access the given config file location.")?;
trace!("Config path: {:?}", config_path);
let mut config: Config = create_or_get_config(&config_path)
.context("Unable to properly parse or create the config file.")?;
trace!("Current config: {:#?}", config);
// Get widget layout separately
let (widget_layout, default_widget_id, default_widget_type_option) =
@ -75,13 +77,17 @@ fn main() -> Result<()> {
// Cleaning loop
{
let cleaning_sender = sender.clone();
trace!("Initializing cleaning thread...");
thread::spawn(move || loop {
thread::sleep(Duration::from_millis(
constants::STALE_MAX_MILLISECONDS + 5000,
));
trace!("Sending cleaning signal...");
if cleaning_sender.send(BottomEvent::Clean).is_err() {
trace!("Failed to send cleaning signal. Halting cleaning thread loop.");
break;
}
trace!("Cleaning signal sent without errors.");
});
}

View File

@ -615,17 +615,23 @@ pub fn create_collection_thread(
thread::spawn(move || {
trace!("Spawned collection thread.");
let mut data_state = data_harvester::DataCollector::default();
trace!("Created initial data state.");
data_state.set_collected_data(used_widget_set);
trace!("Set collected data.");
data_state.set_temperature_type(temp_type);
trace!("Set initial temp type.");
data_state.set_use_current_cpu_total(use_current_cpu_total);
trace!("Set current CPU total.");
data_state.set_show_average_cpu(show_average_cpu);
trace!("Set showing average CPU.");
data_state.init();
trace!("Data state is now fully initialized.");
loop {
trace!("Collecting...");
let mut update_time = update_rate_in_milliseconds;
if let Ok(message) = reset_receiver.try_recv() {
trace!("Received message: {:?}", message);
trace!("Received message in collection thread: {:?}", message);
match message {
CollectionThreadEvent::Reset => {
data_state.data.cleanup();
@ -650,6 +656,7 @@ pub fn create_collection_thread(
trace!("Collection thread done updating. Sending data now...");
data_state.data = data_harvester::Data::default();
if sender.send(event).is_err() {
trace!("Error sending from collection thread...");
break;
}
trace!("No problem sending from collection thread!");

View File

@ -20,7 +20,7 @@ pub mod layout_options;
use anyhow::{Context, Result};
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Config {
pub flags: Option<ConfigFlags>,
pub colors: Option<ConfigColours>,
@ -154,7 +154,7 @@ impl WidgetIdEnabled {
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ConfigColours {
pub table_header_color: Option<String>,
pub all_cpu_color: Option<String>,
@ -176,7 +176,7 @@ pub struct ConfigColours {
pub battery_colors: Option<Vec<String>>,
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct IgnoreList {
pub is_list_ignored: bool,
pub list: Vec<String>,