Proc optimization

This commit is contained in:
aristocratos 2021-05-22 02:13:56 +02:00
parent 881b90f4f3
commit 81f2284a75
7 changed files with 191 additions and 172 deletions

View file

@ -26,6 +26,8 @@ tab-size = 4
#include <atomic>
#include <numeric>
#include <ranges>
#include <filesystem>
#include <unistd.h>
#include <btop_globs.h>
#include <btop_tools.h>
@ -56,6 +58,7 @@ tab-size = 4
using std::string, std::vector, std::array, std::map, std::atomic, std::endl, std::cout, std::views::iota, std::list, std::accumulate;
using std::flush, std::endl, std::future, std::string_literals::operator""s, std::future_status;
namespace fs = std::filesystem;
using namespace Tools;
@ -151,9 +154,9 @@ int main(int argc, char **argv){
#if defined(LINUX)
//? Linux init
Global::proc_path = (fs::is_directory(fs::path("/proc"))) ? fs::path("/proc") : Global::proc_path;
Global::proc_path = (fs::is_directory(fs::path("/proc")) && access("/proc", R_OK) != -1) ? fs::path("/proc") : Global::proc_path;
if (Global::proc_path.empty()) {
cout << "ERROR: Proc filesystem not detected!" << endl;
cout << "ERROR: Proc filesystem not found/readable!" << endl;
exit(1);
}
#endif
@ -161,7 +164,7 @@ int main(int argc, char **argv){
//? Initialize terminal and set options
if (!Term::init()) {
cout << "ERROR: No tty detected!" << endl;
cout << "Sorry, btop++ needs an interactive shell to run." << endl;
cout << "btop++ needs an interactive shell to run." << endl;
exit(1);
}
@ -171,7 +174,7 @@ int main(int argc, char **argv){
auto thts = time_ms();
//? Generate the theme
Theme::set(Global::Default_theme);
Theme::set(Theme::Default_theme);
//? Create the btop++ banner
Global::banner = createBanner();
@ -233,7 +236,6 @@ int main(int argc, char **argv){
}
if (thread_test){
map<int, future<string>> runners;
@ -263,26 +265,17 @@ int main(int argc, char **argv){
//------>>>>>>
//*------>>>>>> Proc testing
auto timestamp = time_ms();
Proc::init();
cout << "Total Processes init: " << time_ms() - timestamp << "ms" << endl;
cout << "Press any key to start!" << Mv::l(100) << flush;
// sleep_ms(1000);
// Input::wait();
// Input::clear();
// insert Processes call here
uint lc;
string ostring;
uint64_t tsl, timestamp2;
uint64_t tsl, timestamp2, rcount = 0;
list<uint64_t> avgtimes;
uint timer = 1000;
bool filtering = false;
@ -326,8 +319,8 @@ int main(int argc, char **argv){
avgtimes.push_front(timestamp);
if (avgtimes.size() > 100) avgtimes.pop_back();
cout << pbox << ostring << Fx::reset << "\n" << endl;
cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << timestamp << "ms. Average: " << accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size() <<
"ms of " << avgtimes.size() << " samples. Drawing took: " << time_ms() - timestamp2 << "ms. " << endl;
cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 4) << "ms. Average: " << rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 3) <<
"ms of " << avgtimes.size() << " samples. Drawing took: " << time_ms() - timestamp2 << "ms. Number of processes: " << Proc::numpids << ". Run count: " << ++rcount << " " << endl;
while (time_ms() < tsl) {
if (Input::poll(tsl - time_ms())) key = Input::get();
@ -353,7 +346,7 @@ int main(int argc, char **argv){
// cout << "Found " << plist.size() << " pids\n" << endl;
//-----<<<<<
//*-----<<<<<
//cout << pw->pw_name << "/" << gr->gr_name << endl;

View file

@ -23,7 +23,6 @@ tab-size = 4
#include <map>
#include <ranges>
#include <btop_globs.h>
#include <btop_config.h>
#include <btop_tools.h>

View file

@ -31,50 +31,7 @@ namespace Global {
atomic<bool> stop_all(false);
const unordered_map<string, string> Default_theme = {
{ "main_bg", "#00" },
{ "main_fg", "#cc" },
{ "title", "#ee" },
{ "hi_fg", "#969696" },
{ "selected_bg", "#7e2626" },
{ "selected_fg", "#ee" },
{ "inactive_fg", "#40" },
{ "graph_text", "#60" },
{ "meter_bg", "#40" },
{ "proc_misc", "#0de756" },
{ "cpu_box", "#3d7b46" },
{ "mem_box", "#8a882e" },
{ "net_box", "#423ba5" },
{ "proc_box", "#923535" },
{ "div_line", "#30" },
{ "temp_start", "#4897d4" },
{ "temp_mid", "#5474e8" },
{ "temp_end", "#ff40b6" },
{ "cpu_start", "#50f095" },
{ "cpu_mid", "#f2e266" },
{ "cpu_end", "#fa1e1e" },
{ "free_start", "#223014" },
{ "free_mid", "#b5e685" },
{ "free_end", "#dcff85" },
{ "cached_start", "#0b1a29" },
{ "cached_mid", "#74e6fc" },
{ "cached_end", "#26c5ff" },
{ "available_start", "#292107" },
{ "available_mid", "#ffd77a" },
{ "available_end", "#ffb814" },
{ "used_start", "#3b1f1c" },
{ "used_mid", "#d9626d" },
{ "used_end", "#ff4769" },
{ "download_start", "#231a63" },
{ "download_mid", "#4f43a3" },
{ "download_end", "#b0a9de" },
{ "upload_start", "#510554" },
{ "upload_mid", "#7d4180" },
{ "upload_end", "#dcafde" },
{ "process_start", "#80d0a3" },
{ "process_mid", "#dcd179" },
{ "process_end", "#d45454" }
};
const unordered_map<string, unordered_map<string, vector<string>>> Menus = {
{ "options", {
@ -115,25 +72,10 @@ namespace Global {
} }
};
//? Units for floating_humanizer function
const array<string, 11> Units_bit = {"bit", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib", "Bib", "GEb"};
const array<string, 11> Units_byte = {"Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB"};
}
namespace Symbols {
const string h_line = "";
const string v_line = "";
const string left_up = "";
const string right_up = "";
const string left_down = "";
const string right_down = "";
const string title_left = "";
const string title_right = "";
const string div_up = "";
const string div_down = "";
const array<string, 10> superscript = { "", "¹", "²", "³", "", "", "", "", "", "" };
}
#endif

View file

@ -23,12 +23,16 @@ tab-size = 4
#include <unordered_map>
#include <iostream>
#include <btop_globs.h>
#include <btop_tools.h>
using std::string, std::unordered_map, std::cin;
using namespace Tools;
/* The input functions relies on the following std::cin options being set:
cin.sync_with_stdio(false);
cin.tie(NULL);
These will automatically be set when running Term::init() from btop_tools.h
*/
//* Functions and variables for handling keyboard and mouse input
namespace Input {

View file

@ -30,7 +30,6 @@ tab-size = 4
#include <ranges>
#include <list>
#include <unistd.h>
#include <btop_config.h>
@ -39,12 +38,13 @@ tab-size = 4
using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, std::unordered_map, std::deque, std::list;
using std::cout, std::flush, std::endl;
namespace fs = std::filesystem;
using namespace Tools;
const auto SSmax = std::numeric_limits<streamsize>::max();
namespace Global {
const string System = "linux";
fs::path proc_path;
}
@ -64,8 +64,6 @@ double system_uptime(){
namespace Proc {
namespace {
uint64_t tstamp;
size_t numpids = 500;
long int clk_tck;
struct p_cache {
string name, cmd, user;
uint64_t cpu_t = 0, cpu_s = 0;
@ -75,10 +73,12 @@ namespace Proc {
fs::path passwd_path;
fs::file_time_type passwd_time;
uint counter = 0;
long page_size = sysconf(_SC_PAGE_SIZE);
auto page_size = sysconf(_SC_PAGE_SIZE);
auto clk_tck = sysconf(_SC_CLK_TCK);
}
size_t numpids = 500;
atomic<bool> stop (false);
atomic<bool> running (false);
array<string, 8> sort_array = {
@ -116,15 +116,13 @@ namespace Proc {
bool new_cache;
char state;
int cpu_n, p_nice;
size_t threads;
size_t threads, s_pos, c_pos, s_count;
ifstream pread;
string pid_str, name, cmd, attr, user, instr, uid, status, tmpstr;
auto since_last = time_ms() - tstamp;
if (since_last < 1) since_last = 1;
auto uptime = system_uptime();
auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7;
vector<string> pstat;
pstat.reserve(40);
vector<proc_info> procs;
procs.reserve((numpids + 10));
vector<uint> c_pids;
@ -136,35 +134,35 @@ namespace Proc {
string r_uid, r_user;
passwd_time = fs::last_write_time(passwd_path);
uid_user.clear();
ifstream pread(passwd_path);
pread.open(passwd_path);
if (pread.good()) {
while (true){
getline(pread, r_user, ':');
pread.ignore(numeric_limits<streamsize>::max(), ':');
pread.ignore(SSmax, ':');
getline(pread, r_uid, ':');
uid_user[r_uid] = r_user;
pread.ignore(numeric_limits<streamsize>::max(), '\n');
pread.ignore(SSmax, '\n');
if (pread.eof()) break;
}
}
pread.close();
}
//* Iterate over all pids in /proc and get relevant values
for (auto& d: fs::directory_iterator(Global::proc_path)){
if (pread.is_open()) pread.close();
if (stop.load()) {
procs.clear();
running.store(false);
stop.store(false);
return procs;
}
numpids++;
pid_str = fs::path(d.path()).filename();
pid_str = d.path().filename();
cpu = 0.0; cpu_s = 0.0; cpu_t = 0; cpu_n = 0;
rss_mem = 0; threads = 0; state = '0'; ppid = 0; p_nice = 0;
new_cache = false;
if (d.is_directory() && isdigit(pid_str[0])) {
numpids++;
pid = stoul(pid_str);
c_pids.push_back(pid);
@ -172,76 +170,106 @@ namespace Proc {
if (!cache.contains(pid)) {
name.clear(); cmd.clear(); user.clear();
new_cache = true;
if (fs::exists((string)d.path() + "/comm")) {
pread.clear(); name.clear();
ifstream pread((string)d.path() + "/comm");
if (pread.good()) getline(pread, name);
pread.open(d.path() / "comm");
if (pread.good()) {
getline(pread, name);
pread.close();
}
if (fs::exists((string)d.path() + "/cmdline")) {
pread.clear(); cmd.clear(); tmpstr.clear();
ifstream pread((string)d.path() + "/cmdline");
if (pread.good()) while(getline(pread, tmpstr, '\0')) cmd += tmpstr + " ";
else continue;
pread.open(d.path() / "cmdline");
if (pread.good()) {
tmpstr.clear();
while(getline(pread, tmpstr, '\0')) cmd += tmpstr + " ";
pread.close();
if (!cmd.empty()) cmd.pop_back();
}
if (fs::exists((string)d.path() + "/status")) {
pread.clear(); status.clear(); uid.clear();
ifstream pread((string)d.path() + "/status");
if (pread.good()) {
while (!pread.eof()){
getline(pread, status, ':');
if (status == "Uid") {
pread.ignore();
getline(pread, uid, '\t');
break;
} else {
pread.ignore(numeric_limits<streamsize>::max(), '\n');
}
else continue;
pread.open(d.path() / "status");
if (pread.good()) {
status.clear(); uid.clear();
while (!pread.eof()){
getline(pread, status, ':');
if (status == "Uid") {
pread.ignore();
getline(pread, uid, '\t');
break;
} else {
pread.ignore(SSmax, '\n');
}
}
pread.close();
user = (!uid.empty() && uid_user.contains(uid)) ? uid_user.at(uid) : uid;
}
else continue;
cache[pid] = p_cache(name, cmd, user);
}
//* Match filter if defined
if (!filter.empty() && pid_str.find(filter) == string::npos &&
cache[pid].name.find(filter) == string::npos &&
cache[pid].cmd.find(filter) == string::npos &&
cache[pid].user.find(filter) == string::npos) {
if (new_cache) cache.erase(pid);
continue;
}
//* Get cpu usage, cpu cumulative and threads from /proc/[pid]/stat
if (fs::exists((string)d.path() + "/stat")) {
pread.clear(); instr.clear(); pstat.clear();
ifstream pread((string)d.path() + "/stat");
if (pread.good() && getline(pread, instr)) {
//? Skip pid and comm field and find comm fields closing ')' from right to avoid names with whitespace or parenthesis
pstat = ssplit(instr.substr(instr.rfind(')') + 1), " ", 37, true);
}
pread.open(d.path() / "stat");
if (pread.good()) {
instr.clear(); s_pos = 0; c_pos = 0; s_count = 0;
getline(pread, instr);
pread.close();
if (pstat.size() < 20) continue;
//? Skip pid and comm field and find comm fields closing ')' to avoid names with whitespace or parenthesis
s_pos = instr.find_last_of(')') + 2;
//? Process state
state = pstat[0][0];
do {
c_pos = instr.find(' ', s_pos);
if (c_pos == string::npos) break;
//? Process parent pid
ppid = stoul(pstat[1]);
//? Process nice value
p_nice = stoi(pstat[16]);
//? Process number of threads
threads = stoul(pstat[17]);
//? Process utime + stime
cpu_t = stoull(pstat[11]) + stoull(pstat[12]);
//? Cache cpu times and cpu seconds
if (new_cache) {
cache[pid].cpu_t = cpu_t;
cache[pid].cpu_s = stoull(pstat[19]);
}
//? CPU number last executed on
if (pstat.size() > 36) cpu_n = stoi(pstat[36]);
switch (s_count) {
case 0: { //? Process state
state = instr[s_pos];
break;
}
case 1: { //? Process parent pid
ppid = stoul(instr.substr(s_pos, c_pos - s_pos));
break;
}
case 11: { //? Process utime
cpu_t = stoull(instr.substr(s_pos, c_pos - s_pos));
break;
}
case 12: { //? Process stime
cpu_t += stoull(instr.substr(s_pos, c_pos - s_pos));
break;
}
case 16: { //? Process nice value
p_nice = stoi(instr.substr(s_pos, c_pos - s_pos));
break;
}
case 17: { //? Process number of threads
threads = stoul(instr.substr(s_pos, c_pos - s_pos));
break;
}
case 19: { //? Cache cpu times and cpu seconds
if (new_cache) {
cache[pid].cpu_t = cpu_t;
cache[pid].cpu_s = stoull(instr.substr(s_pos, c_pos - s_pos));
};
break;
}
case 36: { //? CPU number last executed on
cpu_n = stoi(instr.substr(s_pos, c_pos - s_pos));
break;
}
}
s_pos = c_pos + 1;
} while (s_count++ < 36);
if (s_count < 20) continue;
//? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion
cpu = static_cast<double>(100000 * (cpu_t - cache[pid].cpu_t) / since_last) / clk_tck;
@ -252,27 +280,17 @@ namespace Proc {
//? Update cache with latest cpu times
cache[pid].cpu_t = cpu_t;
}
else continue;
//* Get RSS memory in bytes from /proc/[pid]/statm
if (fs::exists((string)d.path() + "/statm")) {
pread.clear();
ifstream pread((string)d.path() + "/statm");
if (pread.good()) {
pread.ignore(numeric_limits<streamsize>::max(), ' ');
pread >> rss_mem;
rss_mem *= page_size;
}
pread.open(d.path() / "statm");
if (pread.good()) {
pread.ignore(SSmax, ' ');
pread >> rss_mem;
rss_mem *= page_size;
pread.close();
}
//* Match filter if defined
if (!filter.empty() &&
pid_str.find(filter) == string::npos &&
cache[pid].name.find(filter) == string::npos &&
cache[pid].cmd.find(filter) == string::npos &&
cache[pid].user.find(filter) == string::npos
) continue;
//* Create proc_info
procs.push_back(proc_info(pid, cache[pid].name, cache[pid].cmd, threads, cache[pid].user, rss_mem, cpu, cpu_s, state, cpu_n, p_nice, ppid));
}
@ -322,9 +340,8 @@ namespace Proc {
//* Initialize needed variables for collect
void init(){
clk_tck = sysconf(_SC_CLK_TCK);
tstamp = time_ms();
passwd_path = (fs::exists(fs::path("/etc/passwd"))) ? fs::path("/etc/passwd") : passwd_path;
passwd_path = (access("/etc/passwd", R_OK) != -1) ? fs::path("/etc/passwd") : passwd_path;
uint i = 0;
for (auto& item : sort_array) sort_map[item] = i++;
}

View file

@ -35,6 +35,51 @@ using namespace Tools;
namespace Theme {
const unordered_map<string, string> Default_theme = {
{ "main_bg", "#00" },
{ "main_fg", "#cc" },
{ "title", "#ee" },
{ "hi_fg", "#969696" },
{ "selected_bg", "#7e2626" },
{ "selected_fg", "#ee" },
{ "inactive_fg", "#40" },
{ "graph_text", "#60" },
{ "meter_bg", "#40" },
{ "proc_misc", "#0de756" },
{ "cpu_box", "#3d7b46" },
{ "mem_box", "#8a882e" },
{ "net_box", "#423ba5" },
{ "proc_box", "#923535" },
{ "div_line", "#30" },
{ "temp_start", "#4897d4" },
{ "temp_mid", "#5474e8" },
{ "temp_end", "#ff40b6" },
{ "cpu_start", "#50f095" },
{ "cpu_mid", "#f2e266" },
{ "cpu_end", "#fa1e1e" },
{ "free_start", "#223014" },
{ "free_mid", "#b5e685" },
{ "free_end", "#dcff85" },
{ "cached_start", "#0b1a29" },
{ "cached_mid", "#74e6fc" },
{ "cached_end", "#26c5ff" },
{ "available_start", "#292107" },
{ "available_mid", "#ffd77a" },
{ "available_end", "#ffb814" },
{ "used_start", "#3b1f1c" },
{ "used_mid", "#d9626d" },
{ "used_end", "#ff4769" },
{ "download_start", "#231a63" },
{ "download_mid", "#4f43a3" },
{ "download_end", "#b0a9de" },
{ "upload_start", "#510554" },
{ "upload_mid", "#7d4180" },
{ "upload_end", "#dcafde" },
{ "process_start", "#80d0a3" },
{ "process_mid", "#dcd179" },
{ "process_end", "#d45454" }
};
namespace {
//* Convert 24-bit colors to 256 colors using 6x6x6 color cube
int truecolor_to_256(uint r, uint g, uint b){
@ -146,7 +191,7 @@ namespace Theme {
vector<string> t_rgb;
string depth;
colors.clear(); rgbs.clear();
for (auto& [name, color] : Global::Default_theme) {
for (auto& [name, color] : Default_theme) {
depth = (name.ends_with("bg")) ? "bg" : "fg";
if (source.contains(name)) {
if (source.at(name)[0] == '#') {

View file

@ -37,6 +37,21 @@ using std::string, std::vector, std::regex, std::max, std::to_string, std::cin;
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
namespace Symbols {
const string h_line = "";
const string v_line = "";
const string left_up = "";
const string right_up = "";
const string left_down = "";
const string right_down = "";
const string title_left = "";
const string title_right = "";
const string div_up = "";
const string div_down = "";
const array<string, 10> superscript = { "", "¹", "²", "³", "", "", "", "", "", "" };
}
//* Collection of escape codes for text style and formatting
namespace Fx {
//* Escape sequence start
@ -228,34 +243,34 @@ namespace Tools {
}
//* Return current time since epoch in milliseconds
uint64_t time_ms(){
inline uint64_t time_ms(){
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
//* Check if a string is a valid bool value
bool isbool(string str){
inline bool isbool(string& str){
return (str == "true") || (str == "false") || (str == "True") || (str == "False");
}
//* Check if a string is a valid integer value
bool isint(string str){
inline bool isint(string& str){
return all_of(str.begin(), str.end(), ::isdigit);
}
//* Left-trim <t_str> from <str> and return string
string ltrim(string str, string t_str = " "){
inline string ltrim(string str, string t_str = " "){
while (str.starts_with(t_str)) str.erase(0, t_str.size());
return str;
}
//* Right-trim <t_str> from <str> and return string
string rtrim(string str, string t_str = " "){
inline string rtrim(string str, string t_str = " "){
while (str.ends_with(t_str)) str.resize(str.size() - t_str.size());
return str;
}
//* Left-right-trim <t_str> from <str> and return string
string trim(string str, string t_str = " "){
inline string trim(string str, string t_str = " "){
return ltrim(rtrim(str, t_str), t_str);
}
@ -352,6 +367,10 @@ namespace Tools {
return out;
}
//? Units for floating_humanizer function
const array<string, 11> Units_bit = {"bit", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib", "Bib", "GEb"};
const array<string, 11> Units_byte = {"Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB"};
//* Scales up in steps of 1024 to highest possible unit and returns string with unit suffixed
//* bit=True or defaults to bytes
//* start=int to set 1024 multiplier starting unit
@ -359,7 +378,7 @@ namespace Tools {
string floating_humanizer(uint64_t value, bool shorten=false, uint start=0, bool bit=false, bool per_second=false){
string out;
uint mult = (bit) ? 8 : 1;
auto& units = (bit) ? Global::Units_bit : Global::Units_byte;
auto& units = (bit) ? Units_bit : Units_byte;
value *= 100 * mult;