summaryrefslogtreecommitdiffstats
path: root/src/osx
diff options
context:
space:
mode:
authorJos Dehaes <jos.dehaes@gmail.com>2021-10-02 23:48:28 +0200
committeraristocratos <gnmjpl@gmail.com>2021-10-06 12:45:29 +0200
commit3db9d6647650bd836201b8150e320a7fbf28e3c2 (patch)
tree254b077d6422929998b3edaebc7ff28228ac1d53 /src/osx
parentf8acb2f8542429677116799ddb5f442488cf3f4f (diff)
first infos on macos: memory used & free
Diffstat (limited to 'src/osx')
-rw-r--r--src/osx/btop_collect.cpp1561
1 files changed, 191 insertions, 1370 deletions
diff --git a/src/osx/btop_collect.cpp b/src/osx/btop_collect.cpp
index d8ea597..0cb8213 100644
--- a/src/osx/btop_collect.cpp
+++ b/src/osx/btop_collect.cpp
@@ -24,22 +24,26 @@ tab-size = 4
#include <regex>
#include <sys/statvfs.h>
#include <netdb.h>
+#include <string>
#include <ifaddrs.h>
#include <net/if.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
#include <btop_shared.hpp>
#include <btop_config.hpp>
#include <btop_tools.hpp>
-using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
+using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
namespace fs = std::filesystem;
namespace rng = std::ranges;
using namespace Tools;
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
-namespace Cpu {
+namespace Cpu
+{
vector<long long> core_old_totals;
vector<long long> core_old_idles;
vector<string> available_fields;
@@ -57,7 +61,8 @@ namespace Cpu {
//* Search /proc/cpuinfo for a cpu name
string get_cpuName();
- struct Sensor {
+ struct Sensor
+ {
fs::path path;
string label;
int64_t temp = 0;
@@ -71,82 +76,64 @@ namespace Cpu {
unordered_flat_map<int, int> core_mapping;
}
-namespace Mem {
+namespace Mem
+{
double old_uptime;
}
-namespace Shared {
+namespace Shared
+{
- fs::path procPath, passwd_path;
+ fs::path passwd_path;
uint64_t totalMem;
long pageSize, clkTck, coreCount;
int totalMem_len;
- void init() {
+ void init()
+ {
+ Logger::debug("Shared init");
//? Shared global variables init
- procPath = (fs::is_directory(fs::path("/proc")) and access("/proc", R_OK) != -1) ? "/proc" : "";
- if (procPath.empty())
- throw std::runtime_error("Proc filesystem not found or no permission to read from it!");
- passwd_path = (fs::is_regular_file(fs::path("/etc/passwd")) and access("/etc/passwd", R_OK) != -1) ? "/etc/passwd" : "";
- if (passwd_path.empty())
- Logger::warning("Could not read /etc/passwd, will show UID instead of username.");
+ // passwd_path = (fs::is_regular_file(fs::path("/etc/passwd")) and access("/etc/passwd", R_OK) != -1) ? "/etc/passwd" : "";
+ // if (passwd_path.empty())
+ // Logger::warning("Could not read /etc/passwd, will show UID instead of username.");
coreCount = sysconf(_SC_NPROCESSORS_ONLN);
- if (coreCount < 1) {
+ if (coreCount < 1)
+ {
coreCount = 1;
Logger::warning("Could not determine number of cores, defaulting to 1.");
}
pageSize = sysconf(_SC_PAGE_SIZE);
- if (pageSize <= 0) {
+ if (pageSize <= 0)
+ {
pageSize = 4096;
Logger::warning("Could not get system page size. Defaulting to 4096, processes memory usage might be incorrect.");
}
clkTck = sysconf(_SC_CLK_TCK);
- if (clkTck <= 0) {
+ if (clkTck <= 0)
+ {
clkTck = 100;
Logger::warning("Could not get system clock ticks per second. Defaulting to 100, processes cpu usage might be incorrect.");
}
- ifstream meminfo(Shared::procPath / "meminfo");
- if (meminfo.good()) {
- meminfo.ignore(SSmax, ':');
- meminfo >> totalMem;
- totalMem_len = to_string(totalMem).size();
- totalMem <<= 10;
- }
- if (not meminfo.good() or totalMem == 0)
- throw std::runtime_error("Could not get total memory size from /proc/meminfo");
-
- //? Init for namespace Cpu
- if (not fs::exists(Cpu::freq_path) or access(Cpu::freq_path.c_str(), R_OK) == -1) Cpu::freq_path.clear();
- Cpu::current_cpu.core_percent.insert(Cpu::current_cpu.core_percent.begin(), Shared::coreCount, {});
- Cpu::current_cpu.temp.insert(Cpu::current_cpu.temp.begin(), Shared::coreCount + 1, {});
- Cpu::core_old_totals.insert(Cpu::core_old_totals.begin(), Shared::coreCount, 0);
- Cpu::core_old_idles.insert(Cpu::core_old_idles.begin(), Shared::coreCount, 0);
- Cpu::collect();
- for (auto& [field, vec] : Cpu::current_cpu.cpu_percent) {
- if (not vec.empty()) Cpu::available_fields.push_back(field);
+ int64_t memsize = 0;
+ size_t size = sizeof(memsize);
+ Logger::debug("getting memsize");
+ if (sysctlbyname("hw.memsize", &memsize, &size, NULL, 0) < 0)
+ {
+ Logger::warning("Could not get memory size");
}
- Cpu::cpuName = Cpu::get_cpuName();
- Cpu::got_sensors = Cpu::get_sensors();
- for (const auto& [sensor, ignored] : Cpu::found_sensors) {
- Cpu::available_sensors.push_back(sensor);
- }
- Cpu::core_mapping = Cpu::get_core_mapping();
-
- //? Init for namespace Mem
- Mem::old_uptime = system_uptime();
- Mem::collect();
-
+ totalMem = memsize;
}
}
-namespace Cpu {
+namespace Cpu
+{
string cpuName;
string cpuHz;
bool has_battery = true;
@@ -155,962 +142,191 @@ namespace Cpu {
const array<string, 10> time_names = {"user", "nice", "system", "idle", "iowait", "irq", "softirq", "steal", "guest", "guest_nice"};
unordered_flat_map<string, long long> cpu_old = {
- {"totals", 0},
- {"idles", 0},
- {"user", 0},
- {"nice", 0},
- {"system", 0},
- {"idle", 0},
- {"iowait", 0},
- {"irq", 0},
- {"softirq", 0},
- {"steal", 0},
- {"guest", 0},
- {"guest_nice", 0}
- };
-
- string get_cpuName() {
- string name;
- ifstream cpuinfo(Shared::procPath / "cpuinfo");
- if (cpuinfo.good()) {
- for (string instr; getline(cpuinfo, instr, ':') and not instr.starts_with("model name");)
- cpuinfo.ignore(SSmax, '\n');
- if (cpuinfo.bad()) return name;
- else if (not cpuinfo.eof()) {
- cpuinfo.ignore(1);
- getline(cpuinfo, name);
- }
- else if (fs::exists("/sys/devices")) {
- for (const auto& d : fs::directory_iterator("/sys/devices")) {
- if (string(d.path().filename()).starts_with("arm")) {
- name = d.path().filename();
- break;
- }
- }
- if (not name.empty()) {
- auto name_vec = ssplit(name, '_');
- if (name_vec.size() < 2) return capitalize(name);
- else return capitalize(name_vec.at(1)) + (name_vec.size() > 2 ? ' ' + capitalize(name_vec.at(2)) : "");
- }
-
- }
-
- auto name_vec = ssplit(name);
-
- if ((s_contains(name, "Xeon"s) or v_contains(name_vec, "Duo"s)) and v_contains(name_vec, "CPU"s)) {
- auto cpu_pos = v_index(name_vec, "CPU"s);
- if (cpu_pos < name_vec.size() - 1 and not name_vec.at(cpu_pos + 1).ends_with(')'))
- name = name_vec.at(cpu_pos + 1);
- else
- name.clear();
- }
- else if (v_contains(name_vec, "Ryzen"s)) {
- auto ryz_pos = v_index(name_vec, "Ryzen"s);
- name = "Ryzen" + (ryz_pos < name_vec.size() - 1 ? ' ' + name_vec.at(ryz_pos + 1) : "")
- + (ryz_pos < name_vec.size() - 2 ? ' ' + name_vec.at(ryz_pos + 2) : "");
- }
- else if (s_contains(name, "Intel"s) and v_contains(name_vec, "CPU"s)) {
- auto cpu_pos = v_index(name_vec, "CPU"s);
- if (cpu_pos < name_vec.size() - 1 and not name_vec.at(cpu_pos + 1).ends_with(')') and name_vec.at(cpu_pos + 1) != "@")
- name = name_vec.at(cpu_pos + 1);
- else
- name.clear();
- }
- else
- name.clear();
-
- if (name.empty() and not name_vec.empty()) {
- for (const auto& n : name_vec) {
- if (n == "@") break;
- name += n + ' ';
- }
- name.pop_back();
- for (const auto& reg : {regex("Processor"), regex("CPU"), regex("\\(R\\)"), regex("\\(TM\\)"), regex("Intel"),
- regex("AMD"), regex("Core"), regex("\\d?\\.?\\d+[mMgG][hH][zZ]")}) {
- name = std::regex_replace(name, reg, "");
- }
- name = trim(name);
- }
- }
+ {"totals", 0},
+ {"idles", 0},
+ {"user", 0},
+ {"nice", 0},
+ {"system", 0},
+ {"idle", 0},
+ {"iowait", 0},
+ {"irq", 0},
+ {"softirq", 0},
+ {"steal", 0},
+ {"guest", 0},
+ {"guest_nice", 0}};
+
+ string get_cpuName()
+ {
+ string name("11th Gen Intel(R) Core(TM) i5-11600 @ 2.80GHz");
return name;
}
- bool get_sensors() {
- bool got_cpu = false, got_coretemp = false;
- vector<fs::path> search_paths;
- try {
- //? Setup up paths to search for sensors
- if (fs::exists(fs::path("/sys/class/hwmon")) and access("/sys/class/hwmon", R_OK) != -1) {
- for (const auto& dir : fs::directory_iterator(fs::path("/sys/class/hwmon"))) {
- fs::path add_path = fs::canonical(dir.path());
- if (v_contains(search_paths, add_path) or v_contains(search_paths, add_path / "device")) continue;
-
- if (s_contains(add_path, "coretemp"))
- got_coretemp = true;
-
- if (fs::exists(add_path / "temp1_input")) {
- search_paths.push_back(add_path);
- }
- else if (fs::exists(add_path / "device/temp1_input"))
- search_paths.push_back(add_path / "device");
- }
- }
- if (not got_coretemp and fs::exists(fs::path("/sys/devices/platform/coretemp.0/hwmon"))) {
- for (auto& d : fs::directory_iterator(fs::path("/sys/devices/platform/coretemp.0/hwmon"))) {
- fs::path add_path = fs::canonical(d.path());
-
- if (fs::exists(d.path() / "temp1_input") and not v_contains(search_paths, add_path)) {
- search_paths.push_back(add_path);
- got_coretemp = true;
- }
- }
- }
- //? Scan any found directories for temperature sensors
- if (not search_paths.empty()) {
- for (const auto& path : search_paths) {
- const string pname = readfile(path / "name", path.filename());
- for (int i = 1; fs::exists(path / string("temp" + to_string(i) + "_input")); i++) {
- const string basepath = path / string("temp" + to_string(i) + "_");
- const string label = readfile(fs::path(basepath + "label"), "temp" + to_string(i));
- const string sensor_name = pname + "/" + label;
- const int64_t temp = stol(readfile(fs::path(basepath + "input"), "0")) / 1000;
- const int64_t high = stol(readfile(fs::path(basepath + "max"), "80000")) / 1000;
- const int64_t crit = stol(readfile(fs::path(basepath + "crit"), "95000")) / 1000;
-
- found_sensors[sensor_name] = {fs::path(basepath + "input"), label, temp, high, crit};
-
- if (not got_cpu and (label.starts_with("Package id") or label.starts_with("Tdie"))) {
- got_cpu = true;
- cpu_sensor = sensor_name;
- }
- else if (label.starts_with("Core") or label.starts_with("Tccd")) {
- got_coretemp = true;
- if (not v_contains(core_sensors, sensor_name)) core_sensors.push_back(sensor_name);
- }
- }
- }
- }
- //? If no good candidate for cpu temp has been found scan /sys/class/thermal
- if (not got_cpu and fs::exists(fs::path("/sys/class/thermal"))) {
- const string rootpath = fs::path("/sys/class/thermal/thermal_zone");
- for (int i = 0; fs::exists(fs::path(rootpath + to_string(i))); i++) {
- const fs::path basepath = rootpath + to_string(i);
- if (not fs::exists(basepath / "temp")) continue;
- const string label = readfile(basepath / "type", "temp" + to_string(i));
- const string sensor_name = "thermal" + to_string(i) + "/" + label;
- const int64_t temp = stol(readfile(basepath / "temp", "0")) / 1000;
-
- int64_t high, crit;
- for (int ii = 0; fs::exists(basepath / string("trip_point_" + to_string(ii) + "_temp")); ii++) {
- const string trip_type = readfile(basepath / string("trip_point_" + to_string(ii) + "_type"));
- if (not is_in(trip_type, "high", "critical")) continue;
- auto& val = (trip_type == "high" ? high : crit);
- val = stol(readfile(basepath / string("trip_point_" + to_string(ii) + "_temp"), "0")) / 1000;
- }
- if (high < 1) high = 80;
- if (crit < 1) crit = 95;
-
- found_sensors[sensor_name] = {basepath / "temp", label, temp, high, crit};
- }
- }
-
- }
- catch (...) {}
-
- if (not got_coretemp or core_sensors.empty()) cpu_temp_only = true;
- if (cpu_sensor.empty() and not found_sensors.empty()) {
- for (const auto& [name, sensor] : found_sensors) {
- if (s_contains(str_to_lower(name), "cpu")) {
- cpu_sensor = name;
- break;
- }
- }
- if (cpu_sensor.empty()) {
- cpu_sensor = found_sensors.begin()->first;
- Logger::warning("No good candidate for cpu sensor found, using random from all found sensors.");
- }
- }
-
+ bool get_sensors()
+ {
return not found_sensors.empty();
}
- void update_sensors() {
- if (cpu_sensor.empty()) return;
+ void update_sensors()
+ {
+ if (cpu_sensor.empty())
+ return;
- const auto& cpu_sensor = (not Config::getS("cpu_sensor").empty() and found_sensors.contains(Config::getS("cpu_sensor")) ? Config::getS("cpu_sensor") : Cpu::cpu_sensor);
+ const auto &cpu_sensor = (not Config::getS("cpu_sensor").empty() and found_sensors.contains(Config::getS("cpu_sensor")) ? Config::getS("cpu_sensor") : Cpu::cpu_sensor);
found_sensors.at(cpu_sensor).temp = stol(readfile(found_sensors.at(cpu_sensor).path, "0")) / 1000;
current_cpu.temp.at(0).push_back(found_sensors.at(cpu_sensor).temp);
current_cpu.temp_max = found_sensors.at(cpu_sensor).crit;
- if (current_cpu.temp.at(0).size() > 20) current_cpu.temp.at(0).pop_front();
+ if (current_cpu.temp.at(0).size() > 20)
+ current_cpu.temp.at(0).pop_front();
- if (Config::getB("show_coretemp") and not cpu_temp_only) {
+ if (Config::getB("show_coretemp") and not cpu_temp_only)
+ {
vector<string> done;
- for (const auto& sensor : core_sensors) {
- if (v_contains(done, sensor)) continue;
+ for (const auto &sensor : core_sensors)
+ {
+ if (v_contains(done, sensor))
+ continue;
found_sensors.at(sensor).temp = stol(readfile(found_sensors.at(sensor).path, "0")) / 1000;
done.push_back(sensor);
}
- for (const auto& [core, temp] : core_mapping) {
- if (cmp_less(core + 1, current_cpu.temp.size()) and cmp_less(temp, core_sensors.size())) {
+ for (const auto &[core, temp] : core_mapping)
+ {
+ if (cmp_less(core + 1, current_cpu.temp.size()) and cmp_less(temp, core_sensors.size()))
+ {
current_cpu.temp.at(core + 1).push_back(found_sensors.at(core_sensors.at(temp)).temp);
- if (current_cpu.temp.at(core + 1).size() > 20) current_cpu.temp.at(core + 1).pop_front();
+ if (current_cpu.temp.at(core + 1).size() > 20)
+ current_cpu.temp.at(core + 1).pop_front();
}
}
}
}
- string get_cpuHz() {
- static int failed = 0;
- if (failed > 4) return ""s;
- string cpuhz;
- try {
- double hz = 0.0;
- //? Try to get freq from /sys/devices/system/cpu/cpufreq/policy first (faster)
- if (not freq_path.empty()) {
- hz = stod(readfile(freq_path, "0.0")) / 1000;
- if (hz <= 0.0 and ++failed >= 2)
- freq_path.clear();
- }
- //? If freq from /sys failed or is missing try to use /proc/cpuinfo
- if (hz <= 0.0) {
- ifstream cpufreq(Shared::procPath / "cpuinfo");
- if (cpufreq.good()) {
- while (cpufreq.ignore(SSmax, '\n')) {
- if (cpufreq.peek() == 'c') {
- cpufreq.ignore(SSmax, ' ');
- if (cpufreq.peek() == 'M') {
- cpufreq.ignore(SSmax, ':');
- cpufreq.ignore(1);
- cpufreq >> hz;
- break;
- }
- }
- }
- }
- }
-
- if (hz <= 1 or hz >= 1000000) throw std::runtime_error("Failed to read /sys/devices/system/cpu/cpufreq/policy and /proc/cpuinfo.");
-
- if (hz >= 1000) {
- if (hz >= 10000) cpuhz = to_string((int)round(hz / 1000)); // Future proof until we reach THz speeds :)
- else cpuhz = to_string(round(hz / 100) / 10.0).substr(0, 3);
- cpuhz += " GHz";
- }
- else if (hz > 0)
- cpuhz = to_string((int)round(hz)) + " MHz";
-
- }
- catch (const std::exception& e) {
- if (++failed < 5) return ""s;
- else {
- Logger::warning("get_cpuHZ() : " + (string)e.what());
- return ""s;
- }
- }
-
- return cpuhz;
+ string get_cpuHz()
+ {
+ uint64_t freq = 0;
+ size_t size = sizeof(freq);
+
+ Logger::debug("get_cpuHz");
+ return "1.0";
+ // if (sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0) < 0)
+ // {
+ // perror("sysctl");
+ // }
+ // Logger::debug("cpufreq:" + freq);
+ // return "" + freq;
}
- auto get_core_mapping() -> unordered_flat_map<int, int> {
+ auto get_core_mapping() -> unordered_flat_map<int, int>
+ {
unordered_flat_map<int, int> core_map;
- if (cpu_temp_only) return core_map;
-
- //? Try to get core mapping from /proc/cpuinfo
- ifstream cpuinfo(Shared::procPath / "cpuinfo");
- if (cpuinfo.good()) {
- int cpu, core, n = 0;
- for (string instr; cpuinfo >> instr;) {
- if (instr == "processor") {
- cpuinfo.ignore(SSmax, ':');
- cpuinfo >> cpu;
- }
- else if (instr.starts_with("core")) {
- cpuinfo.ignore(SSmax, ':');
- cpuinfo >> core;
- if (std::cmp_greater_equal(core, core_sensors.size())) {
- if (std::cmp_greater_equal(n, core_sensors.size())) n = 0;
- core_map[cpu] = n++;
- }
- else
- core_map[cpu] = core;
- }
- cpuinfo.ignore(SSmax, '\n');
- }
- }
-
- //? If core mapping from cpuinfo was incomplete try to guess remainder, if missing completely, map 0-0 1-1 2-2 etc.
- if (cmp_less(core_map.size(), Shared::coreCount)) {
- if (Shared::coreCount % 2 == 0 and (long)core_map.size() == Shared::coreCount / 2) {
- for (int i = 0, n = 0; i < Shared::coreCount / 2; i++) {
- if (std::cmp_greater_equal(n, core_sensors.size())) n = 0;
- core_map[Shared::coreCount / 2 + i] = n++;
- }
- }
- else {
- core_map.clear();
- for (int i = 0, n = 0; i < Shared::coreCount; i++) {
- if (std::cmp_greater_equal(n, core_sensors.size())) n = 0;
- core_map[i] = n++;
- }
- }
- }
-
- //? Apply user set custom mapping if any
- const auto& custom_map = Config::getS("cpu_core_map");
- if (not custom_map.empty()) {
- try {
- for (const auto& split : ssplit(custom_map)) {
- const auto vals = ssplit(split, ':');
- if (vals.size() != 2) continue;
- int change_id = std::stoi(vals.at(0));
- int new_id = std::stoi(vals.at(1));
- if (not core_map.contains(change_id) or cmp_greater(new_id, core_sensors.size())) continue;
- core_map.at(change_id) = new_id;
- }
- }
- catch (...) {}
- }
-
return core_map;
}
- auto get_battery() -> tuple<int, long, string> {
- if (not has_battery) return {0, 0, ""};
- static fs::path bat_dir, energy_now_path, energy_full_path, power_now_path, status_path, online_path;
- static bool use_energy = true;
-
- //? Get paths to needed files and check for valid values on first run
- if (bat_dir.empty() and has_battery) {
- if (fs::exists("/sys/class/power_supply")) {
- for (const auto& d : fs::directory_iterator("/sys/class/power_supply")) {
- if (const string dir_name = d.path().filename(); d.is_directory() and (dir_name.starts_with("BAT") or s_contains(str_to_lower(dir_name), "battery"))) {
- bat_dir = d.path();
- break;
- }
- }
- }
- if (bat_dir.empty()) {
- has_battery = false;
- return {0, 0, ""};
- }
- else {
- if (fs::exists(bat_dir / "energy_now")) energy_now_path = bat_dir / "energy_now";
- else if (fs::exists(bat_dir / "charge_now")) energy_now_path = bat_dir / "charge_now";
- else use_energy = false;
-
- if (fs::exists(bat_dir / "energy_full")) energy_full_path = bat_dir / "energy_full";
- else if (fs::exists(bat_dir / "charge_full")) energy_full_path = bat_dir / "charge_full";
- else use_energy = false;
-
- if (not use_energy and not fs::exists(bat_dir / "capacity")) {
- has_battery = false;
- return {0, 0, ""};
- }
-
- if (fs::exists(bat_dir / "power_now")) power_now_path = bat_dir / "power_now";
- else if (fs::exists(bat_dir / "current_now")) power_now_path = bat_dir / "current_now";
-
- if (fs::exists(bat_dir / "AC0/online")) online_path = bat_dir / "AC0/online";
- else if (fs::exists(bat_dir / "AC/online")) online_path = bat_dir / "AC/online";
- }
- }
-
- int percent = -1;
- long seconds = -1;
-
- //? Try to get battery percentage
- if (use_energy) {
- try {
- percent = round(100.0 * stoll(readfile(energy_now_path, "-1")) / stoll(readfile(energy_full_path, "1")));
- }
- catch (const std::invalid_argument&) { }
- catch (const std::out_of_range&) { }
- }
- if (percent < 0) {
- try {
- percent = stoll(readfile(bat_dir / "capacity", "-1"));
- }
- catch (const std::invalid_argument&) { }
- catch (const std::out_of_range&) { }
- }
- if (percent < 0) {
- has_battery = false;
- return {0, 0, ""};
- }
-
- //? Get charging/discharging status
- string status = str_to_lower(readfile(bat_dir / "status", "unknown"));
- if (status == "unknown" and not online_path.empty()) {
- const auto online = readfile(online_path, "0");
- if (online == "1" and percent < 100) status = "charging";
- else if (online == "1") status = "full";
- else status = "discharging";
- }
-
- //? Get seconds to empty
- if (not is_in(status, "charging", "full")) {
- if (use_energy and not power_now_path.empty()) {
- try {
- seconds = round((double)stoll(readfile(energy_now_path, "0")) / stoll(readfile(power_now_path, "1")) * 3600);
- }
- catch (const std::invalid_argument&) { }
- catch (const std::out_of_range&) { }
- }
- if (seconds < 0 and fs::exists(bat_dir / "time_to_empty")) {
- try {
- seconds = stoll(readfile(bat_dir / "time_to_empty", "0")) * 60;
- }
- catch (const std::invalid_argument&) { }
- catch (const std::out_of_range&) { }
- }
- }
-
- return {percent, seconds, status};
+ auto get_battery() -> tuple<int, long, string>
+ {
+ // if (not has_battery)
+ return {0, 0, ""};
}
- auto collect(const bool no_update) -> cpu_info& {
- if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty())) return current_cpu;
- auto& cpu = current_cpu;
-
- ifstream cread;
-
- try {
- //? Get cpu load averages from /proc/loadavg
- cread.open(Shared::procPath / "loadavg");
- if (cread.good()) {
- cread >> cpu.load_avg[0] >> cpu.load_avg[1] >> cpu.load_avg[2];
- }
- cread.close();
-
- //? Get cpu total times for all cores from /proc/stat
- cread.open(Shared::procPath / "stat");
- for (int i = 0; cread.good() and cread.peek() == 'c'; i++) {
- cread.ignore(SSmax, ' ');
-
- //? Expected on kernel 2.6.3> : 0=user, 1=nice, 2=system, 3=idle, 4=iowait, 5=irq, 6=softirq, 7=steal, 8=guest, 9=guest_nice
- vector<long long> times;
- long long total_sum = 0;
-
- for (uint64_t val; cread >> val; total_sum += val) {
- times.push_back(val);
- }
- cread.clear();
- if (times.size() < 4) throw std::runtime_error("Malformatted /proc/stat");
-
- //? Subtract fields 8-9 and any future unknown fields
- const long long totals = max(0ll, total_sum - (times.size() > 8 ? std::accumulate(times.begin() + 8, times.end(), 0) : 0));
-
- //? Add iowait field if present
- const long long idles = max(0ll, times.at(3) + (times.size() > 4 ? times.at(4) : 0));
-
- //? Calculate values for totals from first line of stat
- if (i == 0) {
- const long long calc_totals = max(1ll, totals - cpu_old.at("totals"));
- const long long calc_idles = max(1ll, idles - cpu_old.at("idles"));
- cpu_old.at("totals") = totals;
- cpu_old.at("idles") = idles;
-
- //? Total usage of cpu
- cpu.cpu_percent.at("total").push_back(clamp((long long)round((double)(calc_totals - calc_idles) * 100 / calc_totals), 0ll, 100ll));
-
- //? Reduce size if there are more values than needed for graph
- while (cmp_greater(cpu.cpu_percent.at("total").size(), width * 2)) cpu.cpu_percent.at("total").pop_front();
-
- //? Populate cpu.cpu_percent with all fields from stat
- for (int ii = 0; const auto& val : times) {
- cpu.cpu_percent.at(time_names.at(ii)).push_back(clamp((long long)round((double)(val - cpu_old.at(time_names.at(ii))) * 100 / calc_totals), 0ll, 100ll));
- cpu_old.at(time_names.at(ii)) = val;
-
- //? Reduce size if there are more values than needed for graph
- while (cmp_greater(cpu.cpu_percent.at(time_names.at(ii)).size(), width * 2)) cpu.cpu_percent.at(time_names.at(ii)).pop_front();
-
- if (++ii == 10) break;
- }
- }
- //? Calculate cpu total for each core
- else {
- if (i > Shared::coreCount) break;
- const long long calc_totals = max(0ll, totals - core_old_totals.at(i-1));
- const long long calc_idles = max(0ll, idles - core_old_idles.at(i-1));
- core_old_totals.at(i-1) = totals;
- core_old_idles.at(i-1) = idles;
-
- cpu.core_percent.at(i-1).push_back(clamp((long long)round((double)(calc_totals - calc_idles) * 100 / calc_totals), 0ll, 100ll));
-
- //? Reduce size if there are more values than needed for graph
- if (cpu.core_percent.at(i-1).size() > 40) cpu.core_percent.at(i-1).pop_front();
-
- }
- }
- }
- catch (const std::exception& e) {
- Logger::debug("get_cpuHz() : " + (string)e.what());
- if (cread.bad()) throw std::runtime_error("Failed to read /proc/stat");
- else throw std::runtime_error("collect() : " + (string)e.what());
- }
+ auto collect(const bool no_update) -> cpu_info &
+ {
+ Logger::debug("CPU collect");
+ // if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
+ // return current_cpu;
+ auto &cpu = current_cpu;
if (Config::getB("show_cpu_freq"))
cpuHz = get_cpuHz();
- if (Config::getB("check_temp") and got_sensors)
- update_sensors();
-
- if (Config::getB("show_battery") and has_battery)
- current_bat = get_battery();
-
return cpu;
}
}
-namespace Mem {
+namespace Mem
+{
bool has_swap = false;
vector<string> fstab;
fs::file_time_type fstab_time;
int disk_ios = 0;
vector<string> last_found;
- mem_info current_mem {};
-
- auto collect(const bool no_update) -> mem_info& {
- if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty())) return current_mem;
- auto& show_swap = Config::getB("show_swap");
- auto& swap_disk = Config::getB("swap_disk");
- auto& show_disks = Config::getB("show_disks");
- auto& mem = current_mem;
- static const bool snapped = (getenv("BTOP_SNAPPED") != NULL);
-
- mem.stats.at("swap_total") = 0;
-
- //? Read memory info from /proc/meminfo
- ifstream meminfo(Shared::procPath / "meminfo");
- if (meminfo.good()) {
- bool got_avail = false;
- for (string label; meminfo >> label;) {
- if (label == "MemFree:") {
- meminfo >> mem.stats.at("free");
- mem.stats.at("free") <<= 10;
- }
- else if (label == "MemAvailable:") {
- meminfo >> mem.stats.at("available");
- mem.stats.at("available") <<= 10;
- got_avail = true;
- }
- else if (label == "Cached:") {
- meminfo >> mem.stats.at("cached");
- mem.stats.at("cached") <<= 10;
- if (not show_swap and not swap_disk) break;
- }
- else if (label == "SwapTotal:") {
- meminfo >> mem.stats.at("swap_total");
- mem.stats.at("swap_total") <<= 10;
- }
- else if (label == "SwapFree:") {
- meminfo >> mem.stats.at("swap_free");
- mem.stats.at("swap_free") <<= 10;
- break;
- }
- meminfo.ignore(SSmax, '\n');
- }
- if (not got_avail) mem.stats.at("available") = mem.stats.at("free") + mem.stats.at("cached");
- mem.stats.at("used") = Shared::totalMem - mem.stats.at("available");
- if (mem.stats.at("swap_total") > 0) mem.stats.at("swap_used") = mem.stats.at("swap_total") - mem.stats.at("swap_free");
- }
- else
- throw std::runtime_error("Failed to read /proc/meminfo");
+ mem_info current_mem{};
+
+ auto collect(const bool no_update) -> mem_info &
+ {
+ Logger::debug("collect MEM");
+ if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
+ return current_mem;
+
+ auto &mem = current_mem;
+ FILE *fpIn = popen("/usr/bin/vm_stat", "r");
+ if (fpIn)
+ {
+ char buf[512];
+ while (fgets(buf, sizeof(buf), fpIn) != NULL)
+ {
+ char *tokens = strtok(buf, ":\n.");
+ while (tokens) {
+ char *label = tokens;
+ char *val = strtok(nullptr, ":\n.");
+ if (strstr(label, "Pages free"))
+ {
+ uint64_t f = stoull(trim(val));
+ mem.stats.at("available") = f * 4096;
+ mem.stats.at("free") = f * 4096;
+ // } else if (strstr(label, "Pages free")) {
- meminfo.close();
-
- //? Calculate percentages
- for (const auto& name : mem_names) {
- mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / Shared::totalMem));
- while (cmp_greater(mem.percent.at(name).size(), width * 2)) mem.percent.at(name).pop_front();
- }
-
- if (show_swap and mem.stats.at("swap_total") > 0) {
- for (const auto& name : swap_names) {
- mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / mem.stats.at("swap_total")));
- while (cmp_greater(mem.percent.at(name).size(), width * 2)) mem.percent.at(name).pop_front();
- }
- has_swap = true;
- }
- else
- has_swap = false;
-
- //? Get disks stats
- if (show_disks) {
- double uptime = system_uptime();
- try {
- auto& disks_filter = Config::getS("disks_filter");
- bool filter_exclude = false;
- auto& use_fstab = Config::getB("use_fstab");
- auto& only_physical = Config::getB("only_physical");
- auto& disks = mem.disks;
- ifstream diskread;
-
- vector<string> filter;
- if (not disks_filter.empty()) {
- filter = ssplit(disks_filter);
- if (filter.at(0).starts_with("exclude=")) {
- filter_exclude = true;
- filter.at(0) = filter.at(0).substr(8);
- }
- }
-
- //? Get list of "real" filesystems from /proc/filesystems
- vector<string> fstypes;
- if (only_physical and not use_fstab) {
- fstypes = {"zfs", "wslfs", "drvfs"};
- diskread.open(Shared::procPath / "filesystems");
- if (diskread.good()) {
- for (string fstype; diskread >> fstype;) {
- if (not is_in(fstype, "nodev", "squashfs", "nullfs"))
- fstypes.push_back(fstype);
- diskread.ignore(SSmax, '\n');
- }
- }
- else
- throw std::runtime_error("Failed to read /proc/filesystems");
- diskread.close();
- }
-
- //? Get disk list to use from fstab if enabled
- if (use_fstab and fs::last_write_time("/etc/fstab") != fstab_time) {
- fstab.clear();
- fstab_time = fs::last_write_time("/etc/fstab");
- diskread.open("/etc/fstab");
- if (diskread.good()) {
- for (string instr; diskread >> instr;) {
- if (not instr.starts_with('#')) {
- diskread >> instr;
- if (snapped and instr == "/") fstab.push_back("/mnt");
- else if (not is_in(instr, "none", "swap")) fstab.push_back(instr);
- }
- diskread.ignore(SSmax, '\n');
- }
- }
- else
- throw std::runtime_error("Failed to read /etc/fstab");
- diskread.close();
- }
-
- //? Get mounts from /etc/mtab or /proc/self/mounts
- diskread.open((fs::exists("/etc/mtab") ? fs::path("/etc/mtab") : Shared::procPath / "self/mounts"));
- if (diskread.good()) {
- vector<string> found;
- found.reserve(last_found.size());
- string dev, mountpoint, fstype;
- while (not diskread.eof()) {
- std::error_code ec;
- diskread >> dev >> mountpoint >> fstype;
-
- //? Match filter if not empty
- if (not filter.empty()) {
- bool match = v_contains(filter, mountpoint);
- if ((filter_exclude and match) or (not filter_exclude and not match))
- continue;
- }
-
- if ((not use_fstab and not only_physical)
- or (use_fstab and v_contains(fstab, mountpoint))
- or (not use_fstab and only_physical and v_contains(fstypes, fstype))) {
- found.push_back(mountpoint);
- if (not v_contains(last_found, mountpoint)) redraw = true;
-
- //? Save mountpoint, name, dev path and path to /sys/block stat file
- if (not disks.contains(mountpoint)) {
- disks[mountpoint] = disk_info{fs::canonical(dev, ec), fs::path(mountpoint).filename()};
- if (disks.at(mountpoint).dev.empty()) disks.at(mountpoint).dev = dev;
- if (disks.at(mountpoint).name.empty()) disks.at(mountpoint).name = (mountpoint == "/" or (snapped and mountpoint == "/mnt") ? "root" : mountpoint);
- string devname = disks.at(mountpoint).dev.filename();
- while (devname.size() >= 2) {
- if (fs::exists("/sys/block/" + devname + "/stat", ec) and access(string("/sys/block/" + devname + "/stat").c_str(), R_OK) == 0) {
- disks.at(mountpoint).stat = "/sys/block/" + devname + "/stat";
- break;
- }
- devname.resize(devname.size() - 1);
- }
- }
-
- }
- diskread.ignore(SSmax, '\n');
- }
- //? Remove disks no longer mounted or filtered out
- if (swap_disk and has_swap) found.push_back("swap");
- for (auto it = disks.begin(); it != disks.end();) {
- if (not v_contains(found, it->first))
- it = disks.erase(it);
- else
- it++;
- }
- if (found.size() != last_found.size()) redraw = true;
- last_found = std::move(found);
- }
- else
- throw std::runtime_error("Failed to get mounts from /etc/mtab and /proc/self/mounts");
- diskread.close();
-
- //? Get disk/partition stats
- for (auto& [mountpoint, disk] : disks) {
- if (std::error_code ec; not fs::exists(mountpoint, ec)) continue;
- struct statvfs vfs;
- if (statvfs(mountpoint.c_str(), &vfs) < 0) {
- Logger::warning("Failed to get disk/partition stats with statvfs() for: " + mountpoint);
- continue;
- }
- disk.total = vfs.f_blocks * vfs.f_frsize;
- disk.free = vfs.f_bfree * vfs.f_frsize;
- disk.used = disk.total - disk.free;
- disk.used_percent = round((double)disk.used * 100 / disk.total);
- disk.free_percent = 100 - disk.used_percent;
- }
-
- //? Setup disks order in UI and add swap if enabled
- mem.disks_order.clear();
- if (snapped and disks.contains("/mnt")) mem.disks_order.push_back("/mnt");
- else if (disks.contains("/")) mem.disks_order.push_back("/");
- if (swap_disk and has_swap) {
- mem.disks_order.push_back("swap");
- if (not disks.contains("swap")) disks["swap"] = {"", "swap"};
- disks.at("swap").total = mem.stats.at("swap_total");
- disks.at("swap").used = mem.stats.at("swap_used");
- disks.at("swap").free = mem.stats.at("swap_free");
- disks.at("swap").used_percent = mem.percent.at("swap_used").back();
- disks.at("swap").free_percent = mem.percent.at("swap_free").back();
- }
- for (const auto& name : last_found)
- if (not is_in(name, "/", "swap")) mem.disks_order.push_back(name);
-
- //? Get disks IO
- int64_t sectors_read, sectors_write, io_ticks;
- disk_ios = 0;
- for (auto& [ignored, disk] : disks) {
- if (disk.stat.empty() or access(disk.stat.c_str(), R_OK) != 0) continue;
- diskread.open(disk.stat);
- if (diskread.good()) {
- disk_ios++;
- for (int i = 0; i < 2; i++) { diskread >> std::ws; diskread.ignore(SSmax, ' '); }
- diskread >> sectors_read;
- if (disk.io_read.empty())
- disk.io_read.push_back(0);
- else
- disk.io_read.push_back(max((int64_t)0, (sectors_read - disk.old_io.at(0)) * 512));
- disk.old_io.at(0) = sectors_read;
- while (cmp_greater(disk.io_read.size(), width * 2)) disk.io_read.pop_front();
-
- for (int i = 0; i < 3; i++) { diskread >> std::ws; diskread.ignore(SSmax, ' '); }
- diskread >> sectors_write;
- if (disk.io_write.empty())
- disk.io_write.push_back(0);
- else
- disk.io_write.push_back(max((int64_t)0, (sectors_write - disk.old_io.at(1)) * 512));
- disk.old_io.at(1) = sectors_write;
- while (cmp_greater(disk.io_write.size(), width * 2)) disk.io_write.pop_front();
-
- for (int i = 0; i < 2; i++) { diskread >> std::ws; diskread.ignore(SSmax, ' '); }
- diskread >> io_ticks;
- if (disk.io_activity.empty())
- disk.io_activity.push_back(0);
- else
- disk.io_activity.push_back(clamp((long)round((double)(io_ticks - disk.old_io.at(2)) / (uptime - old_uptime) / 10), 0l, 100l));
- disk.old_io.at(2) = io_ticks;
- while (cmp_greater(disk.io_activity.size(), width * 2)) disk.io_activity.pop_front();
}
- diskread.close();
+ tokens = strtok(nullptr, ":\n.");