Processes sorting, filtering and cpu calculations

This commit is contained in:
aristocratos 2021-05-13 03:11:29 +02:00
parent d1180d6b38
commit cfa944faac
6 changed files with 199 additions and 75 deletions

View File

@ -1,7 +1,7 @@
PREFIX ?= /usr/local
DOCDIR ?= $(PREFIX)/share/btop/doc
CXX = g++
CXXFLAGS = -std=c++20 -pthread -Wall -Wextra
CXXFLAGS = -std=c++20 -pthread -Wall
INCLUDES = -I./src
btop: btop.cpp

View File

@ -36,7 +36,6 @@ tab-size = 4
#if defined(__linux__)
#define SYSTEM "linux"
#include <sys/sysinfo.h>
#include <btop_linux.h>
#elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__)
#include <sys/param.h>
@ -245,40 +244,49 @@ int main(int argc, char **argv){
}
}
struct sysinfo sinfo;
sysinfo(&sinfo);
cout << "Up for " << sec_to_dhms(sinfo.uptime) << endl;
cout << "Up for " << sec_to_dhms(round(system_uptime())) << endl;
//------>>>>>>
auto timestamp = time_ms();
Processes Proc;
cout << "Total Processes init: " << time_ms() - timestamp << "ms" << endl;
sleep_ms(1000);
// insert Processes call here
timestamp = time_ms() - timestamp;
auto timestamp2 = time_ms();
// insert Processes call here
timestamp2 = time_ms() - timestamp2;
// int lc = 0;
// string ostring;
// cout << rjustify("Pid:", 8) << " " << ljustify("Program:", 16) << " " << ljustify("Command:", Term.width - 50) << " " << rjustify("User:", 10) << " " << rjustify("Group:", 10) << "\n";
// for (auto& [lpid, lname, lcmd, luser, lgroup] : plist){
// ostring += rjustify(to_string(lpid), 8) + " " + ljustify(lname, 16) + " " + ljustify(lcmd, Term.width - 50, true) + " " + rjustify(luser, 10) + " " + rjustify(lgroup, 10) + "\n";
// if (lc++ > Term.height - 30) break;
// }
unsigned lc;
string ostring;
cout << rjustify("Pid:", 8) << " " << ljustify("Program:", 16) << " " << ljustify("Command:", Term.width - 48) << " " << rjustify("User:", 10) << " " << rjustify("Cpu%", 4) << "\n" << Mv::save << flush;
// cout << ostring << endl;
while (Input() != "q") {
timestamp = time_ms();
auto plist = Proc.collect("cpu lazy", false);
timestamp = time_ms() - timestamp;
ostring.clear();
lc = 0;
for (auto& [lpid, lname, lcmd, luser, lcpu, lcpu_s] : plist){
(void) lcpu_s;
ostring += rjustify(to_string(lpid), 8) + " " + ljustify(lname, 16) + " " + ljustify(lcmd, Term.width - 48, true) + " " + rjustify(luser, 10) + " ";
ostring += (lcpu > 100) ? rjustify(to_string(lcpu), 3) + " " : rjustify(to_string(lcpu), 4);
ostring += "\n";
if (lc++ > Term.height - 20) break;
}
cout << Mv::restore << ostring << endl;
cout << "Processes call took: " << timestamp << "ms." << endl;
Input(2000);
}
// cout << "List generated in " << timestamp << "ms first call and in " << timestamp2 << "ms second call" << endl;
// cout << "Found " << plist.size() << " pids\n" << endl;
//-----<<<<<

View File

@ -115,4 +115,4 @@ namespace Global {
};
}
#endif
#endif

View File

@ -29,9 +29,6 @@ tab-size = 4
#include <ranges>
#include <unistd.h>
#include <pwd.h>
// #include <grp.h>
#include <sys/stat.h>
#include <btop_config.h>
#include <btop_globs.h>
@ -41,82 +38,197 @@ namespace fs = std::filesystem;
using namespace std;
class Processes {
uint64_t timestamp;
uint64_t tstamp;
long int clk_tck;
map<int, tuple<string, string, string>> cache;
map<string, unsigned> sorts = {
{"pid", 0},
{"name", 1},
{"command", 2},
{"user", 3}
{"user", 3},
{"cpu direct", 4},
{"cpu lazy", 5}
};
map<string, string> uid_user;
fs::file_time_type passwd_time;
map<int, uint64_t> cpu_times;
map<int, uint64_t> cpu_second;
unsigned counter = 0;
public:
atomic<bool> stop;
atomic<bool> running;
//* Collects process information from /proc and returns a vector of tuples
auto collect(string sorting="pid", bool reverse=false, string filter=""){
running.store(true);
int pid;
uint64_t cpu_t;
double cpu, cpu_s;
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 = (sorts.contains(sorting)) ? sorts[sorting] : 5;
vector<string> pstat;
//? Return type! Values in tuple: pid, program, command, username, cpu%, cpu cumulative
vector<tuple<int, string, string, string, double, double>> procs;
//* Update uid_user map if /etc/passwd changed since last run
if (fs::last_write_time(fs::path("/etc/passwd")) != passwd_time) {
string r_uid, r_user;
passwd_time = fs::last_write_time(fs::path("/etc/passwd"));
uid_user.clear();
pread.clear();
ifstream pread("/etc/passwd");
while (true){
getline(pread, r_user, ':');
pread.ignore(numeric_limits<streamsize>::max(), ':');
getline(pread, r_uid, ':');
uid_user[r_uid] = r_user;
pread.ignore(numeric_limits<streamsize>::max(), '\n');
if (pread.eof()) break;
}
pread.close();
}
auto collect(string sorting="pid", bool reverse=false){
int pid, count = 0;
auto timestamp = time_ms();
string item, name, cmd, attr, user;
struct stat ug;
auto sortint = (sorts.contains(sorting)) ? sorts[sorting] : 0;
vector<tuple<int, string, string, string>> procs;
//* Iterate over all pid directories in /proc and get relevant values
for (auto& d: fs::directory_iterator("/proc")){
item = fs::path(d.path()).filename();
bool cached = false;
if (d.is_directory() && isdigit(item[0])) {
pid = stoi(item);
if (cache.contains(pid)) {
cached = true;
} else {
if (stop.load()) {
procs.clear();
running.store(false);
stop.store(false);
return procs;
}
pid_str = fs::path(d.path()).filename();
cpu = 0.0;
if (d.is_directory() && isdigit(pid_str[0])) {
pid = stoi(pid_str);
//* Get cpu usage
if (fs::is_regular_file((string)d.path() + "/stat")) {
pstat.clear();
ifstream pread((string)d.path() + "/stat");
while (getline(pread, instr, ' ')) pstat.push_back(instr);
pread.close();
//? Process utime + stime
cpu_t = stoull(pstat[13]) + stoull(pstat[14]);
if (!cpu_times.contains(pid)) cpu_times[pid] = cpu_t;
//? Cache process start time
if (!cpu_second.contains(pid)) cpu_second[pid] = stoull(pstat[21]);
//? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion
cpu = static_cast<double>(100000 * (cpu_t - cpu_times[pid]) / since_last) / clk_tck;
//? Process cumulative cpu usage since process start
cpu_s = static_cast<double>((cpu_t / clk_tck) / (uptime - (cpu_second[pid] / clk_tck)));
cpu_times[pid] = cpu_t;
}
//* Cache program name, command and username
if (!cache.contains(pid)) {
if (fs::is_regular_file((string)d.path() + "/comm")) {
ifstream pread((string)d.path() + "/comm");
getline(pread, name);
pread.close();
}
if (fs::is_regular_file((string)d.path() + "/cmdline")) {
cmd.clear();
ifstream pread((string)d.path() + "/cmdline");
getline(pread, cmd);
// if (cmd.size() > 1) cmd.erase(cmd.size(), 1);
// cmd = to_string(ulen(cmd)) + ":" + to_string(cmd.size()) + cmd;
while(getline(pread, tmpstr, '\0')) cmd += tmpstr + " ";
pread.close();
if (!cmd.empty()) cmd.pop_back();
}
if (fs::is_regular_file((string)d.path() + "/attr")) {
attr = (string)d.path() + "/attr";
stat(attr.c_str(), &ug); // Error check omitted
struct passwd *pw = getpwuid(ug.st_uid);
user = pw->pw_name;
// struct group *gr = getgrgid(ug.st_gid);
if (fs::exists((string)d.path() + "/status")) {
ifstream pread((string)d.path() + "/status");
status.clear();
while (!pread.eof()){
getline(pread, status, ':');
if (status == "Uid") {
pread.ignore();
getline(pread, uid, '\t');
break;
} else {
pread.ignore(numeric_limits<streamsize>::max(), '\n');
}
}
pread.close();
user = (uid_user.contains(uid)) ? uid_user.at(uid) : "";
}
cache[pid] = make_tuple(name, clean_nullbyte(cmd), user);
cache[pid] = make_tuple(name, cmd, user);
}
procs.push_back(make_tuple(pid, get<0>(cache[pid]), get<1>(cache[pid]), get<2>(cache[pid])));
// //* Match filter if applicable
if (!filter.empty() &&
pid_str.find(filter) == string::npos &&
get<0>(cache[pid]).find(filter) == string::npos &&
get<1>(cache[pid]).find(filter) == string::npos &&
get<2>(cache[pid]).find(filter) == string::npos
) continue;
//* Create tuple
procs.push_back(make_tuple(pid, get<0>(cache[pid]), get<1>(cache[pid]), get<2>(cache[pid]), cpu, cpu_s));
}
}
ranges::sort(procs, [sortint, reverse](tuple<int, string, string, string>& a, tuple<int, string, string, string>& b) {
if (reverse) {
// auto st = time_ms();
//* Sort processes vector
ranges::sort(procs, [&sortint, &reverse](tuple<int, string, string, string, double, double>& a, tuple<int, string, string, string, double, double>& b) {
switch (sortint) {
case 0: return get<0>(a) > get<0>(b);
case 1: return get<1>(a) > get<1>(b);
case 2: return get<2>(a) > get<2>(b);
case 3: return get<3>(a) > get<3>(b);
}
} else {
switch (sortint) {
case 0: return get<0>(a) < get<0>(b);
case 1: return get<1>(a) < get<1>(b);
case 2: return get<2>(a) < get<2>(b);
case 3: return get<3>(a) < get<3>(b);
case 0: return (reverse) ? get<0>(a) < get<0>(b) : get<0>(a) > get<0>(b); //? Pid
case 1: return (reverse) ? get<1>(a) < get<1>(b) : get<1>(a) > get<1>(b); //? Program
case 2: return (reverse) ? get<2>(a) < get<2>(b) : get<2>(a) > get<2>(b); //? Command
case 3: return (reverse) ? get<3>(a) < get<3>(b) : get<3>(a) > get<3>(b); //? User
case 4: return (reverse) ? get<4>(a) < get<4>(b) : get<4>(a) > get<4>(b); //? Cpu direct
case 5: return (reverse) ? get<5>(a) < get<5>(b) : get<5>(a) > get<5>(b); //? Cpu lazy
}
return false;
}
});
// if (reverse) views::reverse(procs);
);
timestamp = time_ms() - timestamp;
//* When using "cpu lazy" sorting, push processes with high cpu usage to the front regardless of cumulative usage
if (sortint == 5 && !reverse) {
double max = 10.0, target = 30.0;
for (size_t i = 0, offset = 0; i < procs.size(); i++) {
if (i <= 5 && get<4>(procs[i]) > max) max = get<4>(procs[i]);
else if (i == 6) target = (max > 30.0) ? max : 10.0;
if (i == offset && get<4>(procs[i]) > 30.0) offset++;
else if (get<4>(procs[i]) > target) rotate(procs.begin() + offset, procs.begin() + i, procs.begin() + i + 1);
}
}
// cout << "Sort took: " << time_ms() - st << "ms " << flush;
//* Clear all cached values at a regular interval
if (++counter >= 10000 || (filter.empty() && cache.size() > procs.size() + 100)) {
counter = 0;
cache.clear();
cpu_times.clear();
cpu_second.clear();
}
tstamp = time_ms();
running.store(false);
return procs;
}
Processes() {
clk_tck = sysconf(_SC_CLK_TCK);
tstamp = time_ms();
stop.store(false);
collect();
}
};
#endif
#endif

View File

@ -26,6 +26,7 @@ tab-size = 4
#include <chrono>
#include <thread>
#include <algorithm>
#include <fstream>
#include <unistd.h>
#include <termios.h>
@ -206,12 +207,6 @@ string trans(string str){
return (newstr.empty()) ? str : newstr;
}
//* Clean string by replacing null byte '\0' with whitespace ' '
string clean_nullbyte(string str){
while (str.find('\0') != string::npos) str.replace(str.find('\0'), 1, " ");
return str;
}
string sec_to_dhms(unsigned sec){
string out;
unsigned d, h, m;
@ -228,6 +223,14 @@ string sec_to_dhms(unsigned sec){
return out;
}
double system_uptime(){
string upstr;
ifstream pread("/proc/uptime");
getline(pread, upstr, ' ');
pread.close();
return stod(upstr);
}
//? --------------------------------------------------- CLASSES -----------------------------------------------------

1
src/tempCodeRunnerFile.h Normal file
View File

@ -0,0 +1 @@
clean_nullbyte(