mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-23 21:55:11 +02:00
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:
parent
5b33e8d6b4
commit
a5b95ae8b2
@ -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.
|
- [#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
|
## [0.4.7] - 2020-08-26
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
@ -122,8 +122,10 @@ impl Default for DataCollector {
|
|||||||
impl DataCollector {
|
impl DataCollector {
|
||||||
pub fn init(&mut self) {
|
pub fn init(&mut self) {
|
||||||
self.mem_total_kb = self.sys.get_total_memory();
|
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 {
|
if self.widgets_to_harvest.use_battery {
|
||||||
|
trace!("First run battery vec creation.");
|
||||||
if let Ok(battery_manager) = Manager::new() {
|
if let Ok(battery_manager) = Manager::new() {
|
||||||
if let Ok(batteries) = battery_manager.batteries() {
|
if let Ok(batteries) = battery_manager.batteries() {
|
||||||
let battery_list: Vec<Battery> = batteries.filter_map(Result::ok).collect();
|
let battery_list: Vec<Battery> = batteries.filter_map(Result::ok).collect();
|
||||||
|
@ -11,6 +11,9 @@ use std::collections::{hash_map::RandomState, HashMap};
|
|||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt};
|
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?
|
// TODO: Add value so we know if it's sorted ascending or descending by default?
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum ProcessSorting {
|
pub enum ProcessSorting {
|
||||||
@ -251,7 +254,9 @@ fn read_proc<S: core::hash::BuildHasher>(
|
|||||||
.entry(pid)
|
.entry(pid)
|
||||||
.or_insert_with(|| PrevProcDetails::new(pid));
|
.or_insert_with(|| PrevProcDetails::new(pid));
|
||||||
let stat_results = read_path_contents(&pid_stat.proc_stat_path)?;
|
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, '(')
|
.splitn(2, '(')
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.last()
|
.last()
|
||||||
@ -261,12 +266,36 @@ fn read_proc<S: core::hash::BuildHasher>(
|
|||||||
.last()
|
.last()
|
||||||
.ok_or(BottomError::MinorError)?
|
.ok_or(BottomError::MinorError)?
|
||||||
.to_string();
|
.to_string();
|
||||||
let command = {
|
let (command, name) = {
|
||||||
let cmd = read_path_contents(&pid_stat.proc_cmdline_path)?; // FIXME: [PROC] Use full proc name all the time
|
let cmd = read_path_contents(&pid_stat.proc_cmdline_path)?;
|
||||||
if cmd.trim().is_empty() {
|
let trimmed_cmd = cmd.trim();
|
||||||
format!("[{}]", name)
|
if trimmed_cmd.is_empty() {
|
||||||
|
(format!("[{}]", truncated_name), truncated_name)
|
||||||
} else {
|
} 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
|
let stat = stat_results
|
||||||
|
@ -42,8 +42,10 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
let config_path = read_config(matches.value_of("config_location"))
|
let config_path = read_config(matches.value_of("config_location"))
|
||||||
.context("Unable to access the given config file 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)
|
let mut config: Config = create_or_get_config(&config_path)
|
||||||
.context("Unable to properly parse or create the config file.")?;
|
.context("Unable to properly parse or create the config file.")?;
|
||||||
|
trace!("Current config: {:#?}", config);
|
||||||
|
|
||||||
// Get widget layout separately
|
// Get widget layout separately
|
||||||
let (widget_layout, default_widget_id, default_widget_type_option) =
|
let (widget_layout, default_widget_id, default_widget_type_option) =
|
||||||
@ -75,13 +77,17 @@ fn main() -> Result<()> {
|
|||||||
// Cleaning loop
|
// Cleaning loop
|
||||||
{
|
{
|
||||||
let cleaning_sender = sender.clone();
|
let cleaning_sender = sender.clone();
|
||||||
|
trace!("Initializing cleaning thread...");
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
thread::sleep(Duration::from_millis(
|
thread::sleep(Duration::from_millis(
|
||||||
constants::STALE_MAX_MILLISECONDS + 5000,
|
constants::STALE_MAX_MILLISECONDS + 5000,
|
||||||
));
|
));
|
||||||
|
trace!("Sending cleaning signal...");
|
||||||
if cleaning_sender.send(BottomEvent::Clean).is_err() {
|
if cleaning_sender.send(BottomEvent::Clean).is_err() {
|
||||||
|
trace!("Failed to send cleaning signal. Halting cleaning thread loop.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
trace!("Cleaning signal sent without errors.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,17 +615,23 @@ pub fn create_collection_thread(
|
|||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
trace!("Spawned collection thread.");
|
trace!("Spawned collection thread.");
|
||||||
let mut data_state = data_harvester::DataCollector::default();
|
let mut data_state = data_harvester::DataCollector::default();
|
||||||
|
trace!("Created initial data state.");
|
||||||
data_state.set_collected_data(used_widget_set);
|
data_state.set_collected_data(used_widget_set);
|
||||||
|
trace!("Set collected data.");
|
||||||
data_state.set_temperature_type(temp_type);
|
data_state.set_temperature_type(temp_type);
|
||||||
|
trace!("Set initial temp type.");
|
||||||
data_state.set_use_current_cpu_total(use_current_cpu_total);
|
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);
|
data_state.set_show_average_cpu(show_average_cpu);
|
||||||
|
trace!("Set showing average CPU.");
|
||||||
|
|
||||||
data_state.init();
|
data_state.init();
|
||||||
|
trace!("Data state is now fully initialized.");
|
||||||
loop {
|
loop {
|
||||||
trace!("Collecting...");
|
trace!("Collecting...");
|
||||||
let mut update_time = update_rate_in_milliseconds;
|
let mut update_time = update_rate_in_milliseconds;
|
||||||
if let Ok(message) = reset_receiver.try_recv() {
|
if let Ok(message) = reset_receiver.try_recv() {
|
||||||
trace!("Received message: {:?}", message);
|
trace!("Received message in collection thread: {:?}", message);
|
||||||
match message {
|
match message {
|
||||||
CollectionThreadEvent::Reset => {
|
CollectionThreadEvent::Reset => {
|
||||||
data_state.data.cleanup();
|
data_state.data.cleanup();
|
||||||
@ -650,6 +656,7 @@ pub fn create_collection_thread(
|
|||||||
trace!("Collection thread done updating. Sending data now...");
|
trace!("Collection thread done updating. Sending data now...");
|
||||||
data_state.data = data_harvester::Data::default();
|
data_state.data = data_harvester::Data::default();
|
||||||
if sender.send(event).is_err() {
|
if sender.send(event).is_err() {
|
||||||
|
trace!("Error sending from collection thread...");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
trace!("No problem sending from collection thread!");
|
trace!("No problem sending from collection thread!");
|
||||||
|
@ -20,7 +20,7 @@ pub mod layout_options;
|
|||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub flags: Option<ConfigFlags>,
|
pub flags: Option<ConfigFlags>,
|
||||||
pub colors: Option<ConfigColours>,
|
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 struct ConfigColours {
|
||||||
pub table_header_color: Option<String>,
|
pub table_header_color: Option<String>,
|
||||||
pub all_cpu_color: Option<String>,
|
pub all_cpu_color: Option<String>,
|
||||||
@ -176,7 +176,7 @@ pub struct ConfigColours {
|
|||||||
pub battery_colors: Option<Vec<String>>,
|
pub battery_colors: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
pub struct IgnoreList {
|
pub struct IgnoreList {
|
||||||
pub is_list_ignored: bool,
|
pub is_list_ignored: bool,
|
||||||
pub list: Vec<String>,
|
pub list: Vec<String>,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user