From ad5864266ad25f5c25afa070e2859a521a236175 Mon Sep 17 00:00:00 2001 From: aristocratos Date: Sun, 4 Jul 2021 01:18:48 +0200 Subject: [PATCH] Added Proc::_collect_details for process info box collection --- Makefile | 14 ++--- src/btop.cpp | 39 +++++++++---- src/btop_config.cpp | 11 ++++ src/btop_config.hpp | 3 + src/btop_draw.cpp | 39 +++++++++---- src/btop_draw.hpp | 27 +++++++-- src/btop_input.cpp | 8 ++- src/btop_input.hpp | 3 + src/btop_linux.cpp | 138 +++++++++++++++++++++++++++++++++----------- src/btop_shared.hpp | 16 +++-- src/btop_theme.cpp | 1 + 11 files changed, 224 insertions(+), 75 deletions(-) diff --git a/Makefile b/Makefile index f2dc173..e721a33 100644 --- a/Makefile +++ b/Makefile @@ -55,19 +55,15 @@ distclean: clean install: @mkdir -p $(DESTDIR)$(PREFIX)/bin - @cp -p bin/btop $(DESTDIR)$(PREFIX)/bin/btop + @cp -p $(TARGETDIR)/btop $(DESTDIR)$(PREFIX)/bin/btop @mkdir -p $(DESTDIR)$(DOCDIR) @cp -p README.md $(DESTDIR)$(DOCDIR) @cp -pr themes $(DESTDIR)$(PREFIX)/share/btop @chmod 755 $(DESTDIR)$(PREFIX)/bin/btop -#Set suid bit for btop to root, will make btop run as root regardless of actual user +#Set suid bit for btop to root, will make btop run with admin privileges regardless of actual user su-setuid: - @su --session-command "make as-root-setuid" root - -as-root-setuid: - @chown root:root $(DESTDIR)$(PREFIX)/bin/btop - @chmod 4755 $(DESTDIR)$(PREFIX)/bin/btop + @su --session-command "chown root:root $(DESTDIR)$(PREFIX)/bin/btop && chmod 4755 $(DESTDIR)$(PREFIX)/bin/btop" root uninstall: @rm -rf $(DESTDIR)$(PREFIX)/bin/btop @@ -78,8 +74,8 @@ uninstall: -include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT)) #Link -$(TARGET): $(OBJECTS) - $(CXX) -o $(TARGETDIR)/$(TARGET) $^ +btop: $(OBJECTS) + $(CXX) -o $(TARGETDIR)/btop $^ #Compile $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) diff --git a/src/btop.cpp b/src/btop.cpp index b1df9a1..9cf1864 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -16,7 +16,6 @@ indent = tab tab-size = 4 */ - #include #include #include @@ -32,6 +31,7 @@ tab-size = 4 #include #include #include +#include #include #include @@ -77,7 +77,6 @@ namespace fs = std::filesystem; namespace rng = std::ranges; using namespace Tools; - namespace Global { string banner; size_t banner_width = 0; @@ -89,7 +88,7 @@ namespace Global { uint64_t start_time; - bool quitting = false; + atomic quitting (false); bool arg_tty = false; bool arg_low_color = false; @@ -277,12 +276,12 @@ int main(int argc, char **argv){ } if (std::error_code ec; not Global::self_path.empty()) { Theme::theme_dir = fs::canonical(Global::self_path / "../share/btop/themes", ec); - if (ec or access(Theme::theme_dir.c_str(), R_OK) == -1) Theme::theme_dir.clear(); + if (ec or not fs::is_directory(Theme::theme_dir) or access(Theme::theme_dir.c_str(), R_OK) == -1) Theme::theme_dir.clear(); } if (Theme::theme_dir.empty()) { for (auto theme_path : {"/usr/local/share/btop/themes", "/usr/share/btop/themes"}) { - if (fs::exists(fs::path(theme_path)) and access(theme_path, R_OK) != -1) { + if (fs::is_directory(fs::path(theme_path)) and access(theme_path, R_OK) != -1) { Theme::theme_dir = fs::path(theme_path); break; } @@ -465,7 +464,7 @@ int main(int argc, char **argv){ if (false) { - vector mydata; + deque mydata; for (long long i = 0; i <= 100; i++) mydata.push_back(i); for (long long i = 100; i >= 0; i--) mydata.push_back(i); // mydata.push_back(0); @@ -606,6 +605,7 @@ int main(int argc, char **argv){ string filter; string filter_cur; string key; + vector plist; int xc; for (size_t i : iota(0, (int)Term::height - 19)){ @@ -613,14 +613,20 @@ int main(int argc, char **argv){ greyscale.push_back(Theme::dec_to_color(xc, xc, xc)); } - string pbox = Draw::createBox({.x = 1, .y = 10, .width = Term::width, .height = Term::height - 16, .line_color = Theme::c("proc_box"), .title = "testbox", .title2 = "below", .fill = false, .num = 7}); + string pbox = Draw::createBox({.x = 1, .y = 10, .width = Term::width, .height = Term::height - 18, .line_color = Theme::c("proc_box"), .title = "testbox", .title2 = "below", .fill = false, .num = 7}); pbox += Mv::r(1) + Theme::c("title") + Fx::b + rjust("Pid:", 8) + " " + ljust("Program:", 16) + " " + ljust("Command:", Term::width - 70) + " Threads: " + ljust("User:", 10) + " " + rjust("MemB", 5) + " " + rjust("Cpu%", 14) + "\n" + Fx::reset + Mv::save; while (key != "q") { timestamp = time_micros(); tsl = time_ms() + timer; - auto plist = Proc::collect(); + try { + plist = Proc::collect(); + } + catch (std::exception const& e) { + Logger::error("Caught exception in Proc::collect() : "s + e.what()); + exit(1); + } timestamp2 = time_micros(); timestamp = timestamp2 - timestamp; ostring.clear(); @@ -645,21 +651,25 @@ int main(int argc, char **argv){ cmd_cond = p.cmd.substr(0, std::min(p.cmd.find(' '), p.cmd.size())); cmd_cond = cmd_cond.substr(std::min(cmd_cond.find_last_of('/') + 1, cmd_cond.size())); } - ostring += Mv::r(1) + (Config::getB("tty_mode") ? "" : greyscale[lc]) + ljust(p.prefix + to_string(p.pid) + " " + p.name + " " + (not cmd_cond.empty() and cmd_cond != p.name ? "(" + cmd_cond + ")" : ""), Term::width - 40, true) + " " + ostring += Mv::r(1) + (Config::getB("tty_mode") ? "" : greyscale[lc]) + ljust(p.prefix + to_string(p.pid) + " " + p.name + " " + + (not cmd_cond.empty() and cmd_cond != p.name ? "(" + cmd_cond + ")" : ""), Term::width - 40, true) + " " + rjust(to_string(p.threads), 5) + " " + ljust(p.user, 10) + " " + rjust(floating_humanizer(p.mem, true), 5) + string(11, ' ') + (p.cpu_p < 10 or p.cpu_p >= 100 ? rjust(to_string(p.cpu_p), 3) + " " : rjust(to_string(p.cpu_p), 4)) + "\n"; } - if (lc++ > Term::height - 21) break; + if (lc++ > Term::height - 23) break; } - while (lc++ < Term::height - 19) ostring += Mv::r(1) + string(Term::width - 2, ' ') + "\n"; + while (lc++ < Term::height - 21) ostring += Mv::r(1) + string(Term::width - 2, ' ') + "\n"; avgtimes.push_front(timestamp); if (avgtimes.size() > 30) avgtimes.pop_back(); cout << pbox << ostring << Fx::reset << "\n" << endl; + cout << " Details for " << Proc::detailed.entry.name << " (" << Proc::detailed.entry.pid << ") Status: " << Proc::detailed.status << " Elapsed: " << Proc::detailed.elapsed + << " Mem: " << floating_humanizer(Proc::detailed.entry.mem) << " " + << "\n Parent: " << Proc::detailed.parent << " IO in/out: " << Proc::detailed.io_read << "/" << Proc::detailed.io_write << " " << endl; cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 5) << " μs. Average: " << rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() << " samples. Drawing took: " << time_micros() - timestamp2 << " μs.\nNumber of processes: " << Proc::numpids << ". Number in vector: " << plist.size() << ". Run count: " << @@ -675,6 +685,7 @@ int main(int argc, char **argv){ else if (ulen(key) == 1 ) filter.append(key); else { key.clear(); continue; } if (filter != Config::getS("proc_filter")) Config::set("proc_filter", filter); + key.clear(); break; } else if (key == "q") break; @@ -693,6 +704,12 @@ int main(int argc, char **argv){ else if (key == "r") Config::flip("proc_reversed"); else if (key == "c") Config::flip("proc_per_core"); else if (key == "delete") { filter.clear(); Config::set("proc_filter", filter); } + else if (key == "d" ) { + Config::flip("show_detailed"); + if (Config::getB("show_detailed")) { + Config::set("detailed_pid", (int)plist.at(0).pid); + } + } else continue; break; } diff --git a/src/btop_config.cpp b/src/btop_config.cpp index 0ed0864..30ec4c8 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -222,6 +222,8 @@ namespace Config { }; unordered_flat_map intsTmp; + vector valid_boxes = { "cpu", "mem", "net", "proc" }; + bool _locked(const string& name){ atomic_wait(writelock); if (not write_new and rng::find_if(descriptions, [&name](const auto& a){ return a.at(0) == name; }) != descriptions.end()) @@ -297,6 +299,13 @@ namespace Config { writelock = false; } + bool check_boxes(string boxes){ + for (auto& box : ssplit(boxes)) { + if (not v_contains(valid_boxes, box)) return false; + } + return true; + } + void load(fs::path conf_file, vector& load_errors){ if (conf_file.empty()) return; @@ -352,6 +361,8 @@ namespace Config { load_errors.push_back("Invalid log_level: " + value); else if (name == "graph_symbol" and not v_contains(valid_graph_symbols, value)) load_errors.push_back("Invalid graph symbol identifier: " + value); + else if (name == "shown_boxes" and not value.empty() and not check_boxes(value)) + load_errors.push_back("Invalid box name in shown_boxes. Using default."); else strings.at(name) = value; } diff --git a/src/btop_config.hpp b/src/btop_config.hpp index 31b4e28..4769e30 100644 --- a/src/btop_config.hpp +++ b/src/btop_config.hpp @@ -32,6 +32,9 @@ namespace Config { extern const vector valid_graph_symbols; + //* Check if string only contains space seperated valid names for boxes + bool check_boxes(string boxes); + //* Return bool for config key const bool& getB(string name); diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp index bea804e..2f1cb9c 100644 --- a/src/btop_draw.cpp +++ b/src/btop_draw.cpp @@ -25,8 +25,10 @@ tab-size = 4 #include #include #include +#include #include + using std::round, std::views::iota, std::string_literals::operator""s, std::clamp, std::array, std::floor; namespace rng = std::ranges; @@ -98,7 +100,6 @@ namespace Draw { using namespace Tools; - //* Create a box using values from a BoxConf struct and return as a string string createBox(BoxConf c){ string out; string lcolor = (c.line_color.empty()) ? Theme::c("div_line") : c.line_color; @@ -137,7 +138,7 @@ namespace Draw { return out + Fx::reset + Mv::to(c.y + 1, c.x + 1); } - + //* Meter class ------------------------------------------------------------------------------------------------------------> void Meter::operator()(int width, string color_gradient, bool invert) { this->width = width; this->color_gradient = color_gradient; @@ -164,7 +165,8 @@ namespace Draw { return out; } - void Graph::_create(const vector& data, int data_offset) { + //* Graph class ------------------------------------------------------------------------------------------------------------> + void Graph::_create(const deque& data, int data_offset) { const bool mult = (data.size() - data_offset > 1); if (mult and (data.size() - data_offset) % 2 != 0) data_offset--; auto& graph_symbol = Symbols::graph_symbols.at(symbol + '_' + (invert ? "down" : "up")); @@ -203,7 +205,6 @@ namespace Draw { graphs[current][horizon] += (height == 1 and result[0] + result[1] == 0) ? Mv::r(1) : graph_symbol[(result[0] * 5 + result[1])]; } if (mult and i > data_offset) last = data_value; - } last = data_value; if (height == 1) @@ -219,8 +220,7 @@ namespace Draw { out += Fx::reset; } - - void Graph::operator()(int width, int height, string color_gradient, const vector& data, string symbol, bool invert, bool no_zero, long long max_value, long long offset) { + void Graph::operator()(int width, int height, string color_gradient, const deque& data, string symbol, bool invert, bool no_zero, long long max_value, long long offset) { graphs[true].clear(); graphs[false].clear(); this->width = width; this->height = height; this->invert = invert; this->offset = offset; @@ -246,8 +246,7 @@ namespace Draw { this->_create(data, data_offset); } - - string& Graph::operator()(const vector& data, bool data_same) { + string& Graph::operator()(const deque& data, bool data_same) { if (data_same) return out; //? Make room for new characters on graph @@ -263,18 +262,38 @@ namespace Draw { string& Graph::operator()() { return out; } + //*-------------------------------------------------------------------------------------------------------------------------> + + void calcSizes(){ + + } } -namespace Box { +namespace Cpu { + Draw::BoxConf box; + string background; +} +namespace Mem { + + Draw::BoxConf box; + string background; + +} + +namespace Net { + + Draw::BoxConf box; + string background; } namespace Proc { - // Draw::BoxConf box; + Draw::BoxConf box; + string background; } \ No newline at end of file diff --git a/src/btop_draw.hpp b/src/btop_draw.hpp index 6f86789..f91ef3e 100644 --- a/src/btop_draw.hpp +++ b/src/btop_draw.hpp @@ -21,8 +21,9 @@ tab-size = 4 #include #include #include +#include -using std::string, std::vector, robin_hood::unordered_flat_map; +using std::string, std::vector, robin_hood::unordered_flat_map, std::deque; namespace Symbols { extern const string h_line; @@ -74,30 +75,44 @@ namespace Draw { unordered_flat_map> graphs = { {true, {}}, {false, {}}}; //* Create two representations of the graph to switch between to represent two values for each braille character - void _create(const vector& data, int data_offset); + void _create(const deque& data, int data_offset); public: //* Set graph options and initialize with data - void operator()(int width, int height, string color_gradient, const vector& data, string symbol="default", bool invert=false, bool no_zero=false, long long max_value=0, long long offset=0); + void operator()(int width, int height, string color_gradient, const deque& data, string symbol="default", bool invert=false, bool no_zero=false, long long max_value=0, long long offset=0); //* Add last value from back of and return string representation of graph - string& operator()(const vector& data, bool data_same=false); + string& operator()(const deque& data, bool data_same=false); //* Return string representation of graph string& operator()(); }; + //* Calculate sizes of boxes, draw outlines and save to enabled boxes namespaces + void calcSizes(); + } -namespace Box { +namespace Cpu { + extern Draw::BoxConf box; +} +namespace Mem { + + extern Draw::BoxConf box; + +} + +namespace Net { + + extern Draw::BoxConf box; } namespace Proc { - // Draw::BoxConf box; + extern Draw::BoxConf box; } \ No newline at end of file diff --git a/src/btop_input.cpp b/src/btop_input.cpp index 1551806..aa18b24 100644 --- a/src/btop_input.cpp +++ b/src/btop_input.cpp @@ -66,12 +66,15 @@ namespace Input { }; } + std::atomic interrupt (false); + string last = ""; bool poll(int timeout){ if (timeout < 1) return cin.rdbuf()->in_avail() > 0; while (timeout > 0) { if (cin.rdbuf()->in_avail() > 0) return true; + if (interrupt) { interrupt = false; return false; } sleep_ms(timeout < 10 ? timeout : 10); timeout -= 10; } @@ -91,7 +94,10 @@ namespace Input { } string wait(){ - while (cin.rdbuf()->in_avail() < 1) sleep_ms(10); + while (cin.rdbuf()->in_avail() < 1) { + if (interrupt) { interrupt = false; return string(); } + sleep_ms(10); + } return get(); } diff --git a/src/btop_input.hpp b/src/btop_input.hpp index b3ecc39..adb4317 100644 --- a/src/btop_input.hpp +++ b/src/btop_input.hpp @@ -19,6 +19,7 @@ tab-size = 4 #pragma once #include +#include /* The input functions relies on the following std::cin options being set: cin.sync_with_stdio(false); @@ -29,6 +30,8 @@ tab-size = 4 //* Functions and variables for handling keyboard and mouse input namespace Input { + extern std::atomic interrupt; + //* Last entered key extern std::string last; diff --git a/src/btop_linux.cpp b/src/btop_linux.cpp index fbcc048..9b5aad9 100644 --- a/src/btop_linux.cpp +++ b/src/btop_linux.cpp @@ -25,7 +25,6 @@ tab-size = 4 #include #include #include -#include #include #include #include @@ -39,7 +38,7 @@ tab-size = 4 using std::string, std::vector, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, - std::round, std::string_literals::operator""s, robin_hood::unordered_flat_map; + std::round, std::string_literals::operator""s; namespace fs = std::filesystem; namespace rng = std::ranges; using namespace Tools; @@ -101,6 +100,8 @@ namespace Proc { size_t depth = 0; bool collapsed = false; }; + + vector current_procs; unordered_flat_map cache; unordered_flat_map uid_user; @@ -120,6 +121,19 @@ namespace Proc { "cpu direct", "cpu lazy", }; + unordered_flat_map proc_states = { + {'R', "Running"}, + {'S', "Sleeping"}, + {'D', "Waiting"}, + {'Z', "Zombie"}, + {'T', "Stopped"}, + {'t', "Tracing"}, + {'X', "Dead"}, + {'x', "Dead"}, + {'K', "Wakekill"}, + {'W', "Unknown"}, + {'P', "Parked"} + }; detail_container detailed; @@ -163,42 +177,100 @@ namespace Proc { out_procs.at(cur_pos).prefix = " │ "s * cur_depth + (children > 0 ? (cache.at(cur_proc.pid).collapsed ? "[+] " : "[-] ") : " ├─ "); } - //* Get datiled info for selected process - void _collect_details(proc_info p){ - fs::path pid_path = Shared::proc_path / std::to_string(p.pid); + //* Get detailed info for selected process + void _collect_details(size_t pid, uint64_t uptime, vector& procs, bool is_filtered){ + fs::path pid_path = Shared::proc_path / std::to_string(pid); + + if (pid != detailed.last_pid) { + detailed.last_pid = pid; + detailed.cpu_percent.clear(); + detailed.parent.clear(); + detailed.io_read.clear(); + detailed.io_write.clear(); + detailed.skip_smaps = false; + } + + //? Copy proc_info for process from proc vector + auto p = rng::find(procs, pid, &proc_info::pid); + detailed.entry = *p; + + //? Update cpu percent deque for process cpu graph + detailed.cpu_percent.push_back(round(detailed.entry.cpu_c)); + while (detailed.cpu_percent.size() > Term::width) detailed.cpu_percent.pop_front(); + + //? Process runtime + detailed.elapsed = sec_to_dhms(uptime - (cache.at(pid).cpu_s / Shared::clk_tck)); + + //? Get parent process name + if (detailed.parent.empty() and cache.contains(detailed.entry.ppid)) detailed.parent = cache.at(detailed.entry.ppid).name; + + //? Expand process status from single char to explanative string + detailed.status = (proc_states.contains(detailed.entry.state)) ? proc_states.at(detailed.entry.state) : "Unknown"; - detailed.entry = p; ifstream d_read; + string short_str; - //* Get RSS mem from smaps - if (fs::exists(pid_path / "smaps")) { - pid_path /= "smaps"; - d_read.open(pid_path); + //? Try to get RSS mem from proc/[pid]/smaps + detailed.memory.clear(); + if (not detailed.skip_smaps and fs::exists(pid_path / "smaps")) { + d_read.open(pid_path / "smaps"); if (d_read.good()) { uint64_t rss = 0; - string val; try { while (not d_read.eof()) { d_read.ignore(SSmax, 'R'); if (d_read.peek() == 's') { d_read.ignore(SSmax, ':'); - getline(d_read, val, 'k'); - rss += stoull(val) << 10; + getline(d_read, short_str, 'k'); + rss += stoull(short_str); } } - detailed.entry.mem = rss; + if (rss == detailed.entry.mem >> 10) + detailed.skip_smaps = true; + else + detailed.memory = floating_humanizer(rss, false, 1); } catch (std::invalid_argument const&) {} catch (std::out_of_range const&) {} } d_read.close(); } + if (detailed.memory.empty()) detailed.memory = floating_humanizer(detailed.entry.mem, false); + + //? Get bytes read and written from proc/[pid]/io + if (fs::exists(pid_path / "io")) { + d_read.open(pid_path / "io"); + if (d_read.good()) { + try { + string name; + while (not d_read.eof()) { + getline(d_read, name, ':'); + if (name.ends_with("read_bytes")) { + getline(d_read, short_str); + detailed.io_read = floating_humanizer(stoull(short_str)); + } + else if (name.ends_with("write_bytes")) { + getline(d_read, short_str); + detailed.io_write = floating_humanizer(stoull(short_str)); + break; + } + else + d_read.ignore(SSmax, '\n'); + } + } + catch (std::invalid_argument const&) {} + catch (std::out_of_range const&) {} + } + d_read.close(); + } + + if (is_filtered) procs.erase(p); + } - vector current_procs; - - //* Collects and sorts process information from /proc, saves to and returns reference to Proc::current_procs; - vector& collect(){ + //* Collects and sorts process information from /proc + vector& collect(bool return_last){ + if (return_last) return current_procs; atomic_wait_set(collecting); auto& sorting = Config::getS("proc_sorting"); auto reverse = Config::getB("proc_reversed"); @@ -210,14 +282,15 @@ namespace Proc { ifstream pread; string long_string; string short_str; - auto uptime = system_uptime(); + double uptime = system_uptime(); vector procs; procs.reserve((numpids + 10)); int npids = 0; int cmult = (per_core) ? Global::coreCount : 1; bool got_detailed = false; + bool detailed_filtered = false; - //* Update uid_user map if /etc/passwd changed since last run + //? Update uid_user map if /etc/passwd changed since last run if (not Shared::passwd_path.empty() and fs::last_write_time(Shared::passwd_path) != Shared::passwd_time) { string r_uid, r_user; Shared::passwd_time = fs::last_write_time(Shared::passwd_path); @@ -254,7 +327,6 @@ namespace Proc { return current_procs; } - bool new_cache = false; string pid_str = d.path().filename(); if (not isdigit(pid_str[0])) continue; @@ -264,7 +336,6 @@ namespace Proc { //* Cache program name, command and username if (not cache.contains(new_proc.pid)) { string name, cmd, user; - new_cache = true; pread.open(d.path() / "comm"); if (not pread.good()) continue; getline(pread, name); @@ -302,13 +373,14 @@ namespace Proc { //* Match filter if defined if (not tree and not filter.empty() - and not (show_detailed and new_proc.pid == detailed_pid) and pid_str.find(filter) == string::npos and cache[new_proc.pid].name.find(filter) == string::npos and cache[new_proc.pid].cmd.find(filter) == string::npos and cache[new_proc.pid].user.find(filter) == string::npos) { - if (new_cache) cache.erase(new_proc.pid); - continue; + if (show_detailed and new_proc.pid == detailed_pid) + detailed_filtered = true; + else + continue; } new_proc.name = cache[new_proc.pid].name; new_proc.cmd = cache[new_proc.pid].cmd; @@ -356,7 +428,7 @@ namespace Proc { } case 20: { //? Number of threads new_proc.threads = stoull(short_str); - next_x = (new_cache) ? 22 : 24; + next_x = (cache[new_proc.pid].cpu_s == 0) ? 22 : 24; continue; } case 22: { //? Save cpu seconds to cache if missing @@ -386,8 +458,6 @@ namespace Proc { if (x-offset < 24) continue; - // _parse_smaps(new_proc); - //? Process cpu usage since last update new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache[new_proc.pid].cpu_t) / (cputimes - old_cputimes)) / 10.0; @@ -397,9 +467,7 @@ namespace Proc { //? Update cache with latest cpu times cache[new_proc.pid].cpu_t = cpu_t; - //? Update the details info box for process if active - if (show_detailed and new_proc.pid == detailed_pid) { - _collect_details(new_proc); + if (show_detailed and not got_detailed and new_proc.pid == detailed_pid) { got_detailed = true; } @@ -408,8 +476,12 @@ namespace Proc { } - if (show_detailed and not got_detailed) { - detailed.entry.state = 'X'; + //* Update the details info box for process if active + if (show_detailed and got_detailed) { + _collect_details(detailed_pid, round(uptime), procs, detailed_filtered); + } + else if (show_detailed and not got_detailed) { + detailed.status = "Dead"; } //* Sort processes diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp index 94b83b0..bd10d9f 100644 --- a/src/btop_shared.hpp +++ b/src/btop_shared.hpp @@ -22,8 +22,10 @@ tab-size = 4 #include #include #include +#include +#include -using std::string, std::vector; +using std::string, std::vector, std::deque, robin_hood::unordered_flat_map; namespace Global { extern const string Version; @@ -49,6 +51,9 @@ namespace Proc { //? Contains the valid sorting options for processes extern vector sort_vector; + //? Translation from process state char to explanative string + extern unordered_flat_map proc_states; + //* Container for process information struct proc_info { size_t pid; @@ -65,13 +70,14 @@ namespace Proc { //* Container for process info box struct detail_container { proc_info entry; - string elapsed, parent, status, io_read, io_write; + string elapsed, parent, status, io_read, io_write, memory; + size_t last_pid = 0; + bool skip_smaps = false; + deque cpu_percent; }; extern detail_container detailed; - extern vector current_procs; - //* Collects and sorts process information from /proc, saves and returns reference to Proc::current_procs; - vector& collect(); + vector& collect(bool return_last=false); } \ No newline at end of file diff --git a/src/btop_theme.cpp b/src/btop_theme.cpp index 50b7563..e87f9e6 100644 --- a/src/btop_theme.cpp +++ b/src/btop_theme.cpp @@ -363,6 +363,7 @@ namespace Theme { themes.push_back("TTY"); for (const auto& path : { theme_dir, user_theme_dir } ) { + if (path.empty()) continue; for (auto& file : fs::directory_iterator(path)){ if (file.path().extension() == ".theme" and access(file.path().c_str(), R_OK) != -1) { themes.push_back(file.path().c_str());