Added Meter class

This commit is contained in:
aristocratos 2021-05-27 22:29:36 +02:00
parent e7cbc28960
commit ecd4ef9985
7 changed files with 2627 additions and 39 deletions

View File

@ -4,7 +4,7 @@ CPP = g++
CPPFLAGS = -std=c++20 -pthread CPPFLAGS = -std=c++20 -pthread
OPTFLAG = -O3 OPTFLAG = -O3
INFOFLAGS += -Wall -Wextra -Wno-stringop-overread -pedantic INFOFLAGS += -Wall -Wextra -Wno-stringop-overread -pedantic
INCLUDES = -I./src INCLUDES = -I./src -I./include
btop: btop.cpp btop: btop.cpp
@mkdir -p bin @mkdir -p bin

View File

@ -238,7 +238,7 @@ int main(int argc, char **argv){
int ill = 0; int ill = 0;
for (int i : iota(0, (int)blen)){ for (int i : iota(0, (int)blen)){
ill = (i <= (int)blen / 2) ? i : ill - 1; ill = (i <= (int)blen / 2) ? i : ill - 1;
cout << Theme::g("used")[ill] << "-"; cout << Theme::g("used")[ill] << Symbols::h_line;
} }
cout << Fx::reset << endl; cout << Fx::reset << endl;
@ -274,6 +274,18 @@ int main(int argc, char **argv){
} }
if (true) {
Draw::Meter kmeter;
kmeter(Term::width - 2, "cpu", false);
cout << kmeter(25) << endl;
cout << kmeter(0) << endl;
cout << kmeter(50) << endl;
cout << kmeter(100) << endl;
cout << kmeter(50) << endl;
exit(0);
}
if (thread_test){ if (thread_test){
map<int, future<string>> runners; map<int, future<string>> runners;
@ -329,15 +341,15 @@ int main(int argc, char **argv){
greyscale.push_back(Theme::dec_to_color(xc, xc, xc)); greyscale.push_back(Theme::dec_to_color(xc, xc, xc));
} }
string pbox = Draw::createBox({.x = 0, .y = 10, .width = Term::width, .height = Term::height - 16, .line_color = Theme::c("proc_box"), .title = "testbox", .title2 = "below", .num = 7}); string pbox = Draw::createBox({.x = 0, .y = 10, .width = Term::width, .height = Term::height - 16, .line_color = Theme::c("proc_box"), .title = "testbox", .title2 = "below", .fill = false, .num = 7});
pbox += rjust("Pid:", 8) + " " + ljust("Program:", 16) + " " + ljust("Command:", Term::width - 69) + " Threads: " + pbox += rjust("Pid:", 8) + " " + ljust("Program:", 16) + " " + ljust("Command:", Term::width - 69) + " Threads: " +
ljust("User:", 10) + " " + rjust("MemB", 5) + " " + rjust("Cpu%", 14) + "\n"; ljust("User:", 10) + " " + rjust("MemB", 5) + " " + rjust("Cpu%", 14) + "\n";
while (key != "q") { while (key != "q") {
timestamp = time_ms(); timestamp = time_micros();
tsl = timestamp + timer; tsl = time_ms() + timer;
auto plist = Proc::collect(Proc::sort_array[sortint], reversing, filter); auto plist = Proc::collect(Proc::sort_array[sortint], reversing, filter);
timestamp2 = time_ms(); timestamp2 = time_micros();
timestamp = timestamp2 - timestamp; timestamp = timestamp2 - timestamp;
ostring.clear(); ostring.clear();
lc = 0; lc = 0;
@ -353,12 +365,16 @@ int main(int argc, char **argv){
if (lc++ > Term::height - 21) break; if (lc++ > Term::height - 21) break;
} }
while (lc++ < Term::height - 19) ostring += Mv::r(1) + string(Term::width - 2, ' ') + "\n";
avgtimes.push_front(timestamp); avgtimes.push_front(timestamp);
if (avgtimes.size() > 100) avgtimes.pop_back(); if (avgtimes.size() > 100) avgtimes.pop_back();
cout << pbox << ostring << Fx::reset << "\n" << endl; cout << pbox << ostring << Fx::reset << "\n" << endl;
cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 4) << "ms. Average: " << 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()), 3) << "ms of " << avgtimes.size() << rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() <<
" samples. Drawing took: " << time_ms() - timestamp2 << "ms.\nNumber of processes: " << Proc::numpids << ". Run count: " << " samples. Drawing took: " << time_micros() - timestamp2 << " μs.\nNumber of processes: " << Proc::numpids << ". Run count: " <<
++rcount << ". Time: " << strf_time("%X ") << endl; ++rcount << ". Time: " << strf_time("%X ") << endl;
while (time_ms() < tsl) { while (time_ms() < tsl) {

2530
include/robin_hood.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,8 @@ tab-size = 4
#include <vector> #include <vector>
#include <map> #include <map>
#include <ranges> #include <ranges>
#include <algorithm>
#include <cmath>
#include <btop_config.h> #include <btop_config.h>
#include <btop_tools.h> #include <btop_tools.h>
@ -29,7 +31,7 @@ tab-size = 4
#ifndef _btop_draw_included_ #ifndef _btop_draw_included_
#define _btop_draw_included_ 1 #define _btop_draw_included_ 1
using std::string, std::vector, std::map, std::round, std::views::iota; using std::string, std::vector, std::map, std::round, std::views::iota, std::string_literals::operator""s;
namespace Draw { namespace Draw {
@ -80,8 +82,42 @@ namespace Draw {
} }
class Meter { class Meter {
string out, color_gradient, color_inactive; string out, color_gradient;
int width = 10;
bool invert = false;
vector<string> cache;
public:
void operator()(int width, string color_gradient, bool invert = false) {
if (width < 0) width = 1;
this->width = width;
this->color_gradient = color_gradient;
this->invert = invert;
cache.clear();
cache.insert(cache.begin(), 101, "");
}
string operator()(int value) {
if (value > 100) value = 100;
else if (value < 0) value = 0;
if (!cache.at(value).empty()) return out = cache.at(value);
out.clear();
int y;
for (int i : iota(1, width + 1)) {
y = round((double)i * 100.0 / width);
if (value >= y)
out += Theme::g(color_gradient)[invert ? 100 - y : y] + Symbols::meter;
else {
out += Theme::c("meter_bg") + Symbols::meter * (width + 1 - i);
break;
}
}
out += Fx::reset;
return cache.at(value) = out;
}
string operator()() {
return out;
}
}; };

View File

@ -23,8 +23,6 @@ tab-size = 4
#include <vector> #include <vector>
#include <deque> #include <deque>
#include <array> #include <array>
#include <map>
#include <unordered_map>
#include <atomic> #include <atomic>
#include <future> #include <future>
#include <thread> #include <thread>
@ -39,9 +37,10 @@ tab-size = 4
#include <btop_config.h> #include <btop_config.h>
#include <btop_globs.h> #include <btop_globs.h>
#include <btop_tools.h> #include <btop_tools.h>
#include <robin_hood.h>
using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, std::map, std::unordered_map, std::deque, std::list; using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, robin_hood::unordered_flat_map;
using std::cout, std::flush, std::endl; using std::cout, std::flush, std::endl;
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace Tools; using namespace Tools;
@ -72,8 +71,8 @@ namespace Proc {
string name, cmd, user; string name, cmd, user;
uint64_t cpu_t = 0, cpu_s = 0; uint64_t cpu_t = 0, cpu_s = 0;
}; };
unordered_map<uint, p_cache> cache; unordered_flat_map<uint, p_cache> cache;
unordered_map<string, string> uid_user; unordered_flat_map<string, string> uid_user;
fs::path passwd_path; fs::path passwd_path;
fs::file_time_type passwd_time; fs::file_time_type passwd_time;
uint counter = 0; uint counter = 0;
@ -95,7 +94,7 @@ namespace Proc {
"cpu direct", "cpu direct",
"cpu lazy", "cpu lazy",
}; };
unordered_map<string, uint> sort_map; unordered_flat_map<string, uint> sort_map;
//* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c, state, cpu_n, p_nice, ppid //* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c, state, cpu_n, p_nice, ppid
struct proc_info { struct proc_info {
@ -138,13 +137,12 @@ namespace Proc {
uid_user.clear(); uid_user.clear();
pread.open(passwd_path); pread.open(passwd_path);
if (pread.good()) { if (pread.good()) {
while (true){ while (!pread.eof()){
getline(pread, r_user, ':'); getline(pread, r_user, ':');
pread.ignore(SSmax, ':'); pread.ignore(SSmax, ':');
getline(pread, r_uid, ':'); getline(pread, r_uid, ':');
uid_user[r_uid] = r_user; uid_user[r_uid] = r_user;
pread.ignore(SSmax, '\n'); pread.ignore(SSmax, '\n');
if (pread.eof()) break;
} }
} }
pread.close(); pread.close();
@ -180,7 +178,6 @@ namespace Proc {
pread.open(d.path() / "cmdline"); pread.open(d.path() / "cmdline");
if (pread.good()) { if (pread.good()) {
tmpstr.clear();
while(getline(pread, tmpstr, '\0')) cmd += tmpstr + " "; while(getline(pread, tmpstr, '\0')) cmd += tmpstr + " ";
pread.close(); pread.close();
if (!cmd.empty()) cmd.pop_back(); if (!cmd.empty()) cmd.pop_back();
@ -189,7 +186,7 @@ namespace Proc {
pread.open(d.path() / "status"); pread.open(d.path() / "status");
if (pread.good()) { if (pread.good()) {
status.clear(); uid.clear(); uid.clear();
while (!pread.eof()){ while (!pread.eof()){
getline(pread, status, ':'); getline(pread, status, ':');
if (status == "Uid") { if (status == "Uid") {
@ -208,10 +205,11 @@ namespace Proc {
} }
//* Match filter if defined //* Match filter if defined
if (!filter.empty() && pid_str.find(filter) == string::npos && if (!filter.empty()
cache[pid].name.find(filter) == string::npos && && pid_str.find(filter) == string::npos
cache[pid].cmd.find(filter) == string::npos && && cache[pid].name.find(filter) == string::npos
cache[pid].user.find(filter) == string::npos) { && cache[pid].cmd.find(filter) == string::npos
&& cache[pid].user.find(filter) == string::npos) {
if (new_cache) cache.erase(pid); if (new_cache) cache.erase(pid);
continue; continue;
} }
@ -219,9 +217,9 @@ namespace Proc {
//* Parse /proc/[pid]/stat //* Parse /proc/[pid]/stat
pread.open(d.path() / "stat"); pread.open(d.path() / "stat");
if (pread.good()) { if (pread.good()) {
instr.clear(); s_pos = 0; c_pos = 0; s_count = 0;
getline(pread, instr); getline(pread, instr);
pread.close(); pread.close();
s_pos = 0; c_pos = 0; s_count = 0;
//? Skip pid and comm field and find comm fields closing ')' //? Skip pid and comm field and find comm fields closing ')'
s_pos = instr.find_last_of(')') + 2; s_pos = instr.find_last_of(')') + 2;
@ -289,12 +287,12 @@ namespace Proc {
if (pread.good()) { if (pread.good()) {
pread.ignore(SSmax, ' '); pread.ignore(SSmax, ' ');
pread >> rss_mem; pread >> rss_mem;
rss_mem *= page_size;
pread.close(); pread.close();
rss_mem *= page_size;
} }
//* Create proc_info //* Create proc_info
procs.push_back({pid, cache[pid].name, cache[pid].cmd, threads, cache[pid].user, rss_mem, cpu, cpu_s, state, cpu_n, p_nice, ppid}); procs.emplace_back(pid, cache[pid].name, cache[pid].cmd, threads, cache[pid].user, rss_mem, cpu, cpu_s, state, cpu_n, p_nice, ppid);
} }
} }
@ -302,14 +300,14 @@ namespace Proc {
//* Sort processes vector //* Sort processes vector
std::ranges::sort(procs, [&sortint, &reverse](proc_info& a, proc_info& b) { std::ranges::sort(procs, [&sortint, &reverse](proc_info& a, proc_info& b) {
switch (sortint) { switch (sortint) {
case 0: return (reverse) ? a.pid < b.pid : a.pid > b.pid; case 0: return (reverse) ? a.pid < b.pid : a.pid > b.pid;
case 1: return (reverse) ? a.name < b.name : a.name > b.name; case 1: return (reverse) ? a.name < b.name : a.name > b.name;
case 2: return (reverse) ? a.cmd < b.cmd : a.cmd > b.cmd; case 2: return (reverse) ? a.cmd < b.cmd : a.cmd > b.cmd;
case 3: return (reverse) ? a.threads < b.threads : a.threads > b.threads; case 3: return (reverse) ? a.threads < b.threads : a.threads > b.threads;
case 4: return (reverse) ? a.user < b.user : a.user > b.user; case 4: return (reverse) ? a.user < b.user : a.user > b.user;
case 5: return (reverse) ? a.mem < b.mem : a.mem > b.mem; case 5: return (reverse) ? a.mem < b.mem : a.mem > b.mem;
case 6: return (reverse) ? a.cpu_p < b.cpu_p : a.cpu_p > b.cpu_p; case 6: return (reverse) ? a.cpu_p < b.cpu_p : a.cpu_p > b.cpu_p;
case 7: return (reverse) ? a.cpu_c < b.cpu_c : a.cpu_c > b.cpu_c; case 7: return (reverse) ? a.cpu_c < b.cpu_c : a.cpu_c > b.cpu_c;
} }
return false; return false;
} }
@ -328,11 +326,12 @@ namespace Proc {
//* Clear dead processes from cache at a regular interval //* Clear dead processes from cache at a regular interval
if (++counter >= 10000 || (filter.empty() && cache.size() > procs.size() + 100)) { if (++counter >= 10000 || (filter.empty() && cache.size() > procs.size() + 100)) {
unordered_map<uint, p_cache> r_cache; unordered_flat_map<uint, p_cache> r_cache;
r_cache.reserve(procs.size());
counter = 0; counter = 0;
if (filter.empty()) { if (filter.empty()) {
for (auto& p : procs) r_cache[p.pid] = cache[p.pid]; for (auto& p : procs) r_cache[p.pid] = cache[p.pid];
cache = move(r_cache); cache.swap(r_cache);
} }
else cache.clear(); else cache.clear();
} }

View File

@ -192,7 +192,7 @@ namespace Theme {
string depth; string depth;
colors.clear(); rgbs.clear(); colors.clear(); rgbs.clear();
for (auto& [name, color] : Default_theme) { for (auto& [name, color] : Default_theme) {
depth = (name.ends_with("bg")) ? "bg" : "fg"; depth = (name.ends_with("bg") && name != "meter_bg") ? "bg" : "fg";
if (source.contains(name)) { if (source.contains(name)) {
if (source.at(name)[0] == '#') { if (source.at(name)[0] == '#') {
colors[name] = hex_to_color(source.at(name), !Config::getB("truecolor"), depth); colors[name] = hex_to_color(source.at(name), !Config::getB("truecolor"), depth);

View File

@ -56,6 +56,8 @@ namespace Symbols {
const string div_up = ""; const string div_up = "";
const string div_down = ""; const string div_down = "";
const string meter = "";
const array<string, 10> superscript = { "", "¹", "²", "³", "", "", "", "", "", "" }; const array<string, 10> superscript = { "", "¹", "²", "³", "", "", "", "", "", "" };
} }
@ -254,6 +256,11 @@ namespace Tools {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
} }
//* Return current time since epoch in milliseconds
inline uint64_t time_micros(){
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
//* Check if a string is a valid bool value //* Check if a string is a valid bool value
inline bool isbool(string& str){ inline bool isbool(string& str){
return (str == "true") || (str == "false") || (str == "True") || (str == "False"); return (str == "true") || (str == "false") || (str == "True") || (str == "False");