From ced3d47ebed6931c72a88e74f0a2739d5e83e723 Mon Sep 17 00:00:00 2001 From: aristocratos Date: Wed, 11 Aug 2021 23:21:33 +0200 Subject: [PATCH] Removed bad goto and added 100 microseconds sleep in thread manager loop to spare some cpu cycles --- src/btop.cpp | 32 ++-- src/btop_draw.cpp | 10 +- src/btop_linux.cpp | 362 +++++++++++++++++++++++---------------------- src/btop_tools.hpp | 3 + 4 files changed, 209 insertions(+), 198 deletions(-) diff --git a/src/btop.cpp b/src/btop.cpp index 7f5ca6e..db44d50 100644 --- a/src/btop.cpp +++ b/src/btop.cpp @@ -347,10 +347,10 @@ namespace Runner { //* Start collection functions for all boxes in async threads and draw in this thread when finished //? Starting order below based on mean time to finish - while (box_mask.count() > 0) { - if (stopping) break; - try { - //* PROC + try { + while (box_mask.count() > 0) { + if (stopping) break; + //? PROC if (box_mask.test(proc_present)) { if (not box_mask.test(proc_running)) { proc = async(Proc::collect, conf->no_update); @@ -367,9 +367,10 @@ namespace Runner { throw std::runtime_error("Proc:: -> " + (string)e.what()); } box_mask ^= proc_done; + continue; } } - //* MEM + //? MEM if (box_mask.test(mem_present)) { if (not box_mask.test(mem_running)) { mem = async(Mem::collect, conf->no_update); @@ -386,9 +387,10 @@ namespace Runner { throw std::runtime_error("Mem:: -> " + (string)e.what()); } box_mask ^= mem_done; + continue; } } - //* NET + //? NET if (box_mask.test(net_present)) { if (not box_mask.test(net_running)) { net = async(Net::collect, conf->no_update); @@ -405,9 +407,10 @@ namespace Runner { throw std::runtime_error("Net:: -> " + (string)e.what()); } box_mask ^= net_done; + continue; } } - //* CPU + //? CPU if (box_mask.test(cpu_present)) { if (not box_mask.test(cpu_running)) { cpu = async(Cpu::collect, conf->no_update); @@ -424,16 +427,17 @@ namespace Runner { throw std::runtime_error("Cpu:: -> " + (string)e.what()); } box_mask ^= cpu_done; + continue; } } + sleep_micros(100); } - catch (const std::exception& e) { - Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what(); - Global::thread_exception = true; - Input::interrupt = true; - stopping = true; - break; - } + } + catch (const std::exception& e) { + Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what(); + Global::thread_exception = true; + Input::interrupt = true; + stopping = true; } if (stopping) { diff --git a/src/btop_draw.cpp b/src/btop_draw.cpp index 8f6c71e..0700842 100644 --- a/src/btop_draw.cpp +++ b/src/btop_draw.cpp @@ -962,8 +962,9 @@ namespace Proc { if (item_fit >= 4) out += cjust("IO/W:", item_width); if (item_fit >= 5) out += cjust("Parent:", item_width); if (item_fit >= 6) out += cjust("User:", item_width); - if (item_fit >= 7) out += cjust("Nice:", item_width); - if (item_fit >= 8) out += cjust("Threads:", item_width); + if (item_fit >= 7) out += cjust("Threads:", item_width); + if (item_fit >= 8) out += cjust("Nice:", item_width); + //? Command line for (int i = 0; const auto& l : {'C', 'M', 'D'}) @@ -1084,8 +1085,9 @@ namespace Proc { if (item_fit >= 4) out += cjust(detailed.io_write, item_width); if (item_fit >= 5) out += cjust(detailed.parent, item_width, true); if (item_fit >= 6) out += cjust(detailed.entry.user, item_width, true); - if (item_fit >= 7) out += cjust(to_string(detailed.entry.p_nice), item_width); - if (item_fit >= 8) out += cjust(to_string(detailed.entry.threads), item_width); + if (item_fit >= 7) out += cjust(to_string(detailed.entry.threads), item_width); + if (item_fit >= 8) out += cjust(to_string(detailed.entry.p_nice), item_width); + const double mem_p = (double)detailed.mem_bytes.back() * 100 / Shared::totalMem; string mem_str = to_string(mem_p); diff --git a/src/btop_linux.cpp b/src/btop_linux.cpp index 916b20d..6805e1f 100644 --- a/src/btop_linux.cpp +++ b/src/btop_linux.cpp @@ -1000,214 +1000,216 @@ namespace Proc { procs.reserve(current_procs.size() + 10); const int cmult = (per_core) ? Shared::coreCount : 1; bool got_detailed = false; + + //* Use pids from last update if only changing filter, sorting or tree options if (no_update and not cache.empty()) { procs = current_procs; if (show_detailed and detailed_pid != detailed.last_pid) _collect_details(detailed_pid, round(uptime), procs); - goto proc_no_update; } - - //? 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) != passwd_time) { - string r_uid, r_user; - passwd_time = fs::last_write_time(Shared::passwd_path); - uid_user.clear(); - pread.open(Shared::passwd_path); - if (pread.good()) { - while (not pread.eof()) { - getline(pread, r_user, ':'); - pread.ignore(SSmax, ':'); - getline(pread, r_uid, ':'); - uid_user[r_uid] = r_user; - pread.ignore(SSmax, '\n'); - } - } - else { - Shared::passwd_path.clear(); - } - pread.close(); - } - - //* Get cpu total times from /proc/stat - cputimes = 0; - pread.open(Shared::procPath / "stat"); - if (pread.good()) { - pread.ignore(SSmax, ' '); - for (uint64_t times; pread >> times; cputimes += times); - pread.close(); - } - else throw std::runtime_error("Failure to read /proc/stat"); - - //* Iterate over all pids in /proc - for (const auto& d: fs::directory_iterator(Shared::procPath)) { - if (Runner::stopping) - return procs; - if (pread.is_open()) pread.close(); - - const string pid_str = d.path().filename(); - if (not isdigit(pid_str[0])) continue; - - proc_info new_proc (stoul(pid_str)); - - //* Cache program name, command and username - if (not cache.contains(new_proc.pid)) { - string name, cmd, user; - pread.open(d.path() / "comm"); - if (not pread.good()) continue; - getline(pread, name); - pread.close(); - size_t name_offset = rng::count(name, ' '); - - pread.open(d.path() / "cmdline"); - if (not pread.good()) continue; - long_string.clear(); - while(getline(pread, long_string, '\0')) cmd += long_string + ' '; - pread.close(); - if (not cmd.empty()) cmd.pop_back(); - - pread.open(d.path() / "status"); - if (not pread.good()) continue; - string uid; - string line; - while (not pread.eof()) { - getline(pread, line, ':'); - if (line == "Uid") { - pread.ignore(); - getline(pread, uid, '\t'); - break; - } else { + //* ---------------------------------------------Collection start---------------------------------------------- + else { + //? 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) != passwd_time) { + string r_uid, r_user; + passwd_time = fs::last_write_time(Shared::passwd_path); + uid_user.clear(); + pread.open(Shared::passwd_path); + if (pread.good()) { + while (not pread.eof()) { + getline(pread, r_user, ':'); + pread.ignore(SSmax, ':'); + getline(pread, r_uid, ':'); + uid_user[r_uid] = r_user; pread.ignore(SSmax, '\n'); } } + else { + Shared::passwd_path.clear(); + } pread.close(); - user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid; - - cache[new_proc.pid] = {name, cmd, user, name_offset}; } - new_proc.name = cache.at(new_proc.pid).name; - new_proc.cmd = cache.at(new_proc.pid).cmd; - new_proc.user = cache.at(new_proc.pid).user; + //? Get cpu total times from /proc/stat + cputimes = 0; + pread.open(Shared::procPath / "stat"); + if (pread.good()) { + pread.ignore(SSmax, ' '); + for (uint64_t times; pread >> times; cputimes += times); + pread.close(); + } + else throw std::runtime_error("Failure to read /proc/stat"); - //* Parse /proc/[pid]/stat - pread.open(d.path() / "stat"); - if (not pread.good()) continue; + //? Iterate over all pids in /proc + for (const auto& d: fs::directory_iterator(Shared::procPath)) { + if (Runner::stopping) + return procs; + if (pread.is_open()) pread.close(); - //? Check cached value for whitespace characters in name and set offset to get correct fields from stat file - size_t& offset = cache.at(new_proc.pid).name_offset; - short_str.clear(); - size_t x = 0, next_x = 3; - uint64_t cpu_t = 0; - try { - for (;;) { - while (pread.good() and ++x - offset < next_x) { - pread.ignore(SSmax, ' '); - } - if (pread.bad()) goto stat_loop_done; + const string pid_str = d.path().filename(); + if (not isdigit(pid_str[0])) continue; - getline(pread, short_str, ' '); + proc_info new_proc (stoul(pid_str)); - switch (x-offset) { - case 3: { //? Process state - new_proc.state = short_str.at(0); - continue; - } - case 4: { //? Parent pid - new_proc.ppid = stoull(short_str); - next_x = 14; - continue; - } - case 14: { //? Process utime - cpu_t = stoull(short_str); - continue; - } - case 15: { //? Process stime - cpu_t += stoull(short_str); - next_x = 19; - continue; - } - case 19: { //? Nice value - new_proc.p_nice = stoull(short_str); - continue; - } - case 20: { //? Number of threads - new_proc.threads = stoull(short_str); - if (cache.at(new_proc.pid).cpu_s == 0) { - next_x = 22; - cache.at(new_proc.pid).cpu_t = cpu_t; - } - else - next_x = 24; - continue; - } - case 22: { //? Save cpu seconds to cache if missing - cache.at(new_proc.pid).cpu_s = stoull(short_str); - next_x = 24; - continue; - } - case 24: { //? RSS memory (can be inaccurate, but parsing smaps increases total cpu usage by ~20x) - new_proc.mem = stoull(short_str) * Shared::pageSize; - next_x = 39; - continue; - } - case 39: { //? CPU number last executed on - new_proc.cpu_n = stoull(short_str); - goto stat_loop_done; + //? Cache program name, command and username + if (not cache.contains(new_proc.pid)) { + string name, cmd, user; + pread.open(d.path() / "comm"); + if (not pread.good()) continue; + getline(pread, name); + pread.close(); + size_t name_offset = rng::count(name, ' '); + + pread.open(d.path() / "cmdline"); + if (not pread.good()) continue; + long_string.clear(); + while(getline(pread, long_string, '\0')) cmd += long_string + ' '; + pread.close(); + if (not cmd.empty()) cmd.pop_back(); + + pread.open(d.path() / "status"); + if (not pread.good()) continue; + string uid; + string line; + while (not pread.eof()) { + getline(pread, line, ':'); + if (line == "Uid") { + pread.ignore(); + getline(pread, uid, '\t'); + break; + } else { + pread.ignore(SSmax, '\n'); } } + pread.close(); + user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid; + + cache[new_proc.pid] = {name, cmd, user, name_offset}; } - } - catch (const std::invalid_argument&) { continue; } - catch (const std::out_of_range&) { continue; } + new_proc.name = cache.at(new_proc.pid).name; + new_proc.cmd = cache.at(new_proc.pid).cmd; + new_proc.user = cache.at(new_proc.pid).user; - stat_loop_done: - pread.close(); + //? Parse /proc/[pid]/stat + pread.open(d.path() / "stat"); + if (not pread.good()) continue; - if (x-offset < 24) continue; + //? Check cached value for whitespace characters in name and set offset to get correct fields from stat file + size_t& offset = cache.at(new_proc.pid).name_offset; + short_str.clear(); + size_t x = 0, next_x = 3; + uint64_t cpu_t = 0; + try { + for (;;) { + while (pread.good() and ++x - offset < next_x) { + pread.ignore(SSmax, ' '); + } + if (pread.bad()) goto stat_loop_done; - //? Process cpu usage since last update - new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache.at(new_proc.pid).cpu_t) / max(1ul, cputimes - old_cputimes)) / 10.0; + getline(pread, short_str, ' '); - //? Process cumulative cpu usage since process start - new_proc.cpu_c = (double)cpu_t / max(1.0, (uptime * Shared::clkTck) - cache.at(new_proc.pid).cpu_s); + switch (x-offset) { + case 3: { //? Process state + new_proc.state = short_str.at(0); + continue; + } + case 4: { //? Parent pid + new_proc.ppid = stoull(short_str); + next_x = 14; + continue; + } + case 14: { //? Process utime + cpu_t = stoull(short_str); + continue; + } + case 15: { //? Process stime + cpu_t += stoull(short_str); + next_x = 19; + continue; + } + case 19: { //? Nice value + new_proc.p_nice = stoull(short_str); + continue; + } + case 20: { //? Number of threads + new_proc.threads = stoull(short_str); + if (cache.at(new_proc.pid).cpu_s == 0) { + next_x = 22; + cache.at(new_proc.pid).cpu_t = cpu_t; + } + else + next_x = 24; + continue; + } + case 22: { //? Save cpu seconds to cache if missing + cache.at(new_proc.pid).cpu_s = stoull(short_str); + next_x = 24; + continue; + } + case 24: { //? RSS memory (can be inaccurate, but parsing smaps increases total cpu usage by ~20x) + new_proc.mem = stoull(short_str) * Shared::pageSize; + next_x = 39; + continue; + } + case 39: { //? CPU number last executed on + new_proc.cpu_n = stoull(short_str); + goto stat_loop_done; + } + } + } - //? Update cache with latest cpu times - cache.at(new_proc.pid).cpu_t = cpu_t; + } + catch (const std::invalid_argument&) { continue; } + catch (const std::out_of_range&) { continue; } + + stat_loop_done: + pread.close(); + + if (x-offset < 24) continue; + + //? Process cpu usage since last update + new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache.at(new_proc.pid).cpu_t) / max(1ul, cputimes - old_cputimes)) / 10.0; + + //? Process cumulative cpu usage since process start + new_proc.cpu_c = (double)cpu_t / max(1.0, (uptime * Shared::clkTck) - cache.at(new_proc.pid).cpu_s); + + //? Update cache with latest cpu times + cache.at(new_proc.pid).cpu_t = cpu_t; + + if (show_detailed and not got_detailed and new_proc.pid == detailed_pid) { + got_detailed = true; + } + + //? Push process to vector + procs.push_back(new_proc); - if (show_detailed and not got_detailed and new_proc.pid == detailed_pid) { - got_detailed = true; } - //? Push process to vector - procs.push_back(new_proc); + //? Clear dead processes from cache at a regular interval + if (++counter >= 1000 or (cache.size() > procs.size() + 100)) { + counter = 0; + unordered_flat_map r_cache; + r_cache.reserve(procs.size()); + rng::for_each(procs, [&r_cache](const auto &p) { + if (cache.contains(p.pid)) + r_cache[p.pid] = cache.at(p.pid); + }); + cache = std::move(r_cache); + } + //? Update the details info box for process if active + if (show_detailed and got_detailed) { + _collect_details(detailed_pid, round(uptime), procs); + } + else if (show_detailed and not got_detailed and detailed.status != "Dead") { + detailed.status = "Dead"; + redraw = true; + } + + old_cputimes = cputimes; + current_procs = procs; } - - //* Clear dead processes from cache at a regular interval - if (++counter >= 1000 or (cache.size() > procs.size() + 100)) { - counter = 0; - unordered_flat_map r_cache; - r_cache.reserve(procs.size()); - rng::for_each(procs, [&r_cache](const auto &p) { - if (cache.contains(p.pid)) - r_cache[p.pid] = cache.at(p.pid); - }); - cache = std::move(r_cache); - } - - //* Update the details info box for process if active - if (show_detailed and got_detailed) { - _collect_details(detailed_pid, round(uptime), procs); - } - else if (show_detailed and not got_detailed and detailed.status != "Dead") { - detailed.status = "Dead"; - redraw = true; - } - - old_cputimes = cputimes; - current_procs = procs; - - proc_no_update: + //* ---------------------------------------------Collection done----------------------------------------------- //* Match filter if defined if (not tree and not filter.empty()) { diff --git a/src/btop_tools.hpp b/src/btop_tools.hpp index e9d7f50..05e3ee0 100644 --- a/src/btop_tools.hpp +++ b/src/btop_tools.hpp @@ -229,6 +229,9 @@ namespace Tools { //* Put current thread to sleep for milliseconds inline void sleep_ms(const size_t& ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); } + //* Put current thread to sleep for microseconds + inline void sleep_micros(const size_t& micros) { std::this_thread::sleep_for(std::chrono::microseconds(micros)); } + //* Left justify string if is greater than length, limit return size to by default string ljust(string str, const size_t x, const bool utf=false, const bool wide=false, const bool limit=true);