This commit is contained in:
Jos Dehaes 2021-10-04 09:15:35 +02:00 committed by aristocratos
parent 28e152b80c
commit 6497a8c202
1 changed files with 96 additions and 157 deletions

View File

@ -16,25 +16,25 @@ indent = tab
tab-size = 4 tab-size = 4
*/ */
#include <fstream>
#include <ranges>
#include <cmath>
#include <unistd.h>
#include <numeric>
#include <regex>
#include <sys/statvfs.h>
#include <netdb.h>
#include <string>
#include <ifaddrs.h> #include <ifaddrs.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <libproc.h> #include <libproc.h>
#include <net/if.h>
#include <netdb.h>
#include <pwd.h> #include <pwd.h>
#include <sys/statvfs.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <btop_shared.hpp>
#include <btop_config.hpp> #include <btop_config.hpp>
#include <btop_shared.hpp>
#include <btop_tools.hpp> #include <btop_tools.hpp>
#include <cmath>
#include <fstream>
#include <numeric>
#include <ranges>
#include <regex>
#include <string>
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater; 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; using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
@ -44,8 +44,7 @@ using namespace Tools;
//? --------------------------------------------------- FUNCTIONS ----------------------------------------------------- //? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
namespace Cpu namespace Cpu {
{
vector<long long> core_old_totals; vector<long long> core_old_totals;
vector<long long> core_old_idles; vector<long long> core_old_idles;
vector<string> available_fields; vector<string> available_fields;
@ -63,8 +62,7 @@ namespace Cpu
//* Search /proc/cpuinfo for a cpu name //* Search /proc/cpuinfo for a cpu name
string get_cpuName(); string get_cpuName();
struct Sensor struct Sensor {
{
fs::path path; fs::path path;
string label; string label;
int64_t temp = 0; int64_t temp = 0;
@ -76,24 +74,20 @@ namespace Cpu
string cpu_sensor; string cpu_sensor;
vector<string> core_sensors; vector<string> core_sensors;
unordered_flat_map<int, int> core_mapping; unordered_flat_map<int, int> core_mapping;
} } // namespace Cpu
namespace Mem namespace Mem {
{
double old_uptime; double old_uptime;
} }
namespace Shared namespace Shared {
{
fs::path passwd_path; fs::path passwd_path;
uint64_t totalMem; uint64_t totalMem;
long pageSize, clkTck, coreCount; long pageSize, clkTck, coreCount;
int totalMem_len; int totalMem_len;
void init() void init() {
{
//? Shared global variables init //? Shared global variables init
// passwd_path = (fs::is_regular_file(fs::path("/etc/passwd")) and access("/etc/passwd", R_OK) != -1) ? "/etc/passwd" : ""; // passwd_path = (fs::is_regular_file(fs::path("/etc/passwd")) and access("/etc/passwd", R_OK) != -1) ? "/etc/passwd" : "";
@ -101,30 +95,26 @@ namespace Shared
// Logger::warning("Could not read /etc/passwd, will show UID instead of username."); // Logger::warning("Could not read /etc/passwd, will show UID instead of username.");
coreCount = sysconf(_SC_NPROCESSORS_ONLN); coreCount = sysconf(_SC_NPROCESSORS_ONLN);
if (coreCount < 1) if (coreCount < 1) {
{
coreCount = 1; coreCount = 1;
Logger::warning("Could not determine number of cores, defaulting to 1."); Logger::warning("Could not determine number of cores, defaulting to 1.");
} }
pageSize = sysconf(_SC_PAGE_SIZE); pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize <= 0) if (pageSize <= 0) {
{
pageSize = 4096; pageSize = 4096;
Logger::warning("Could not get system page size. Defaulting to 4096, processes memory usage might be incorrect."); Logger::warning("Could not get system page size. Defaulting to 4096, processes memory usage might be incorrect.");
} }
clkTck = sysconf(_SC_CLK_TCK); clkTck = sysconf(_SC_CLK_TCK);
if (clkTck <= 0) if (clkTck <= 0) {
{
clkTck = 100; clkTck = 100;
Logger::warning("Could not get system clock ticks per second. Defaulting to 100, processes cpu usage might be incorrect."); Logger::warning("Could not get system clock ticks per second. Defaulting to 100, processes cpu usage might be incorrect.");
} }
int64_t memsize = 0; int64_t memsize = 0;
size_t size = sizeof(memsize); size_t size = sizeof(memsize);
if (sysctlbyname("hw.memsize", &memsize, &size, NULL, 0) < 0) if (sysctlbyname("hw.memsize", &memsize, &size, NULL, 0) < 0) {
{
Logger::warning("Could not get memory size"); Logger::warning("Could not get memory size");
} }
totalMem = memsize; totalMem = memsize;
@ -132,10 +122,9 @@ namespace Shared
Cpu::cpuName = Cpu::get_cpuName(); Cpu::cpuName = Cpu::get_cpuName();
} }
} } // namespace Shared
namespace Cpu namespace Cpu {
{
string cpuName; string cpuName;
string cpuHz; string cpuHz;
bool has_battery = true; bool has_battery = true;
@ -144,38 +133,34 @@ namespace Cpu
const array<string, 10> time_names = {"user", "nice", "system", "idle", "iowait", "irq", "softirq", "steal", "guest", "guest_nice"}; 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 = { unordered_flat_map<string, long long> cpu_old = {
{"totals", 0}, {"totals", 0},
{"idles", 0}, {"idles", 0},
{"user", 0}, {"user", 0},
{"nice", 0}, {"nice", 0},
{"system", 0}, {"system", 0},
{"idle", 0}, {"idle", 0},
{"iowait", 0}, {"iowait", 0},
{"irq", 0}, {"irq", 0},
{"softirq", 0}, {"softirq", 0},
{"steal", 0}, {"steal", 0},
{"guest", 0}, {"guest", 0},
{"guest_nice", 0}}; {"guest_nice", 0}};
string get_cpuName() string get_cpuName() {
{
char buffer[1024]; char buffer[1024];
size_t size = sizeof(buffer); size_t size = sizeof(buffer);
if (sysctlbyname("machdep.cpu.brand_string", &buffer, &size, NULL, 0) < 0) if (sysctlbyname("machdep.cpu.brand_string", &buffer, &size, NULL, 0) < 0) {
{
Logger::error("Failed to get CPU name"); Logger::error("Failed to get CPU name");
return ""; return "";
} }
return string(buffer); return string(buffer);
} }
bool get_sensors() bool get_sensors() {
{
return not found_sensors.empty(); return not found_sensors.empty();
} }
void update_sensors() void update_sensors() {
{
if (cpu_sensor.empty()) if (cpu_sensor.empty())
return; return;
@ -187,20 +172,16 @@ namespace Cpu
if (current_cpu.temp.at(0).size() > 20) if (current_cpu.temp.at(0).size() > 20)
current_cpu.temp.at(0).pop_front(); 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; vector<string> done;
for (const auto &sensor : core_sensors) for (const auto &sensor : core_sensors) {
{
if (v_contains(done, sensor)) if (v_contains(done, sensor))
continue; continue;
found_sensors.at(sensor).temp = stol(readfile(found_sensors.at(sensor).path, "0")) / 1000; found_sensors.at(sensor).temp = stol(readfile(found_sensors.at(sensor).path, "0")) / 1000;
done.push_back(sensor); done.push_back(sensor);
} }
for (const auto &[core, temp] : core_mapping) for (const auto &[core, temp] : core_mapping) {
{ if (cmp_less(core + 1, current_cpu.temp.size()) and cmp_less(temp, core_sensors.size())) {
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); 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) if (current_cpu.temp.at(core + 1).size() > 20)
current_cpu.temp.at(core + 1).pop_front(); current_cpu.temp.at(core + 1).pop_front();
@ -209,33 +190,29 @@ namespace Cpu
} }
} }
string get_cpuHz() string get_cpuHz() {
{
uint64_t freq = 0; uint64_t freq = 0;
size_t size = sizeof(freq); size_t size = sizeof(freq);
return "1.0"; return "1.0";
if (sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0) < 0) if (sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0) < 0) {
{
Logger::error("Failed to get CPU frequency"); Logger::error("Failed to get CPU frequency");
} }
return "" + 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; unordered_flat_map<int, int> core_map;
return core_map; return core_map;
} }
auto get_battery() -> tuple<int, long, string> auto get_battery() -> tuple<int, long, string> {
{ //if (not has_battery)
// if (not has_battery)
return {0, 0, ""}; return {0, 0, ""};
} }
auto collect(const bool no_update) -> cpu_info & auto collect(const bool no_update) -> cpu_info & {
{
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty())) if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
return current_cpu; return current_cpu;
auto &cpu = current_cpu; auto &cpu = current_cpu;
@ -245,10 +222,9 @@ namespace Cpu
return cpu; return cpu;
} }
} } // namespace Cpu
namespace Mem namespace Mem {
{
bool has_swap = false; bool has_swap = false;
vector<string> fstab; vector<string> fstab;
fs::file_time_type fstab_time; fs::file_time_type fstab_time;
@ -257,55 +233,46 @@ namespace Mem
mem_info current_mem{}; mem_info current_mem{};
auto collect(const bool no_update) -> mem_info & auto collect(const bool no_update) -> mem_info & {
{
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty())) if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
return current_mem; return current_mem;
auto &show_disks = Config::getB("show_disks"); auto &show_disks = Config::getB("show_disks");
auto &mem = current_mem; auto &mem = current_mem;
FILE *fpIn = popen("/usr/bin/vm_stat", "r"); FILE *fpIn = popen("/usr/bin/vm_stat", "r");
if (fpIn) if (fpIn) {
{
char buf[512]; char buf[512];
while (fgets(buf, sizeof(buf), fpIn) != NULL) while (fgets(buf, sizeof(buf), fpIn) != NULL) {
{
char *delim = ":\n."; char *delim = ":\n.";
char *tokens = strtok(buf, delim); char *tokens = strtok(buf, delim);
while (tokens) while (tokens) {
{
char *label = tokens; char *label = tokens;
char *val = strtok(nullptr, delim); char *val = strtok(nullptr, delim);
if (strstr(label, "Pages free")) if (strstr(label, "Pages free")) {
{
uint64_t f = stoull(trim(val)); uint64_t f = stoull(trim(val));
mem.stats.at("available") = f * 4096; mem.stats.at("available") = f * 4096;
mem.stats.at("free") = f * 4096; mem.stats.at("free") = f * 4096;
mem.stats.at("cached") = 1; mem.stats.at("cached") = 1;
mem.stats.at("used") = Shared::totalMem - (f*4096); mem.stats.at("used") = Shared::totalMem - (f * 4096);
} }
tokens = strtok(nullptr, delim); tokens = strtok(nullptr, delim);
} }
} }
pclose(fpIn); pclose(fpIn);
} } else {
else
{
Logger::error("failed to read vm_stat"); Logger::error("failed to read vm_stat");
} }
//? Calculate percentages //? Calculate percentages
for (const auto& name : mem_names) { for (const auto &name : mem_names) {
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / Shared::totalMem)); 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(); while (cmp_greater(mem.percent.at(name).size(), width * 2)) mem.percent.at(name).pop_front();
} }
if (show_disks) if (show_disks) {
{ auto &disks = mem.disks;
auto& disks = mem.disks;
struct statfs *stfs; struct statfs *stfs;
int count = getmntinfo(&stfs, MNT_WAIT); int count = getmntinfo(&stfs, MNT_WAIT);
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++) {
{
std::error_code ec; std::error_code ec;
string mountpoint = stfs[i].f_mntonname; string mountpoint = stfs[i].f_mntonname;
Logger::debug("found mountpoint " + mountpoint); Logger::debug("found mountpoint " + mountpoint);
@ -320,10 +287,9 @@ namespace Mem
return mem; return mem;
} }
} } // namespace Mem
namespace Net namespace Net {
{
unordered_flat_map<string, net_info> current_net; unordered_flat_map<string, net_info> current_net;
net_info empty_net = {}; net_info empty_net = {};
vector<string> interfaces; vector<string> interfaces;
@ -335,25 +301,22 @@ namespace Net
uint64_t timestamp = 0; uint64_t timestamp = 0;
//* RAII wrapper for getifaddrs //* RAII wrapper for getifaddrs
class getifaddr_wrapper class getifaddr_wrapper {
{
struct ifaddrs *ifaddr; struct ifaddrs *ifaddr;
public: public:
int status; int status;
getifaddr_wrapper() { status = getifaddrs(&ifaddr); } getifaddr_wrapper() { status = getifaddrs(&ifaddr); }
~getifaddr_wrapper() { freeifaddrs(ifaddr); } ~getifaddr_wrapper() { freeifaddrs(ifaddr); }
auto operator()() -> struct ifaddrs * { return ifaddr; } auto operator()() -> struct ifaddrs * { return ifaddr; }
}; };
auto collect(const bool no_update) -> net_info & auto collect(const bool no_update) -> net_info & {
{
return empty_net; return empty_net;
} }
} } // namespace Net
namespace Proc namespace Proc {
{
vector<proc_info> current_procs; vector<proc_info> current_procs;
unordered_flat_map<string, string> uid_user; unordered_flat_map<string, string> uid_user;
@ -372,64 +335,50 @@ namespace Proc
detail_container detailed; detail_container detailed;
//* Generate process tree list //* Generate process tree list
void _tree_gen(proc_info &cur_proc, vector<proc_info> &in_procs, vector<std::reference_wrapper<proc_info>> &out_procs, int cur_depth, const bool collapsed, const string &filter, bool found = false, const bool no_update = false, const bool should_filter = false) void _tree_gen(proc_info &cur_proc, vector<proc_info> &in_procs, vector<std::reference_wrapper<proc_info>> &out_procs, int cur_depth, const bool collapsed, const string &filter, bool found = false, const bool no_update = false, const bool should_filter = false) {
{
auto cur_pos = out_procs.size(); auto cur_pos = out_procs.size();
bool filtering = false; bool filtering = false;
//? If filtering, include children of matching processes //? If filtering, include children of matching processes
if (not found and (should_filter or not filter.empty())) if (not found and (should_filter or not filter.empty())) {
{ if (not s_contains(std::to_string(cur_proc.pid), filter) and not s_contains(cur_proc.name, filter) and not s_contains(cur_proc.cmd, filter) and not s_contains(cur_proc.user, filter)) {
if (not s_contains(std::to_string(cur_proc.pid), filter) and not s_contains(cur_proc.name, filter) and not s_contains(cur_proc.cmd, filter) and not s_contains(cur_proc.user, filter))
{
filtering = true; filtering = true;
cur_proc.filtered = true; cur_proc.filtered = true;
filter_found++; filter_found++;
} } else {
else
{
found = true; found = true;
cur_depth = 0; cur_depth = 0;
} }
} } else if (cur_proc.filtered)
else if (cur_proc.filtered)
cur_proc.filtered = false; cur_proc.filtered = false;
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree //? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
if (not collapsed and not filtering) if (not collapsed and not filtering) {
{
out_procs.push_back(std::ref(cur_proc)); out_procs.push_back(std::ref(cur_proc));
cur_proc.tree_index = out_procs.size() - 1; cur_proc.tree_index = out_procs.size() - 1;
//? Try to find name of the binary file and append to program name if not the same //? Try to find name of the binary file and append to program name if not the same
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
{
std::string_view cmd_view = cur_proc.cmd; std::string_view cmd_view = cur_proc.cmd;
cmd_view = cmd_view.substr((size_t)0, min(cmd_view.find(' '), cmd_view.size())); cmd_view = cmd_view.substr((size_t)0, min(cmd_view.find(' '), cmd_view.size()));
cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, cmd_view.size())); cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
cur_proc.short_cmd = (string)cmd_view; cur_proc.short_cmd = (string)cmd_view;
} }
} } else {
else
{
cur_proc.tree_index = in_procs.size(); cur_proc.tree_index = in_procs.size();
} }
//? Recursive iteration over all children //? Recursive iteration over all children
int children = 0; int children = 0;
for (auto &p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) for (auto &p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
{ if (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
if (not no_update and not filtering and (collapsed or cur_proc.collapsed))
{
out_procs.back().get().cpu_p += p.cpu_p; out_procs.back().get().cpu_p += p.cpu_p;
out_procs.back().get().mem += p.mem; out_procs.back().get().mem += p.mem;
out_procs.back().get().threads += p.threads; out_procs.back().get().threads += p.threads;
filter_found++; filter_found++;
} }
if (collapsed and not filtering) if (collapsed and not filtering) {
{
cur_proc.filtered = true; cur_proc.filtered = true;
} } else
else
children++; children++;
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cur_proc.collapsed), filter, found, no_update, should_filter); _tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cur_proc.collapsed), filter, found, no_update, should_filter);
} }
@ -445,33 +394,27 @@ namespace Proc
} }
//* Get detailed info for selected process //* Get detailed info for selected process
void _collect_details(const size_t pid, const uint64_t uptime, vector<proc_info> &procs) void _collect_details(const size_t pid, const uint64_t uptime, vector<proc_info> &procs) {
{
} }
//* Collects and sorts process information from /proc //* Collects and sorts process information from /proc
auto collect(const bool no_update) -> vector<proc_info> & auto collect(const bool no_update) -> vector<proc_info> & {
{
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
struct kinfo_proc *processes = NULL; struct kinfo_proc *processes = NULL;
const double uptime = system_uptime(); const double uptime = system_uptime();
auto procs = &current_procs; auto procs = &current_procs;
for (int retry = 3; retry > 0; retry--) for (int retry = 3; retry > 0; retry--) {
{
size_t size = 0; size_t size = 0;
if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) {
{
Logger::error("Unable to get size of kproc_infos"); Logger::error("Unable to get size of kproc_infos");
} }
processes = (struct kinfo_proc *)malloc(size); processes = (struct kinfo_proc *)malloc(size);
if (sysctl(mib, 4, processes, &size, NULL, 0) == 0) if (sysctl(mib, 4, processes, &size, NULL, 0) == 0) {
{
size_t count = size / sizeof(struct kinfo_proc); size_t count = size / sizeof(struct kinfo_proc);
for (size_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++) {
{
struct kinfo_proc kproc = processes[i]; struct kinfo_proc kproc = processes[i];
Proc::proc_info p{kproc.kp_proc.p_pid}; Proc::proc_info p{kproc.kp_proc.p_pid};
char fullname[PROC_PIDPATHINFO_MAXSIZE]; char fullname[PROC_PIDPATHINFO_MAXSIZE];
@ -482,8 +425,7 @@ namespace Proc
p.ppid = kproc.kp_eproc.e_ppid; p.ppid = kproc.kp_eproc.e_ppid;
p.p_nice = kproc.kp_proc.p_nice; p.p_nice = kproc.kp_proc.p_nice;
struct proc_taskinfo pti; struct proc_taskinfo pti;
if (sizeof(pti) == proc_pidinfo(p.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) if (sizeof(pti) == proc_pidinfo(p.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
{
p.threads = pti.pti_threadnum; p.threads = pti.pti_threadnum;
p.cpu_t = pti.pti_total_user + pti.pti_total_system; p.cpu_t = pti.pti_total_user + pti.pti_total_system;
p.cpu_c = (double)p.cpu_t / max(1.0, (uptime * Shared::clkTck) - p.cpu_s); p.cpu_c = (double)p.cpu_t / max(1.0, (uptime * Shared::clkTck) - p.cpu_s);
@ -498,20 +440,17 @@ namespace Proc
} }
return current_procs; return current_procs;
} }
} } // namespace Proc
namespace Tools namespace Tools {
{ double system_uptime() {
double system_uptime()
{
struct timeval ts, currTime; struct timeval ts, currTime;
std::size_t len = sizeof(ts); std::size_t len = sizeof(ts);
int mib[2] = {CTL_KERN, KERN_BOOTTIME}; int mib[2] = {CTL_KERN, KERN_BOOTTIME};
if (sysctl(mib, 2, &ts, &len, NULL, 0) != -1) if (sysctl(mib, 2, &ts, &len, NULL, 0) != -1) {
{
gettimeofday(&currTime, NULL); gettimeofday(&currTime, NULL);
return currTime.tv_sec - ts.tv_sec; return currTime.tv_sec - ts.tv_sec;
} }
return 0.0; return 0.0;
} }
} } // namespace Tools