Skip to content

bug: switch over to procfs for linux mem usage #547

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 3 commits into from
Jul 18, 2021
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
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Bug Fixes

- [#542](https://github.com/ClementTsang/bottom/pull/542): Fixes missing config options in the default generated config file.
- [#545](https://github.com/ClementTsang/bottom/pull/545): Fixes inaccurate memory usage/totals in macOS and Linux.
- [#545](https://github.com/ClementTsang/bottom/pull/545): Fixes inaccurate memory usage/totals in macOS and Linux, switch unit to binary prefix.

## Changes

- [#547](https://github.com/ClementTsang/bottom/pull/547): Switch memory usage calculation to match htop.

## [0.6.2] - 2021-06-26

Expand Down
10 changes: 10 additions & 0 deletions docs/content/usage/widgets/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,13 @@ Note that key bindings are generally case-sensitive.
| Binding | Action |
| ------------ | -------------------------------------------------------------- |
| ++"Scroll"++ | Scrolling up or down zooms in or out of the graph respectively |

## Calculations

Memory usage is calculated using the following formula based on values from `/proc/meminfo` (based on [htop's implementation](https://github.com/htop-dev/htop/blob/976c6123f41492aaf613b9d172eef1842fb7b0a3/linux/LinuxProcessList.c#L1584)):

```
MemTotal - MemFree - Buffers - (Cached + SReclaimable - Shmem)
```

You can find more info on `/proc/meminfo` and its fields [here](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-meminfo).
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,38 @@ pub async fn get_mem_data(
}

pub async fn get_ram_data() -> crate::utils::error::Result<Option<MemHarvest>> {
let memory = heim::memory::memory().await?;

let (mem_total_in_kib, mem_used_in_kib) = {
#[cfg(target_os = "linux")]
{
use heim::memory::os::linux::MemoryExt;
use heim::units::information::kilobyte;
let mem_info = procfs::Meminfo::new()?;

// For Linux, the "kilobyte" value in the .total call is actually kibibytes - see
// https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/deployment_guide/s2-proc-meminfo
// Let's preface this by saying that memory usage calculations are... not straightforward.
// There are conflicting implementations everywhere.
//
// Now that we've added this preface (mainly for future reference), the current implementation below for usage
// is based on htop's calculation formula. See
// https://github.com/htop-dev/htop/blob/976c6123f41492aaf613b9d172eef1842fb7b0a3/linux/LinuxProcessList.c#L1584
// for implementation details as of writing.
//
// Heim parses this as kilobytes (https://github.com/heim-rs/heim/blob/master/heim-memory/src/sys/linux/memory.rs#L82)
// even though it probably shouldn't...
// Another implementation, commonly used in other things, is to skip the shmem part of the calculation,
// which matches gopsutil and stuff like free.

(
memory.total().get::<kilobyte>(),
memory.used().get::<kilobyte>(),
)
let total = mem_info.mem_total / 1024;
let cached_mem =
mem_info.cached + mem_info.s_reclaimable.unwrap_or(0) - mem_info.shmem.unwrap_or(0);
let used_diff = (mem_info.mem_free + cached_mem + mem_info.buffers) / 1024;
let used = if total >= used_diff {
total - used_diff
} else {
total - mem_info.mem_free
};

(total, used)
}
#[cfg(target_os = "macos")]
{
let memory = heim::memory::memory().await?;

use heim::memory::os::macos::MemoryExt;
use heim::units::information::kibibyte;
(
Expand All @@ -53,6 +64,8 @@ pub async fn get_ram_data() -> crate::utils::error::Result<Option<MemHarvest>> {
}
#[cfg(target_os = "windows")]
{
let memory = heim::memory::memory().await?;

use heim::units::information::kibibyte;
let mem_total_in_kib = memory.total().get::<kibibyte>();
(
Expand Down
4 changes: 2 additions & 2 deletions src/app/data_harvester/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

cfg_if::cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] {
pub mod heim;
pub use self::heim::*;
pub mod general;
pub use self::general::*;
}
}