Added processes tree view

This commit is contained in:
aristocratos 2021-06-13 23:12:11 +02:00
parent c4b55c7dfd
commit ba481d042c
3 changed files with 119 additions and 60 deletions

View file

@ -120,9 +120,9 @@ void clean_quit(int sig){
Term::restore();
if (!Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
}
if (Global::debug) Logger::debug("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
Global::quitting = true;
Config::write();
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
if (sig != -1) exit(sig);
}
@ -256,18 +256,16 @@ int main(int argc, char **argv){
}
}
//? Read config file if present
//? Config init
{ vector<string> load_errors;
Config::load(Config::conf_file, load_errors);
if (Global::debug) Logger::loglevel = 4;
else Logger::loglevel = v_index(Logger::log_levels, Config::getS("log_level"));
if (Logger::loglevel == 4) Logger::debug("Starting with logger set to debug.");
Logger::info("Log level set to " + Config::getS("log_level") + ".");
if (!load_errors.empty()) {
for (auto& err_str : load_errors) Logger::error(err_str);
}
for (auto& err_str : load_errors) Logger::warning(err_str);
}
if (!string(getenv("LANG")).ends_with("UTF-8") && !string(getenv("LANG")).ends_with("utf-8")) {
@ -501,8 +499,6 @@ int main(int argc, char **argv){
list<uint64_t> avgtimes = {0};
uint timer = 2000;
bool filtering = false;
bool reversing = false;
int sortint = Proc::sort_map["cpu lazy"];
vector<string> greyscale;
string filter;
string filter_cur;
@ -515,13 +511,13 @@ int main(int argc, char **argv){
}
string pbox = Draw::createBox({.x = 1, .y = 10, .width = Term::width, .height = Term::height - 16, .line_color = Theme::c("proc_box"), .title = "testbox", .title2 = "below", .fill = false, .num = 7});
pbox += Mv::r(1) + rjust("Pid:", 8) + " " + ljust("Program:", 16) + " " + ljust("Command:", Term::width - 70) + " Threads: " +
ljust("User:", 10) + " " + rjust("MemB", 5) + " " + rjust("Cpu%", 14) + "\n" + Mv::save;
pbox += Mv::r(1) + Theme::c("title") + Fx::b + rjust("Pid:", 8) + " " + ljust("Program:", 16) + " " + ljust("Command:", Term::width - 70) + " Threads: " +
ljust("User:", 10) + " " + rjust("MemB", 5) + " " + rjust("Cpu%", 14) + "\n" + Fx::reset + Mv::save;
while (key != "q") {
timestamp = time_micros();
tsl = time_ms() + timer;
auto plist = Proc::collect(Proc::sort_array[sortint], reversing, filter, Config::getB("proc_per_core"));
auto plist = Proc::collect();
timestamp2 = time_micros();
timestamp = timestamp2 - timestamp;
ostring.clear();
@ -530,14 +526,27 @@ int main(int argc, char **argv){
ostring = Mv::u(2) + Mv::l(Term::width) + Mv::r(12)
+ trans("Filter: " + filter + (filtering ? Fx::bl + "" + Fx::reset : "")) + Mv::l(Term::width)
+ trans(rjust("Per core: " + (Config::getB("proc_per_core") ? "On "s : "Off"s) + " Sorting: "
+ string(Proc::sort_array[sortint]), Term::width - 3))
+ string(Config::getS("proc_sorting")), Term::width - 3))
+ Mv::restore;
for (auto& p : plist){
ostring += Mv::r(1) + greyscale[lc] + rjust(to_string(p.pid), 8) + " " + ljust(p.name, 16) + " " + ljust(p.cmd, Term::width - 66, true) + " "
if (!Config::getB("proc_tree")) {
ostring += Mv::r(1) + greyscale[lc] + rjust(to_string(p.pid), 8) + " " + ljust(p.name, 16) + " " + ljust(p.cmd, Term::width - 66, true) + " "
+ rjust(to_string(p.threads), 5) + " " + ljust(p.user, 10) + " " + rjust(floating_humanizer(p.mem, true), 5) + string(11, ' ')
+ (p.cpu_p < 10 || p.cpu_p >= 100 ? rjust(to_string(p.cpu_p), 3) + " " : rjust(to_string(p.cpu_p), 4))
+ "\n";
}
else {
string cmd_cond;
if (!p.cmd.empty()) {
cmd_cond = p.cmd.substr(0, std::min(p.cmd.find(' '), p.cmd.size()));
cmd_cond = cmd_cond.substr(std::min(cmd_cond.find_last_of('/') + 1, cmd_cond.size()));
}
ostring += Mv::r(1) + greyscale[lc] + ljust(p.prefix + to_string(p.pid) + " " + p.name + " " + (!cmd_cond.empty() && cmd_cond != p.name ? "(" + cmd_cond + ")" : ""), Term::width - 40, true) + " "
+ rjust(to_string(p.threads), 5) + " " + ljust(p.user, 10) + " " + rjust(floating_humanizer(p.mem, true), 5) + string(11, ' ')
+ (p.cpu_p < 10 || p.cpu_p >= 100 ? rjust(to_string(p.cpu_p), 3) + " " : rjust(to_string(p.cpu_p), 4))
+ "\n";
}
if (lc++ > Term::height - 21) break;
}
@ -562,15 +571,25 @@ int main(int argc, char **argv){
else if (key == "space") filter.push_back(' ');
else if (ulen(key) == 1 ) filter.append(key);
else { key.clear(); continue; }
if (filter != Config::getS("proc_filter")) Config::set("proc_filter", filter);
break;
}
else if (key == "q") break;
else if (key == "left") { if (--sortint < 0) sortint = (int)Proc::sort_array.size() - 1; }
else if (key == "right") { if (++sortint > (int)Proc::sort_array.size() - 1) sortint = 0; }
else if (key == "left") {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (--cur_i < 0) cur_i = Proc::sort_vector.size() - 1;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "right") {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (++cur_i > (int)Proc::sort_vector.size() - 1) cur_i = 0;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "f") filtering = true;
else if (key == "r") reversing = !reversing;
else if (key == "t") Config::flip("proc_tree");
else if (key == "r") Config::flip("proc_reversed");
else if (key == "c") Config::flip("proc_per_core");
else if (key == "delete") filter.clear();
else if (key == "delete") { filter.clear(); Config::set("proc_filter", filter); }
else continue;
break;
}

View file

@ -56,7 +56,6 @@ namespace Config {
"#* \"cpu lazy\" updates top process over time, \"cpu responsive\" updates top process directly."},
{"proc_reversed", "#* Reverse sorting order, True or False."},
{"proc_tree", "#* Show processes as a tree."},
{"tree_depth", "#* Which depth the tree view should auto collapse processes at."},
{"proc_colors", "#* Use the cpu graph colors in the process list."},
{"proc_gradient", "#* Use a darkening gradient in the process list."},
{"proc_per_core", "#* If process cpu usage should be of the core it's running on or usage of the total available cpu power."},
@ -115,7 +114,8 @@ namespace Config {
{"net_download", "10M"},
{"net_upload", "10M"},
{"net_iface", ""},
{"log_level", "WARNING"}
{"log_level", "WARNING"},
{"proc_filter", ""}
};
unordered_flat_map<string, string> stringsTmp;
@ -154,7 +154,6 @@ namespace Config {
unordered_flat_map<string, int> ints = {
{"update_ms", 2000},
{"proc_update_mult", 2},
{"tree_depth", 3}
};
unordered_flat_map<string, int> intsTmp;
@ -215,26 +214,24 @@ namespace Config {
//* Unlock config and write any cached values to config
void unlock(){
atomic_wait_set(writelock);
if (stringsTmp.size() > 0) {
for (auto& item : stringsTmp){
strings.at(item.first) = item.second;
}
stringsTmp.clear();
for (auto& item : stringsTmp){
strings.at(item.first) = item.second;
}
if (intsTmp.size() > 0) {
for (auto& item : intsTmp){
ints.at(item.first) = item.second;
}
intsTmp.clear();
stringsTmp.clear();
for (auto& item : intsTmp){
ints.at(item.first) = item.second;
}
if (boolsTmp.size() > 0) {
for (auto& item : boolsTmp){
bools.at(item.first) = item.second;
}
boolsTmp.clear();
intsTmp.clear();
for (auto& item : boolsTmp){
bools.at(item.first) = item.second;
}
writelock = false;
boolsTmp.clear();
locked = false;
writelock = false;
}
//* Load the config file from disk
@ -247,9 +244,9 @@ namespace Config {
}
std::ifstream cread(conf_file);
if (cread.good()) {
unordered_flat_map<string, int> valid_names;
vector<string> valid_names;
for (auto &n : descriptions)
valid_names[n[0]] = 0;
valid_names.push_back(n[0]);
string v_string;
getline(cread, v_string, '\n');
if (!v_string.ends_with(Global::Version))
@ -262,7 +259,7 @@ namespace Config {
}
string name, value;
getline(cread, name, '=');
if (!valid_names.contains(name)) {
if (!v_contains(valid_names, name)) {
cread.ignore(SSmax, '\n');
continue;
}

View file

@ -41,7 +41,7 @@ tab-size = 4
using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize;
using std::cout, std::flush, std::endl;
using std::cout, std::flush, std::endl, std::string_literals::operator""s;
namespace fs = std::filesystem;
using namespace Tools;
@ -64,7 +64,7 @@ namespace Proc {
uint64_t cpu_t = 0, cpu_s = 0;
string prefix = "";
size_t depth = 0;
bool collapsed = false;
int collapsed = -1;
};
unordered_flat_map<uint, p_cache> cache;
unordered_flat_map<string, string> uid_user;
@ -82,7 +82,7 @@ namespace Proc {
atomic<bool> stop (false);
atomic<bool> collecting (false);
atomic<bool> drawing (false);
array<string, 8> sort_array = {
vector<string> sort_vector = {
"pid",
"name",
"command",
@ -92,7 +92,6 @@ namespace Proc {
"cpu direct",
"cpu lazy",
};
unordered_flat_map<string, int> sort_map;
//* Container for process information
struct proc_info {
@ -108,12 +107,49 @@ namespace Proc {
string prefix = "";
};
//* Generate process tree list
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth=0, bool collapsed=false){
auto cur_pos = out_procs.size();
if (!collapsed)
out_procs.push_back(cur_proc);
int children = 0;
for (auto& p : in_procs) {
if (p.ppid == (int)cur_proc.pid) {
children++;
if (collapsed) {
out_procs.back().cpu_p += p.cpu_p;
out_procs.back().mem += p.mem;
out_procs.back().threads += p.threads;
_tree_gen(p, in_procs, out_procs, cur_depth + 1, collapsed);
}
else _tree_gen(p, in_procs, out_procs, cur_depth + 1, (cache.at(cur_proc.pid).collapsed == 1));
}
}
if (collapsed) return;
if (out_procs.size() > cur_pos + 1 && !out_procs.back().prefix.ends_with("] ")) {
std::string_view n_prefix = out_procs.back().prefix;
n_prefix.remove_suffix(8);
out_procs.back().prefix = (string)n_prefix + " └─ ";
}
string prefix = " ├─ ";
if (children > 0) prefix = (cache.at(cur_proc.pid).collapsed == 1) ? "[+] " : "[-] ";
out_procs.at(cur_pos).prefix = ""s * cur_depth + prefix;
}
vector<proc_info> current_procs;
//* Collects and sorts process information from /proc, saves to and returns reference to Proc::current_procs;
auto& collect(string sorting="pid", bool reverse=false, string filter="", bool per_core=true, bool tree=false){
auto& collect(){
atomic_wait_set(collecting);
auto& sorting = Config::getS("proc_sorting");
auto& reverse = Config::getB("proc_reversed");
auto& filter = Config::getS("proc_filter");
auto& per_core = Config::getB("proc_per_core");
auto& tree = Config::getB("proc_tree");
ifstream pread;
auto uptime = system_uptime();
vector<proc_info> procs;
@ -305,20 +341,19 @@ namespace Proc {
//* Sort processes
std::ranges::sort(procs, [sortint = sort_map.at(sorting), &reverse](proc_info& a, proc_info& b) {
switch (sortint) {
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 2: return (reverse) ? a.cmd < b.cmd : a.cmd > b.cmd;
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 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 7: return (reverse) ? a.cpu_c < b.cpu_c : a.cpu_c > b.cpu_c;
}
return false;
std::ranges::sort(procs, [sortint = v_index(sort_vector, sorting), &reverse](proc_info& a, proc_info& b) {
switch (sortint) {
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 2: return (reverse) ? a.cmd < b.cmd : a.cmd > b.cmd;
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 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 7: return (reverse) ? a.cpu_c < b.cpu_c : a.cpu_c > b.cpu_c;
}
);
return false;
});
//* When using "cpu lazy" sorting push processes with high cpu usage to the front regardless of cumulative usage
if (sorting == "cpu lazy" && !reverse) {
@ -331,6 +366,17 @@ namespace Proc {
}
}
//* Generate tree view if enabled
if (tree) {
auto min_ppid = std::ranges::min(procs, [](proc_info& a, proc_info& b) { return a.ppid < b.ppid; }).ppid;
vector<proc_info> tree_procs;
for (auto& p : procs) {
if (p.ppid == min_ppid) _tree_gen(p, procs, tree_procs);
}
procs.swap(tree_procs);
}
//* Clear dead processes from cache at a regular interval
if (++counter >= 10000 || ((int)cache.size() > npids + 100)) {
counter = 0;
@ -373,9 +419,6 @@ namespace Proc {
clk_tck = 100;
Logger::warning("Could not get system clocks per second. Defaulting to 100, processes cpu usage might be incorrect.");
}
uint i = 0;
for (auto& item : sort_array) sort_map[item] = i++;
}
}