diff options
-rw-r--r-- | Makefile | 18 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/btop.cpp | 269 | ||||
-rw-r--r-- | src/btop_config.cpp | 48 | ||||
-rw-r--r-- | src/btop_config.hpp | 8 | ||||
-rw-r--r-- | src/btop_draw.cpp | 36 | ||||
-rw-r--r-- | src/btop_input.cpp | 61 | ||||
-rw-r--r-- | src/btop_input.hpp | 3 | ||||
-rw-r--r-- | src/btop_linux.cpp | 42 | ||||
-rw-r--r-- | src/btop_shared.hpp | 67 | ||||
-rw-r--r-- | src/btop_tools.cpp | 4 | ||||
-rw-r--r-- | src/btop_tools.hpp | 19 |
12 files changed, 360 insertions, 217 deletions
@@ -4,17 +4,11 @@ DOCDIR ?= $(PREFIX)/share/btop/doc #Compiler and Linker CXX := g++ -#If using g++ try to make sure we are using version 11 or 10 -ifeq ($(CXX),g++) - CXX_VERSION = $(shell $(CXX) -dumpversion) - ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0) - ifneq ($(shell command -v g++-11),) - CXX := g++-11 - else ifneq ($(shell test $(CXX_VERSION) -eq 10; echo $$?),0) - ifneq ($(shell command -v g++-10),) - CXX := g++-10 - endif - endif +#Try to make sure we are using GCC/G++ version 11 or later +CXX_VERSION = $(shell $(CXX) -dumpversion) +ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0) + ifneq ($(shell command -v g++-11),) + CXX := g++-11 endif endif @@ -75,7 +69,7 @@ uninstall: #Link btop: $(OBJECTS) - $(CXX) -o $(TARGETDIR)/btop $^ + $(CXX) -o $(TARGETDIR)/btop $^ -pthread #Compile $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) @@ -126,7 +126,7 @@ Options menu. #### Manual compilation and installation -Needs at least GCC/G++ 10, preferably 11 (or higher) to make use of some C++20 functionality. +Needs GCC/G++ 11 or higher, (GCC 10 is missing some C++20 features). >Install dependencies (Ubuntu 21.04 Hirsute) diff --git a/src/btop.cpp b/src/btop.cpp index 31fd07a..1a35181 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -81,6 +81,9 @@ namespace Global { string banner; size_t banner_width = 0; + string exit_error_msg; + atomic<bool> thread_exception (false); + fs::path self_path; bool debuginit = false; @@ -142,10 +145,7 @@ void argumentParser(int argc, char **argv){ void _resize(bool force=false){ if (Term::refresh(false) or force) { Global::resized = true; - if (Runner::active) { - Runner::stop = true; - atomic_wait(Runner::active); - } + Runner::stop(); Term::refresh(); } else return; @@ -160,29 +160,23 @@ void _resize(bool force=false){ } //* Exit handler; stops threads, restores terminal and saves config changes -void clean_quit(int sig=-1){ +void clean_quit(int sig){ if (Global::quitting) return; Global::quitting = true; - if (Runner::active) { - Runner::stop = true; - atomic_wait(Runner::active); - } + Runner::stop(); if (Term::initialized) { Term::restore(); if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush; } - Config::write(); Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time)); + if (not Global::exit_error_msg.empty()) cout << Global::exit_error_msg << endl; if (sig != -1) exit(sig); } //* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP void _sleep(){ - if (Runner::active) { - Runner::stop = true; - atomic_wait(Runner::active); - } + Runner::stop(); if (Term::initialized) { Term::restore(); if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush; @@ -259,9 +253,97 @@ void banner_gen() { + Fx::i + "v" + Global::Version + Fx::ui; } +//? Manages secondary thread for collection and drawing of boxes namespace Runner { atomic<bool> active (false); - atomic<bool> stop (false); + atomic<bool> stopping (false); + atomic<bool> has_output (false); + atomic<uint64_t> time_spent (0); + + string output; + + void _runner(const vector<string> boxes, const bool no_update, const bool force_redraw, const bool interrupt) { + auto timestamp = time_micros(); + string out; + out.reserve(output.size()); + + try { + for (auto& box : boxes) { + if (stopping) break; + try { + if (box == "cpu") { + out += Cpu::draw(Cpu::collect(no_update), force_redraw); + } + else if (box == "mem") { + out += Mem::draw(Mem::collect(no_update), force_redraw); + } + else if (box == "net") { + out += Net::draw(Net::collect(no_update), force_redraw); + } + else if (box == "proc") { + out += Proc::draw(Proc::collect(no_update), force_redraw); + } + } + catch (const std::exception& e) { + string fname = box; + fname[0] = toupper(fname[0]); + throw std::runtime_error(fname + "::draw(" + fname + "::collect(no_update=" + (no_update ? "true" : "false") + + "), force_redraw=" + (force_redraw ? "true" : "false") + ") : " + (string)e.what()); + } + } + } + catch (std::exception& e) { + Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what(); + Logger::error(Global::exit_error_msg); + Global::thread_exception = true; + Input::interrupt = true; + stopping = true; + } + + if (stopping) { + active = false; + active.notify_all(); + return; + } + + if (out.empty()) { + out += "No boxes shown!"; + } + + output.swap(out); + time_spent = time_micros() - timestamp; + has_output = true; + if (interrupt) Input::interrupt = true; + + active = false; + active.notify_one(); + } + + void run(const string box, const bool no_update, const bool force_redraw, const bool input_interrupt) { + active.wait(true); + if (stopping) return; + active = true; + Config::lock(); + vector<string> boxes; + if (box.empty()) boxes = Config::current_boxes; + else boxes.push_back(box); + std::thread run_thread(_runner, boxes, no_update, force_redraw, input_interrupt); + run_thread.detach(); + } + + void stop() { + stopping = true; + active.wait(true); + Config::unlock(); + stopping = false; + } + + string get_output() { + active.wait(true); + Config::unlock(); + has_output = false; + return output; + } } @@ -396,27 +478,6 @@ int main(int argc, char **argv){ cout << Cpu::box << Mem::box << Net::box << Proc::box << flush; - if (false) { - Draw::calcSizes(); - cout << Cpu::box << Mem::box << Net::box << Proc::box << flush; - Input::wait(); - exit(0); - } - - // if (true) { - // cout << Term::clear << flush; - // unordered_flat_map<string, string(*)(string)> korvs = { - // {"korv1", korv1}, - // {"korv2", korv2}, - // }; - - // // auto hej = korv1; - - // cout << korvs["korv1"]("hejsan") << endl; - // cout << korvs["korv2"]("hejsan igen") << endl; - // exit(0); - // } - //* Test theme if (false) { string key; @@ -474,7 +535,7 @@ int main(int argc, char **argv){ exit(0); } - + //* Test graphs if (false) { deque<long long> mydata; @@ -524,119 +585,49 @@ int main(int argc, char **argv){ } - if (false) { - cout << Config::getS("log_level") << endl; - - vector<string> vv = {"hej", "vad", "du"}; - vector<int> vy; - - cout << v_contains(vv, "vad"s) << endl; - cout << v_index(vv, "hej"s) << endl; - cout << v_index(vv, "du"s) << endl; - cout << v_index(vv, "kodkod"s) << endl; - cout << v_index(vy, 4) << endl; - - - exit(0); - } - - -//*------>>>>>> Proc testing - - - auto timestamp = time_ms(); - + //* ------------------------------------------------ MAIN LOOP ---------------------------------------------------- - string ostring; - uint64_t tsl, timestamp2, rcount = 0; + uint64_t future_time = time_ms(), rcount = 0; list<uint64_t> avgtimes; - size_t timer = 2000; - vector<string> greyscale; - string filter; - string filter_cur; - string key; - vector<Proc::proc_info> plist; - - int xc; - for (size_t i : iota(0, (int)Term::height - 19)){ - xc = 230 - i * 150 / (Term::height - 20); - greyscale.push_back(Theme::dec_to_color(xc, xc, xc)); - } - while (key != "q") { - timestamp = time_micros(); - tsl = time_ms() + timer; - Config::lock(); - 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(); + try { + while (true) { + if (Global::thread_exception) clean_quit(1); - ostring = Proc::draw(plist); - Config::unlock(); + if (Runner::has_output) { + cout << Term::sync_start << Runner::get_output() << Term::sync_end << flush; - avgtimes.push_front(timestamp); - if (avgtimes.size() > 30) avgtimes.pop_back(); - cout << Term::sync_start << ostring << Fx::reset << Mv::to(2, 2) << '\n'; - 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 << " "; - cout << Mv::to(4, 2) << "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: " << ++rcount << ". Time: " << strf_time("%X ") << Term::sync_end << flush; - - while (time_ms() < tsl and not Global::resized) { - if (Input::poll(tsl - time_ms())) key = Input::get(); - else { key.clear() ; continue; } - if (Config::getB("proc_filtering")) { - if (key == "enter") Config::set("proc_filtering", false); - else if (key == "backspace" and not filter.empty()) filter = uresize(filter, ulen(filter) - 1); - else if (key == "space") filter.push_back(' '); - 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(); - Proc::redraw = true; - break; + //! DEBUG stats + avgtimes.push_front(Runner::time_spent); + if (avgtimes.size() > 30) avgtimes.pop_back(); + cout << Fx::reset << Mv::to(2, 2) << "Runner took: " << rjust(to_string(avgtimes.front()), 5) << " μs. Average: " << + rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() << + " samples. Run count: " << ++rcount << ". " << flush; } - else if (key == "q") break; - else if (key == "left") { - int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting")); - if (--cur_i < 0) cur_i = Proc::sort_vector.size() - 1; - Config::set("proc_sorting", Proc::sort_vector.at(cur_i)); + + if (time_ms() >= future_time) { + Runner::run(); + future_time = time_ms() + Config::getI("update_ms"); } - else if (key == "right") { - int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting")); - if (++cur_i > (int)Proc::sort_vector.size() - 1) cur_i = 0; - Config::set("proc_sorting", Proc::sort_vector.at(cur_i)); + + while (time_ms() < future_time and not Global::resized) { + if (Input::poll(future_time - time_ms())) + Input::process(Input::get()); + else + break; } - else if (key == "f") Config::flip("proc_filtering"); - else if (key == "t") Config::flip("proc_tree"); - 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 (is_in(key, "up", "down", "page_up", "page_down", "home", "end")) { - Proc::selection(key); - cout << Proc::draw(plist) << flush; - continue; + if (Global::resized) { + // cout << Cpu::box << Mem::box << Net::box << Proc::box << flush; + Global::resized = false; + future_time = time_ms(); } - - else continue; - Proc::redraw = true; - break; - } - if (Global::resized) { - cout << Cpu::box << Mem::box << Net::box << Proc::box << flush; - Global::resized = false; } - cout << Mv::to(Term::height - 3, 1) << flush; + } + catch (std::exception& e) { + Global::exit_error_msg = "Exception in main loop -> " + (string)e.what(); + Logger::error(Global::exit_error_msg); + clean_quit(1); } diff --git a/src/btop_config.cpp b/src/btop_config.cpp index 012b0c2..10f86e4 100644 --- a/src/btop_config.cpp +++ b/src/btop_config.cpp @@ -230,7 +230,7 @@ namespace Config { vector<string> valid_boxes = { "cpu", "mem", "net", "proc" }; bool _locked(const string& name){ - atomic_wait(writelock); + writelock.wait(true); if (not write_new and rng::find_if(descriptions, [&name](const auto& a){ return a.at(0) == name; }) != descriptions.end()) write_new = true; return locked.load(); @@ -240,17 +240,19 @@ namespace Config { fs::path conf_dir; fs::path conf_file; + vector<string> current_boxes; + const vector<string> valid_graph_symbols = { "braille", "block", "tty" }; - const bool& getB(string name){ + const bool& getB(const string& name){ return bools.at(name); } - const int& getI(string name){ + const int& getI(const string& name){ return ints.at(name); } - const string& getS(string name){ + const string& getS(const string& name){ return strings.at(name); } @@ -278,13 +280,19 @@ namespace Config { } void lock(){ - atomic_wait_set(locked); + writelock.wait(true); + locked = true; } void unlock(){ if (not locked) return; - atomic_wait(Runner::active); - atomic_wait_set(writelock); + writelock = true; + + if (Proc::shown) { + ints.at("selected_pid") = Proc::selected_pid; + ints.at("proc_start") = Proc::start; + ints.at("proc_selected") = Proc::selected; + } for (auto& item : stringsTmp){ strings.at(item.first) = item.second; @@ -301,20 +309,17 @@ namespace Config { } boolsTmp.clear(); - if (Proc::shown) { - ints.at("selected_pid") = Proc::selected_pid; - ints.at("proc_start") = Proc::start; - ints.at("proc_selected") = Proc::selected; - } - locked = false; writelock = false; + writelock.notify_all(); } bool check_boxes(string boxes){ - for (auto& box : ssplit(boxes)) { + auto new_boxes = ssplit(boxes); + for (auto& box : new_boxes) { if (not v_contains(valid_boxes, box)) return false; } + current_boxes.swap(new_boxes); return true; } @@ -342,10 +347,12 @@ namespace Config { } string name, value; getline(cread, name, '='); + if (name.ends_with(' ')) name = trim(name); if (not v_contains(valid_names, name)) { cread.ignore(SSmax, '\n'); continue; } + cread >> std::ws; if (bools.contains(name)) { cread >> value; @@ -362,7 +369,6 @@ namespace Config { ints.at(name) = stoi(value); } else if (strings.contains(name)) { - cread >> std::ws; if (cread.peek() == '"') { cread.ignore(1); getline(cread, value, '"'); @@ -393,10 +399,14 @@ namespace Config { if (cwrite.good()) { cwrite << "#? Config file for btop v. " << Global::Version; for (auto [name, description] : descriptions) { - cwrite << "\n\n" << (description.empty() ? "" : description + "\n") << name << "="; - if (strings.contains(name)) cwrite << "\"" << strings.at(name) << "\""; - else if (ints.contains(name)) cwrite << ints.at(name); - else if (bools.contains(name)) cwrite << (bools.at(name) ? "True" : "False"); + cwrite << "\n\n" << (description.empty() ? "" : description + "\n") + << name << " = "; + if (strings.contains(name)) + cwrite << "\"" << strings.at(name) << "\""; + else if (ints.contains(name)) + cwrite << ints.at(name); + else if (bools.contains(name)) + cwrite << (bools.at(name) ? "True" : "False"); } cwrite.close(); } diff --git a/src/btop_config.hpp b/src/btop_config.hpp index 4769e30..609bb68 100644 --- a/src/btop_config.hpp +++ b/src/btop_config.hpp @@ -32,17 +32,19 @@ namespace Config { extern const vector<string> valid_graph_symbols; + extern vector<string> current_boxes; + //* Check if string only contains space seperated valid names for boxes bool check_boxes(string boxes); //* Return bool for config key <name> - const bool& getB(string name); + const bool& getB(const string& name); //* Return integer for config key <name> - const int& getI(string name); + const int& getI(const string& name); //* Return string for config key <name> - const string& getS(string name); + const string& getS(const string& name); //* Set config key <name> to bool <value> void set(string name, bool value); diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp index fbaca0e..49745ff 100644 --- a/src/btop_draw.cpp +++ b/src/btop_draw.cpp @@ -275,6 +275,16 @@ namespace Cpu { bool shown = true, redraw = true; string box; + string draw(const cpu_info& cpu, bool force_redraw) { + (void)cpu; + string out; + if (redraw or force_redraw) { + redraw = false; + out += box; + } + return out; + } + } namespace Mem { @@ -285,6 +295,16 @@ namespace Mem { bool shown = true, redraw = true; string box; + string draw(const mem_info& mem, bool force_redraw) { + (void)mem; + string out; + if (redraw or force_redraw) { + redraw = false; + out += box; + } + return out; + } + } namespace Net { @@ -296,6 +316,16 @@ namespace Net { bool shown = true, redraw = true; string box; + string draw(const net_info& net, bool force_redraw) { + (void)net; + string out; + if (redraw or force_redraw) { + redraw = false; + out += box; + } + return out; + } + } namespace Proc { @@ -311,6 +341,7 @@ namespace Proc { void selection(string cmd_key) { auto start = Config::getI("proc_start"); auto selected = Config::getI("proc_selected"); + int numpids = Proc::numpids; if (cmd_key == "up" and selected > 0) { if (start > 0 and selected == 1) start--; else selected--; @@ -340,7 +371,7 @@ namespace Proc { Config::set("proc_selected", selected); } - string draw(vector<proc_info> plist){ + string draw(const vector<proc_info>& plist, bool force_redraw){ auto& filter = Config::getS("proc_filter"); auto& filtering = Config::getB("proc_filtering"); auto& proc_tree = Config::getB("proc_tree"); @@ -352,9 +383,10 @@ namespace Proc { uint64_t total_mem = 16328872 << 10; int y = show_detailed ? Proc::y + 9 : Proc::y; int height = show_detailed ? Proc::height - 9 : Proc::height; + int numpids = Proc::numpids; string out; //* If true, redraw elements not needed to be updated every cycle - if (redraw) { + if (redraw or force_redraw) { redraw = false; out = box; out += Mv::to(y, x) + Mv::r(12) diff --git a/src/btop_input.cpp b/src/btop_input.cpp index aa18b24..d992398 100644 --- a/src/btop_input.cpp +++ b/src/btop_input.cpp @@ -22,8 +22,10 @@ tab-size = 4 #include <btop_input.hpp> #include <btop_tools.hpp> +#include <btop_config.hpp> +#include <btop_shared.hpp> -using std::string, robin_hood::unordered_flat_map, std::cin; +using std::string, robin_hood::unordered_flat_map, std::cin, std::string_literals::operator""s; using namespace Tools; namespace Input { @@ -95,7 +97,7 @@ namespace Input { string wait(){ while (cin.rdbuf()->in_avail() < 1) { - if (interrupt) { interrupt = false; return string(); } + if (interrupt) { interrupt = false; return ""; } sleep_ms(10); } return get(); @@ -105,4 +107,59 @@ namespace Input { last.clear(); } + void process(const string key){ + if (key.empty()) return; + try { + auto& filtering = Config::getB("proc_filtering"); + if (not filtering and key == "q") clean_quit(0); + bool recollect = true; + bool redraw = true; + + //* Input actions for proc + if (Proc::shown) { + bool keep_going = false; + if (filtering) { + string filter = Config::getS("proc_filter"); + if (key == "enter") Config::set("proc_filtering", false); + else if (key == "backspace" and not filter.empty()) filter = uresize(filter, ulen(filter) - 1); + else if (key == "space") filter.push_back(' '); + else if (ulen(key) == 1) filter.append(key); + else return; + Config::set("proc_filter", filter); + } + else if (key == "left") { + int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting")); + if (--cur_i < 0) cur_i = Proc::sort_vector.size() - 1; + Config::set("proc_sorting", Proc::sort_vector.at(cur_i)); + } + else if (key == "right") { + int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting")); + if (++cur_i > (int)Proc::sort_vector.size() - 1) cur_i = 0; + Config::set("proc_sorting", Proc::sort_vector.at(cur_i)); + } + else if (key == "f") { Config::flip("proc_filtering"); recollect = false; } + else if (key == "t") Config::flip("proc_tree"); + else if (key == "r") Config::flip("proc_reversed"); + else if (key == "c") Config::flip("proc_per_core"); + else if (key == "delete" and not Config::getS("proc_filter").empty()) Config::set("proc_filter", ""s); + else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end")) { + Proc::selection(key); + recollect = false; + redraw = false; + } + else keep_going = true; + + if (not keep_going) { + Runner::run("proc", not recollect, redraw); + return; + } + } + } + + + catch (const std::exception& e) { + throw std::runtime_error("Input::process(\"" + key + "\") : " + (string)e.what()); + } + } + }
\ No newline at end of file diff --git a/src/btop_input.hpp b/src/btop_input.hpp index adb4317..11ca095 100644 --- a/src/btop_input.hpp +++ b/src/btop_input.hpp @@ -47,4 +47,7 @@ namespace Input { //* Clears last entered key void clear(); + //* Process actions for input <key> + void process(const std::string key); + }
\ No newline at end of file diff --git a/src/btop_linux.cpp b/src/btop_linux.cpp index 8fe4869..e35e5aa 100644 --- a/src/btop_linux.cpp +++ b/src/btop_linux.cpp @@ -93,10 +93,34 @@ namespace Shared { namespace Cpu { bool got_sensors = false; string cpuName = ""; + + cpu_info current_cpu; + + cpu_info& collect(const bool return_last) { + (void)return_last; + return current_cpu; + } } namespace Mem { bool has_swap = false; + + mem_info current_mem; + + mem_info& collect(const bool return_last) { + (void)return_last; + return current_mem; + } + +} + +namespace Net { + net_info current_net; + + net_info& collect(const bool return_last) { + (void)return_last; + return current_net; + } } namespace Proc { @@ -117,11 +141,9 @@ namespace Proc { int counter = 0; } uint64_t old_cputimes = 0; - int numpids = 0; + atomic<int> numpids = 0; size_t reserve_pids = 500; bool tree_state = false; - atomic<bool> stop (false); - atomic<bool> collecting (false); vector<string> sort_vector = { "pid", "name", @@ -150,6 +172,7 @@ namespace Proc { //* Generate process tree list void _tree_gen(const proc_info& cur_proc, const vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false){ + if (Runner::stopping) return; auto cur_pos = out_procs.size(); bool filtering = false; @@ -290,9 +313,8 @@ namespace Proc { } //* Collects and sorts process information from /proc - vector<proc_info>& collect(bool return_last){ + vector<proc_info>& collect(const 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"); auto& filter = Config::getS("proc_filter"); @@ -345,12 +367,9 @@ namespace Proc { //* Iterate over all pids in /proc for (auto& d: fs::directory_iterator(Shared::proc_path)){ - if (pread.is_open()) pread.close(); - if (stop) { - collecting = false; - stop = false; + if (Runner::stopping) return current_procs; - } + if (pread.is_open()) pread.close(); string pid_str = d.path().filename(); if (not isdigit(pid_str[0])) continue; @@ -554,6 +573,8 @@ namespace Proc { for (auto& p : rng::equal_range(procs, procs.at(0).ppid, rng::less{}, &proc_info::ppid)) { _tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, filter); } + + if (Runner::stopping) return current_procs; procs.swap(tree_procs); } @@ -574,7 +595,6 @@ namespace Proc { numpids = (int)procs.size(); current_procs.swap(procs); reserve_pids = npids; - collecting = false; return current_procs; } } diff --git a/src/btop_shared.hpp b/src/btop_shared.hpp index 4a7e86b..61627b0 100644 --- a/src/btop_shared.hpp +++ b/src/btop_shared.hpp @@ -24,11 +24,16 @@ tab-size = 4 #include <atomic> #include <deque> #include <robin_hood.h> +#include <array> -using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic; +using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic, std::array; + +void clean_quit(int sig=-1); namespace Global { extern const string Version; + extern string exit_error_msg; + extern atomic<bool> thread_exception; extern int coreCount; extern string banner; } @@ -36,7 +41,10 @@ namespace Global { namespace Runner { extern atomic<bool> active; - extern atomic<bool> stop; + extern atomic<bool> stopping; + + void run(const string box="", const bool no_update=false, const bool force_redraw=false, const bool input_interrupt=true); + void stop(); } @@ -53,22 +61,65 @@ namespace Shared { namespace Cpu { extern string box, cpuName; extern bool shown, redraw, got_sensors; + + struct cpu_info { + vector<deque<long long>> percent; + vector<deque<long long>> temp; + array<float, 3> load_avg; + }; + + //* Collect cpu stats and temperatures + cpu_info& collect(const bool return_last=false); + + //* Draw contents of cpu box using <cpu> as source + string draw(const cpu_info& cpu, bool force_redraw=false); } namespace Mem { extern string box; extern bool has_swap, shown, redraw; + + struct disk_info { + uint64_t total = 0, used = 0; + }; + + struct mem_info { + uint64_t total = 0, available = 0, cached = 0, free = 0; + unordered_flat_map<string, deque<long long>> percent; + unordered_flat_map<string, deque<long long>> disks_io; + unordered_flat_map<string, disk_info> disks; + }; + + //* Collect mem & disks stats + mem_info& collect(const bool return_last=false); + + //* Draw contents of mem box using <mem> as source + string draw(const mem_info& mem, bool force_redraw=false); } namespace Net { extern string box; extern bool shown, redraw; + + struct net_stat { + uint64_t speed = 0, top = 0, total = 0; + }; + + struct net_info { + unordered_flat_map<string, deque<long long>> bandwidth; + unordered_flat_map<string, net_stat> stat; + string ip_addr; + }; + + //* Collect net upload/download stats + net_info& collect(const bool return_last=false); + + //* Draw contents of net box using <net> as source + string draw(const net_info& net, bool force_redraw=false); } namespace Proc { - extern int numpids; - extern atomic<bool> stop; - extern atomic<bool> collecting; + extern atomic<int> numpids; extern string box; extern bool shown, redraw; @@ -106,12 +157,12 @@ namespace Proc { extern detail_container detailed; - //* Collect and sort process information from /proc, saves and returns reference to Proc::current_procs; - vector<proc_info>& collect(bool return_last=false); + //* Collect and sort process information from /proc + vector<proc_info>& collect(const bool return_last=false); //* Update current selection and view void selection(string cmd_key); //* Draw contents of proc box using <plist> as data source - string draw(vector<proc_info> plist); + string draw(const vector<proc_info>& plist, bool force_redraw=false); } diff --git a/src/btop_tools.cpp b/src/btop_tools.cpp index a58a396..cce168c 100644 --- a/src/btop_tools.cpp +++ b/src/btop_tools.cpp @@ -329,7 +329,8 @@ namespace Logger { void log_write(const size_t level, const string& msg){ if (loglevel < level or logfile.empty()) return; - atomic_wait_set(busy, true); + busy.wait(true); + busy = true; std::error_code ec; if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) { auto old_log = logfile; @@ -345,5 +346,6 @@ namespace Logger { } else logfile.clear(); busy = false; + busy.notify_one(); } } diff --git a/src/btop_tools.hpp b/src/btop_tools.hpp index fdb4417..bd93681 100644 --- a/src/btop_tools.hpp +++ b/src/btop_tools.hpp @@ -267,25 +267,6 @@ namespace Tools { //* Return current time in <strf> format string strf_time(const string& strf); - //* Waits for <atom> to not be <val> - #if (__GNUC__ > 10) - //* Redirects to atomic wait - inline void atomic_wait(const atomic<bool>& atom, bool val=true){ - atom.wait(val); - } - #else - //* Crude implementation of atomic wait for GCC 10 - inline void atomic_wait(const atomic<bool>& atom, bool val=true){ - while (atom == val) std::this_thread::sleep_for(std::chrono::microseconds(1)); - } - #endif - - //* Waits for <atom> to not be <val> and then sets it to <val> again - inline void atomic_wait_set(std::atomic<bool>& atom, bool val=true){ - atomic_wait(atom, val); - atom = val; - }; - } //* Simple logging implementation |