mirror of
https://github.com/aristocratos/btop.git
synced 2024-06-17 01:45:34 +12:00
Processes sorting, filtering and cpu calculations
This commit is contained in:
parent
d1180d6b38
commit
cfa944faac
2
Makefile
2
Makefile
|
@ -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
|
||||
|
|
52
btop.cpp
52
btop.cpp
|
@ -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
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
// }
|
||||
|
||||
// cout << ostring << endl;
|
||||
|
||||
// cout << "List generated in " << timestamp << "ms first call and in " << timestamp2 << "ms second call" << endl;
|
||||
// cout << "Found " << plist.size() << " pids\n" << endl;
|
||||
|
||||
//-----<<<<<
|
||||
|
|
214
src/btop_linux.h
214
src/btop_linux.h
|
@ -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,80 +38,195 @@ 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);
|
||||
}
|
||||
cache[pid] = make_tuple(name, clean_nullbyte(cmd), user);
|
||||
}
|
||||
|
||||
procs.push_back(make_tuple(pid, get<0>(cache[pid]), get<1>(cache[pid]), get<2>(cache[pid])));
|
||||
}
|
||||
}
|
||||
|
||||
ranges::sort(procs, [sortint, reverse](tuple<int, string, string, string>& a, tuple<int, string, string, string>& b) {
|
||||
if (reverse) {
|
||||
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);
|
||||
}
|
||||
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, cmd, user);
|
||||
}
|
||||
|
||||
// //* 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));
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
);
|
||||
|
||||
//* 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
// if (reverse) views::reverse(procs);
|
||||
|
||||
timestamp = time_ms() - timestamp;
|
||||
|
||||
|
||||
// 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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
1
src/tempCodeRunnerFile.h
Normal file
|
@ -0,0 +1 @@
|
|||
clean_nullbyte(
|
Loading…
Reference in a new issue