Changed: Rewrite of process sorting and tree generation including fixes for tree sorting and mouse support

This commit is contained in:
aristocratos 2022-07-03 12:37:54 +02:00
parent 102b6dbc9d
commit 6b1b9f8142
8 changed files with 356 additions and 360 deletions

View file

@ -264,6 +264,7 @@ namespace Config {
{"net_upload", 100},
{"detailed_pid", 0},
{"selected_pid", 0},
{"selected_depth", 0},
{"proc_start", 0},
{"proc_selected", 0},
{"proc_last_selected", 0},
@ -472,6 +473,7 @@ namespace Config {
strings.at("selected_name") = Proc::selected_name;
ints.at("proc_start") = Proc::start;
ints.at("proc_selected") = Proc::selected;
ints.at("selected_depth") = Proc::selected_depth;
}
for (auto& item : stringsTmp) {

View file

@ -1022,7 +1022,7 @@ namespace Proc {
int x, y, width = 20, height;
int start, selected, select_max;
bool shown = true, redraw = true;
int selected_pid = 0;
int selected_pid = 0, selected_depth = 0;
string selected_name;
unordered_flat_map<size_t, Draw::Graph> p_graphs;
unordered_flat_map<size_t, bool> p_wide_cmd;
@ -1345,6 +1345,7 @@ namespace Proc {
if (is_selected) {
selected_pid = (int)p.pid;
selected_name = p.name;
selected_depth = p.depth;
}
//? Update graphs for processes with above 0.0% cpu usage, delete if below 0.1% 10x times

View file

@ -354,7 +354,16 @@ namespace Input {
const auto& current_selection = Config::getI("proc_selected");
if (current_selection == line - y - 1) {
redraw = true;
goto proc_mouse_enter;
if (Config::getB("proc_tree")) {
const int x_pos = col - Proc::x;
const int offset = Config::getI("selected_depth") * 3;
if (x_pos > offset and x_pos < 4 + offset) {
process("space");
return;
}
}
process("enter");
return;
}
else if (current_selection == 0 or line - y - 1 == 0)
redraw = true;
@ -380,7 +389,6 @@ namespace Input {
keep_going = true;
}
else if (key == "enter") {
proc_mouse_enter:
if (Config::getI("proc_selected") == 0 and not Config::getB("show_detailed")) {
return;
}

174
src/btop_shared.cpp Normal file
View file

@ -0,0 +1,174 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <ranges>
#include <btop_shared.hpp>
#include <btop_tools.hpp>
using std::string_literals::operator""s;
namespace rng = std::ranges;
using namespace Tools;
namespace Proc {
void proc_sorter(vector<proc_info>& proc_vec, string sorting, const bool reverse, const bool tree) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(proc_vec, rng::less{}, &proc_info::pid); break;
case 1: rng::stable_sort(proc_vec, rng::less{}, &proc_info::name); break;
case 2: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cmd); break;
case 3: rng::stable_sort(proc_vec, rng::less{}, &proc_info::threads); break;
case 4: rng::stable_sort(proc_vec, rng::less{}, &proc_info::user); break;
case 5: rng::stable_sort(proc_vec, rng::less{}, &proc_info::mem); break;
case 6: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cpu_c); break;
}
}
else {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::pid); break;
case 1: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::name); break;
case 2: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cmd); break;
case 3: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::threads); break;
case 4: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::user); break;
case 5: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::mem); break;
case 6: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cpu_c); break;
}
}
//* When sorting with "cpu lazy" push processes over threshold cpu usage to the front regardless of cumulative usage
if (not tree and not reverse and sorting == "cpu lazy") {
double max = 10.0, target = 30.0;
for (size_t i = 0, x = 0, offset = 0; i < proc_vec.size(); i++) {
if (i <= 5 and proc_vec.at(i).cpu_p > max)
max = proc_vec.at(i).cpu_p;
else if (i == 6)
target = (max > 30.0) ? max : 10.0;
if (i == offset and proc_vec.at(i).cpu_p > 30.0)
offset++;
else if (proc_vec.at(i).cpu_p > target) {
rotate(proc_vec.begin() + offset, proc_vec.begin() + i, proc_vec.begin() + i + 1);
if (++x > 10) break;
}
}
}
}
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, const bool reverse, int& c_index, const int index_max, const bool collapsed) {
if (proc_vec.size() > 1) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 3: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().threads < b.entry.get().threads; }); break;
case 5: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().mem < b.entry.get().mem; }); break;
case 6: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_p < b.entry.get().cpu_p; }); break;
case 7: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_c < b.entry.get().cpu_c; }); break;
}
}
else {
switch (v_index(sort_vector, sorting)) {
case 3: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().threads > b.entry.get().threads; }); break;
case 5: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().mem > b.entry.get().mem; }); break;
case 6: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_p > b.entry.get().cpu_p; }); break;
case 7: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_c > b.entry.get().cpu_c; }); break;
}
}
}
for (auto& r : proc_vec) {
r.entry.get().tree_index = (collapsed or r.entry.get().filtered ? index_max : c_index++);
if (not r.children.empty()) {
tree_sort(r.children, sorting, reverse, c_index, (collapsed or r.entry.get().collapsed or r.entry.get().tree_index == (size_t)index_max));
}
}
}
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found, const bool no_update, const bool should_filter) {
auto cur_pos = out_procs.size();
bool filtering = false;
//? If filtering, include children of matching processes
if (not found and (should_filter or not filter.empty())) {
if (not s_contains(std::to_string(cur_proc.pid), filter)
and not s_contains(cur_proc.name, filter)
and not s_contains(cur_proc.cmd, filter)
and not s_contains(cur_proc.user, filter)) {
filtering = true;
cur_proc.filtered = true;
filter_found++;
}
else {
found = true;
cur_depth = 0;
}
}
else if (cur_proc.filtered) cur_proc.filtered = false;
cur_proc.depth = cur_depth;
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
out_procs.push_back({ cur_proc, {} });
if (not collapsed and not filtering) {
cur_proc.tree_index = out_procs.size() - 1;
//? Try to find name of the binary file and append to program name if not the same
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
std::string_view cmd_view = cur_proc.cmd;
cmd_view = cmd_view.substr((size_t)0, std::min(cmd_view.find(' '), cmd_view.size()));
cmd_view = cmd_view.substr(std::min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
cur_proc.short_cmd = (string)cmd_view;
}
}
else {
cur_proc.tree_index = in_procs.size();
}
//? Recursive iteration over all children
int children = 0;
for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
if (collapsed and not filtering) {
cur_proc.filtered = true;
}
children++;
_tree_gen(p, in_procs, out_procs.back().children, cur_depth + 1, (collapsed or cur_proc.collapsed), filter, found, no_update, should_filter);
if (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
//auto& parent = cur_proc;
cur_proc.cpu_p += p.cpu_p;
cur_proc.cpu_c += p.cpu_c;
cur_proc.mem += p.mem;
cur_proc.threads += p.threads;
filter_found++;
p.filtered = true;
}
}
if (collapsed or filtering) {
return;
}
//? Add tree terminator symbol if it's the last child in a sub-tree
if (children > 0 and out_procs.back().children.back().entry.get().prefix.size() >= 8 and not out_procs.back().children.back().entry.get().prefix.ends_with("]─"))
out_procs.back().children.back().entry.get().prefix.replace(out_procs.back().children.back().entry.get().prefix.size() - 8, 8, " └─ ");
//? Add collapse/expand symbols if process have any children
out_procs.at(cur_pos).entry.get().prefix = ""s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
}
}

View file

@ -131,7 +131,7 @@ namespace Mem {
struct disk_info {
std::filesystem::path dev;
string name;
string fstype;
string fstype = "";
std::filesystem::path stat = "";
int64_t total = 0, used = 0, free = 0;
int used_percent = 0, free_percent = 0;
@ -199,7 +199,7 @@ namespace Proc {
extern bool shown, redraw;
extern int select_max;
extern atomic<int> detailed_pid;
extern int selected_pid, start, selected, collapse, expand;
extern int selected_pid, start, selected, collapse, expand, filter_found, selected_depth;
extern string selected_name;
//? Contains the valid sorting options for processes
@ -268,4 +268,18 @@ namespace Proc {
//* Draw contents of proc box using <plist> as data source
string draw(const vector<proc_info>& plist, const bool force_redraw=false, const bool data_same=false);
struct tree_proc {
std::reference_wrapper<proc_info> entry;
vector<tree_proc> children;
};
//* Sort vector of proc_info's
void proc_sorter(vector<proc_info>& proc_vec, string sorting, const bool reverse, const bool tree = false);
//* Recursive sort of process tree
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, const bool reverse, int& c_index, const int index_max, const bool collapsed = false);
//* Generate process tree list
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false, const bool no_update=false, const bool should_filter=false);
}

View file

@ -1007,65 +1007,6 @@ namespace Proc {
detail_container detailed;
//* Generate process tree list
void _tree_gen(proc_info &cur_proc, vector<proc_info> &in_procs, vector<std::reference_wrapper<proc_info>> &out_procs, int cur_depth, const bool collapsed, const string &filter, bool found = false, const bool no_update = false, const bool should_filter = false) {
auto cur_pos = out_procs.size();
bool filtering = false;
//? If filtering, include children of matching processes
if (not found and (should_filter or not filter.empty())) {
if (not s_contains(std::to_string(cur_proc.pid), filter) and not s_contains(cur_proc.name, filter) and not s_contains(cur_proc.cmd, filter) and not s_contains(cur_proc.user, filter)) {
filtering = true;
cur_proc.filtered = true;
filter_found++;
} else {
found = true;
cur_depth = 0;
}
} else if (cur_proc.filtered)
cur_proc.filtered = false;
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
if (not collapsed and not filtering) {
out_procs.push_back(std::ref(cur_proc));
cur_proc.tree_index = out_procs.size() - 1;
//? Try to find name of the binary file and append to program name if not the same
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
std::string_view cmd_view = cur_proc.cmd;
cmd_view = cmd_view.substr((size_t)0, min(cmd_view.find(' '), cmd_view.size()));
cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
cur_proc.short_cmd = (string)cmd_view;
}
} else {
cur_proc.tree_index = in_procs.size();
}
//? Recursive iteration over all children
int children = 0;
for (auto &p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
if (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
out_procs.back().get().cpu_p += p.cpu_p;
out_procs.back().get().mem += p.mem;
out_procs.back().get().threads += p.threads;
filter_found++;
}
if (collapsed and not filtering) {
cur_proc.filtered = true;
} else
children++;
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cur_proc.collapsed), filter, found, no_update, should_filter);
}
if (collapsed or filtering)
return;
//? Add tree terminator symbol if it's the last child in a sub-tree
if (out_procs.size() > cur_pos + 1 and not out_procs.back().get().prefix.ends_with("]─"))
out_procs.back().get().prefix.replace(out_procs.back().get().prefix.size() - 8, 8, " └─ ");
//? Add collapse/expand symbols if process have any children
out_procs.at(cur_pos).get().prefix = ""s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
}
string get_status(char s) {
if (s & SRUN) return "Running";
if (s & SSLEEP) return "Sleeping";
@ -1156,6 +1097,8 @@ namespace Proc {
const int cmult = (per_core) ? Shared::coreCount : 1;
bool got_detailed = false;
static vector<size_t> found;
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, NULL, 0) == -1) {
@ -1175,7 +1118,7 @@ namespace Proc {
//* ---------------------------------------------Collection start----------------------------------------------
should_filter = true;
vector<size_t> found;
found.clear();
struct timeval currentTime;
gettimeofday(&currentTime, NULL);
const double timeNow = currentTime.tv_sec + (currentTime.tv_usec / 1'000'000);
@ -1206,6 +1149,7 @@ namespace Proc {
if (no_cache) {
if (kproc->ki_comm == NULL or kproc->ki_comm == "idle"s) {
current_procs.pop_back();
found.pop_back();
continue;
}
new_proc.name = kproc->ki_comm;
@ -1249,116 +1193,111 @@ namespace Proc {
if (show_detailed and not got_detailed and new_proc.pid == detailed_pid) {
got_detailed = true;
}
// //? Clear dead processes from current_procs
auto eraser = rng::remove_if(current_procs, [&](const auto &element) { return not v_contains(found, element.pid); });
current_procs.erase(eraser.begin(), eraser.end());
//? Update the details info box for process if active
if (show_detailed and got_detailed) {
_collect_details(detailed_pid, current_procs);
} else if (show_detailed and not got_detailed and detailed.status != "Dead") {
detailed.status = "Dead";
redraw = true;
}
old_cputimes = cputimes;
}
//? Clear dead processes from current_procs
auto eraser = rng::remove_if(current_procs, [&](const auto &element) { return not v_contains(found, element.pid); });
current_procs.erase(eraser.begin(), eraser.end());
//? Update the details info box for process if active
if (show_detailed and got_detailed) {
_collect_details(detailed_pid, current_procs);
} else if (show_detailed and not got_detailed and detailed.status != "Dead") {
detailed.status = "Dead";
redraw = true;
}
old_cputimes = cputimes;
}
//* ---------------------------------------------Collection done-----------------------------------------------
//* Sort processes
if (sorted_change or not no_update) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(current_procs, rng::less{}, &proc_info::pid); break;
case 1: rng::stable_sort(current_procs, rng::less{}, &proc_info::name); break;
case 2: rng::stable_sort(current_procs, rng::less{}, &proc_info::cmd); break;
case 3: rng::stable_sort(current_procs, rng::less{}, &proc_info::threads); break;
case 4: rng::stable_sort(current_procs, rng::less{}, &proc_info::user); break;
case 5: rng::stable_sort(current_procs, rng::less{}, &proc_info::mem); break;
case 6: rng::stable_sort(current_procs, rng::less{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(current_procs, rng::less{}, &proc_info::cpu_c); break;
}
} else {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(current_procs, rng::greater{}, &proc_info::pid); break;
case 1: rng::stable_sort(current_procs, rng::greater{}, &proc_info::name); break;
case 2: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cmd); break;
case 3: rng::stable_sort(current_procs, rng::greater{}, &proc_info::threads); break;
case 4: rng::stable_sort(current_procs, rng::greater{}, &proc_info::user); break;
case 5: rng::stable_sort(current_procs, rng::greater{}, &proc_info::mem); break;
case 6: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cpu_c); break;
}
}
//* When sorting with "cpu lazy" push processes over threshold cpu usage to the front regardless of cumulative usage
if (not tree and not reverse and sorting == "cpu lazy") {
double max = 10.0, target = 30.0;
for (size_t i = 0, x = 0, offset = 0; i < current_procs.size(); i++) {
if (i <= 5 and current_procs.at(i).cpu_p > max)
max = current_procs.at(i).cpu_p;
else if (i == 6)
target = (max > 30.0) ? max : 10.0;
if (i == offset and current_procs.at(i).cpu_p > 30.0)
offset++;
else if (current_procs.at(i).cpu_p > target) {
rotate(current_procs.begin() + offset, current_procs.begin() + i, current_procs.begin() + i + 1);
if (++x > 10) break;
}
}
}
}
//* Match filter if defined
if (should_filter) {
filter_found = 0;
for (auto &p : current_procs) {
for (auto& p : current_procs) {
if (not tree and not filter.empty()) {
if (not s_contains_ic(to_string(p.pid), filter) and not s_contains_ic(p.name, filter) and not s_contains_ic(p.cmd, filter) and not s_contains_ic(p.user, filter)) {
p.filtered = true;
filter_found++;
} else {
p.filtered = false;
if (not s_contains_ic(to_string(p.pid), filter)
and not s_contains_ic(p.name, filter)
and not s_contains_ic(p.cmd, filter)
and not s_contains_ic(p.user, filter)) {
p.filtered = true;
filter_found++;
}
else {
p.filtered = false;
}
}
} else {
else {
p.filtered = false;
}
}
}
//* Sort processes
if (sorted_change or not no_update) {
proc_sorter(current_procs, sorting, reverse, tree);
}
//* Generate tree view if enabled
if (tree and (not no_update or should_filter or sorted_change)) {
bool locate_selection = false;
if (auto find_pid = (collapse != -1 ? collapse : expand); find_pid != -1) {
auto collapser = rng::find(current_procs, find_pid, &proc_info::pid);
if (collapser != current_procs.end()) {
if (collapse == expand) {
collapser->collapsed = not collapser->collapsed;
} else if (collapse > -1) {
}
else if (collapse > -1) {
collapser->collapsed = true;
} else if (expand > -1) {
}
else if (expand > -1) {
collapser->collapsed = false;
}
if (Config::ints.at("proc_selected") > 0) locate_selection = true;
}
collapse = expand = -1;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<std::reference_wrapper<proc_info>> tree_procs;
vector<tree_proc> tree_procs;
tree_procs.reserve(current_procs.size());
for (auto& p : current_procs) {
if (not v_contains(found, p.ppid)) p.ppid = 0;
}
//? Stable sort to retain selected sorting among processes with the same parent
rng::stable_sort(current_procs, rng::less{}, &proc_info::ppid);
rng::stable_sort(current_procs, rng::less{}, & proc_info::ppid);
//? Start recursive iteration over processes with the lowest shared parent pids
for (auto &p : rng::equal_range(current_procs, current_procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
for (auto& p : rng::equal_range(current_procs, current_procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
_tree_gen(p, current_procs, tree_procs, 0, false, filter, false, no_update, should_filter);
}
//? Recursive sort over tree structure to account for collapsed processes in the tree
int index = 0;
tree_sort(tree_procs, sorting, reverse, index, current_procs.size());
//? Add tree begin symbol to first item if childless
if (tree_procs.front().children.empty())
tree_procs.front().entry.get().prefix.replace(tree_procs.front().entry.get().prefix.size() - 8, 8, " ┌─ ");
//? Add tree terminator symbol to last item if childless
if (tree_procs.back().children.empty())
tree_procs.back().entry.get().prefix.replace(tree_procs.back().entry.get().prefix.size() - 8, 8, " └─ ");
//? Final sort based on tree index
rng::stable_sort(current_procs, rng::less{}, &proc_info::tree_index);
rng::sort(current_procs, rng::less{}, & proc_info::tree_index);
//? Move current selection/view to the selected process when collapsing/expanding in the tree
if (locate_selection) {
int loc = rng::find(current_procs, Proc::selected_pid, &proc_info::pid)->tree_index;
if (Config::ints.at("proc_start") >= loc or Config::ints.at("proc_start") <= loc - Proc::select_max)
Config::ints.at("proc_start") = max(0, loc - 1);
Config::ints.at("proc_selected") = loc - Config::ints.at("proc_start") + 1;
}
}
numpids = (int)current_procs.size() - filter_found;

View file

@ -1307,69 +1307,6 @@ namespace Proc {
constexpr size_t KTHREADD = 2;
static robin_hood::unordered_set<size_t> kernels_procs = {KTHREADD};
//* Generate process tree list
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<std::reference_wrapper<proc_info>>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false, const bool no_update=false, const bool should_filter=false) {
auto cur_pos = out_procs.size();
bool filtering = false;
//? If filtering, include children of matching processes
if (not found and (should_filter or not filter.empty())) {
if (not s_contains(std::to_string(cur_proc.pid), filter)
and not s_contains(cur_proc.name, filter)
and not s_contains(cur_proc.cmd, filter)
and not s_contains(cur_proc.user, filter)) {
filtering = true;
cur_proc.filtered = true;
filter_found++;
}
else {
found = true;
cur_depth = 0;
}
}
else if (cur_proc.filtered) cur_proc.filtered = false;
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
if (not collapsed and not filtering) {
out_procs.push_back(std::ref(cur_proc));
cur_proc.tree_index = out_procs.size() - 1;
//? Try to find name of the binary file and append to program name if not the same
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
std::string_view cmd_view = cur_proc.cmd;
cmd_view = cmd_view.substr((size_t)0, min(cmd_view.find(' '), cmd_view.size()));
cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
cur_proc.short_cmd = (string)cmd_view;
}
}
else {
cur_proc.tree_index = in_procs.size();
}
//? Recursive iteration over all children
int children = 0;
for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
if (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
out_procs.back().get().cpu_p += p.cpu_p;
out_procs.back().get().mem += p.mem;
out_procs.back().get().threads += p.threads;
filter_found++;
}
if (collapsed and not filtering) {
cur_proc.filtered = true;
}
else children++;
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cur_proc.collapsed), filter, found, no_update, should_filter);
}
if (collapsed or filtering) return;
//? Add tree terminator symbol if it's the last child in a sub-tree
if (out_procs.size() > cur_pos + 1 and not out_procs.back().get().prefix.ends_with("]─"))
out_procs.back().get().prefix.replace(out_procs.back().get().prefix.size() - 8, 8, " └─ ");
//? Add collapse/expand symbols if process have any children
out_procs.at(cur_pos).get().prefix = ""s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
}
//* Get detailed info for selected process
void _collect_details(const size_t pid, const uint64_t uptime, vector<proc_info>& procs) {
fs::path pid_path = Shared::procPath / std::to_string(pid);
@ -1488,6 +1425,8 @@ namespace Proc {
string long_string;
string short_str;
static vector<size_t> found;
const double uptime = system_uptime();
const int cmult = (per_core) ? Shared::coreCount : 1;
@ -1502,10 +1441,11 @@ namespace Proc {
//* ---------------------------------------------Collection start----------------------------------------------
else {
should_filter = true;
found.clear();
//? First make sure kernel proc cache is cleared.
if (should_filter_kernel and ++proc_clear_count >= 256) {
//? Clearing the cache is used in the event of a pid wrap around.
//? Clearing the cache is used in the event of a pid wrap around.
//? In that event processes that acquire old kernel pids would also be filtered out so we need to manually clean the cache every now and then.
kernels_procs.clear();
kernels_procs.emplace(KTHREADD);
@ -1547,7 +1487,6 @@ namespace Proc {
pread.close();
//? Iterate over all pids in /proc
vector<size_t> found;
for (const auto& d: fs::directory_iterator(Shared::procPath)) {
if (Runner::stopping)
return current_procs;
@ -1557,7 +1496,7 @@ namespace Proc {
if (not isdigit(pid_str[0])) continue;
const size_t pid = stoul(pid_str);
if (should_filter_kernel and kernels_procs.contains(pid)) {
continue;
}
@ -1694,7 +1633,7 @@ namespace Proc {
catch (const std::out_of_range&) { continue; }
pread.close();
if (should_filter_kernel and new_proc.ppid == KTHREADD) {
kernels_procs.emplace(new_proc.pid);
found.pop_back();
@ -1743,50 +1682,6 @@ namespace Proc {
}
//* ---------------------------------------------Collection done-----------------------------------------------
//* Sort processes
if (sorted_change or not no_update) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(current_procs, rng::less{}, &proc_info::pid); break;
case 1: rng::stable_sort(current_procs, rng::less{}, &proc_info::name); break;
case 2: rng::stable_sort(current_procs, rng::less{}, &proc_info::cmd); break;
case 3: rng::stable_sort(current_procs, rng::less{}, &proc_info::threads); break;
case 4: rng::stable_sort(current_procs, rng::less{}, &proc_info::user); break;
case 5: rng::stable_sort(current_procs, rng::less{}, &proc_info::mem); break;
case 6: rng::stable_sort(current_procs, rng::less{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(current_procs, rng::less{}, &proc_info::cpu_c); break;
}
} else {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(current_procs, rng::greater{}, &proc_info::pid); break;
case 1: rng::stable_sort(current_procs, rng::greater{}, &proc_info::name); break;
case 2: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cmd); break;
case 3: rng::stable_sort(current_procs, rng::greater{}, &proc_info::threads); break;
case 4: rng::stable_sort(current_procs, rng::greater{}, &proc_info::user); break;
case 5: rng::stable_sort(current_procs, rng::greater{}, &proc_info::mem); break;
case 6: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cpu_c); break;
}
}
//* When sorting with "cpu lazy" push processes over threshold cpu usage to the front regardless of cumulative usage
if (not tree and not reverse and sorting == "cpu lazy") {
double max = 10.0, target = 30.0;
for (size_t i = 0, x = 0, offset = 0; i < current_procs.size(); i++) {
if (i <= 5 and current_procs.at(i).cpu_p > max)
max = current_procs.at(i).cpu_p;
else if (i == 6)
target = (max > 30.0) ? max : 10.0;
if (i == offset and current_procs.at(i).cpu_p > 30.0)
offset++;
else if (current_procs.at(i).cpu_p > target) {
rotate(current_procs.begin() + offset, current_procs.begin() + i, current_procs.begin() + i + 1);
if (++x > 10) break;
}
}
}
}
//* Match filter if defined
if (should_filter) {
filter_found = 0;
@ -1809,8 +1704,14 @@ namespace Proc {
}
}
//* Sort processes
if (sorted_change or not no_update) {
proc_sorter(current_procs, sorting, reverse, tree);
}
//* Generate tree view if enabled
if (tree and (not no_update or should_filter or sorted_change)) {
bool locate_selection = false;
if (auto find_pid = (collapse != -1 ? collapse : expand); find_pid != -1) {
auto collapser = rng::find(current_procs, find_pid, &proc_info::pid);
if (collapser != current_procs.end()) {
@ -1823,24 +1724,49 @@ namespace Proc {
else if (expand > -1) {
collapser->collapsed = false;
}
if (Config::ints.at("proc_selected") > 0) locate_selection = true;
}
collapse = expand = -1;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<std::reference_wrapper<proc_info>> tree_procs;
vector<tree_proc> tree_procs;
tree_procs.reserve(current_procs.size());
for (auto& p : current_procs) {
if (not v_contains(found, p.ppid)) p.ppid = 0;
}
//? Stable sort to retain selected sorting among processes with the same parent
rng::stable_sort(current_procs, rng::less{}, &proc_info::ppid);
rng::stable_sort(current_procs, rng::less{}, & proc_info::ppid);
//? Start recursive iteration over processes with the lowest shared parent pids
for (auto& p : rng::equal_range(current_procs, current_procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
_tree_gen(p, current_procs, tree_procs, 0, false, filter, false, no_update, should_filter);
}
//? Recursive sort over tree structure to account for collapsed processes in the tree
int index = 0;
tree_sort(tree_procs, sorting, reverse, index, current_procs.size());
//? Add tree begin symbol to first item if childless
if (tree_procs.front().children.empty())
tree_procs.front().entry.get().prefix.replace(tree_procs.front().entry.get().prefix.size() - 8, 8, " ┌─ ");
//? Add tree terminator symbol to last item if childless
if (tree_procs.back().children.empty())
tree_procs.back().entry.get().prefix.replace(tree_procs.back().entry.get().prefix.size() - 8, 8, " └─ ");
//? Final sort based on tree index
rng::stable_sort(current_procs, rng::less{}, &proc_info::tree_index);
rng::sort(current_procs, rng::less{}, & proc_info::tree_index);
//? Move current selection/view to the selected process when collapsing/expanding in the tree
if (locate_selection) {
int loc = rng::find(current_procs, Proc::selected_pid, &proc_info::pid)->tree_index;
if (Config::ints.at("proc_start") >= loc or Config::ints.at("proc_start") <= loc - Proc::select_max)
Config::ints.at("proc_start") = max(0, loc - 1);
Config::ints.at("proc_selected") = loc - Config::ints.at("proc_start") + 1;
}
}
numpids = (int)current_procs.size() - filter_found;

View file

@ -1046,65 +1046,6 @@ namespace Proc {
detail_container detailed;
//* Generate process tree list
void _tree_gen(proc_info &cur_proc, vector<proc_info> &in_procs, vector<std::reference_wrapper<proc_info>> &out_procs, int cur_depth, const bool collapsed, const string &filter, bool found = false, const bool no_update = false, const bool should_filter = false) {
auto cur_pos = out_procs.size();
bool filtering = false;
//? If filtering, include children of matching processes
if (not found and (should_filter or not filter.empty())) {
if (not s_contains(std::to_string(cur_proc.pid), filter) and not s_contains(cur_proc.name, filter) and not s_contains(cur_proc.cmd, filter) and not s_contains(cur_proc.user, filter)) {
filtering = true;
cur_proc.filtered = true;
filter_found++;
} else {
found = true;
cur_depth = 0;
}
} else if (cur_proc.filtered)
cur_proc.filtered = false;
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
if (not collapsed and not filtering) {
out_procs.push_back(std::ref(cur_proc));
cur_proc.tree_index = out_procs.size() - 1;
//? Try to find name of the binary file and append to program name if not the same
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
std::string_view cmd_view = cur_proc.cmd;
cmd_view = cmd_view.substr((size_t)0, min(cmd_view.find(' '), cmd_view.size()));
cmd_view = cmd_view.substr(min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
cur_proc.short_cmd = (string)cmd_view;
}
} else {
cur_proc.tree_index = in_procs.size();
}
//? Recursive iteration over all children
int children = 0;
for (auto &p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
if (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
out_procs.back().get().cpu_p += p.cpu_p;
out_procs.back().get().mem += p.mem;
out_procs.back().get().threads += p.threads;
filter_found++;
}
if (collapsed and not filtering) {
cur_proc.filtered = true;
} else
children++;
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cur_proc.collapsed), filter, found, no_update, should_filter);
}
if (collapsed or filtering)
return;
//? Add tree terminator symbol if it's the last child in a sub-tree
if (out_procs.size() > cur_pos + 1 and not out_procs.back().get().prefix.ends_with("]─"))
out_procs.back().get().prefix.replace(out_procs.back().get().prefix.size() - 8, 8, " └─ ");
//? Add collapse/expand symbols if process have any children
out_procs.at(cur_pos).get().prefix = ""s * cur_depth + (children > 0 ? (cur_proc.collapsed ? "[+]─" : "[-]─") : " ├─ ");
}
string get_status(char s) {
if (s & SRUN) return "Running";
if (s & SSLEEP) return "Sleeping";
@ -1184,6 +1125,8 @@ namespace Proc {
const int cmult = (per_core) ? Shared::coreCount : 1;
bool got_detailed = false;
static vector<size_t> found;
//* Use pids from last update if only changing filter, sorting or tree options
if (no_update and not current_procs.empty()) {
if (show_detailed and detailed_pid != detailed.last_pid) _collect_details(detailed_pid, current_procs);
@ -1211,7 +1154,7 @@ namespace Proc {
should_filter = true;
int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
vector<size_t> found;
found.clear();
size_t size = 0;
const auto timeNow = time_micros();
@ -1323,50 +1266,6 @@ namespace Proc {
//* ---------------------------------------------Collection done-----------------------------------------------
//* Sort processes
if (sorted_change or not no_update) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(current_procs, rng::less{}, &proc_info::pid); break;
case 1: rng::stable_sort(current_procs, rng::less{}, &proc_info::name); break;
case 2: rng::stable_sort(current_procs, rng::less{}, &proc_info::cmd); break;
case 3: rng::stable_sort(current_procs, rng::less{}, &proc_info::threads); break;
case 4: rng::stable_sort(current_procs, rng::less{}, &proc_info::user); break;
case 5: rng::stable_sort(current_procs, rng::less{}, &proc_info::mem); break;
case 6: rng::stable_sort(current_procs, rng::less{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(current_procs, rng::less{}, &proc_info::cpu_c); break;
}
} else {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(current_procs, rng::greater{}, &proc_info::pid); break;
case 1: rng::stable_sort(current_procs, rng::greater{}, &proc_info::name); break;
case 2: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cmd); break;
case 3: rng::stable_sort(current_procs, rng::greater{}, &proc_info::threads); break;
case 4: rng::stable_sort(current_procs, rng::greater{}, &proc_info::user); break;
case 5: rng::stable_sort(current_procs, rng::greater{}, &proc_info::mem); break;
case 6: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(current_procs, rng::greater{}, &proc_info::cpu_c); break;
}
}
//* When sorting with "cpu lazy" push processes over threshold cpu usage to the front regardless of cumulative usage
if (not tree and not reverse and sorting == "cpu lazy") {
double max = 10.0, target = 30.0;
for (size_t i = 0, x = 0, offset = 0; i < current_procs.size(); i++) {
if (i <= 5 and current_procs.at(i).cpu_p > max)
max = current_procs.at(i).cpu_p;
else if (i == 6)
target = (max > 30.0) ? max : 10.0;
if (i == offset and current_procs.at(i).cpu_p > 30.0)
offset++;
else if (current_procs.at(i).cpu_p > target) {
rotate(current_procs.begin() + offset, current_procs.begin() + i, current_procs.begin() + i + 1);
if (++x > 10) break;
}
}
}
}
//* Match filter if defined
if (should_filter) {
filter_found = 0;
@ -1384,36 +1283,69 @@ namespace Proc {
}
}
//* Sort processes
if (sorted_change or not no_update) {
proc_sorter(current_procs, sorting, reverse, tree);
}
//* Generate tree view if enabled
if (tree and (not no_update or should_filter or sorted_change)) {
bool locate_selection = false;
if (auto find_pid = (collapse != -1 ? collapse : expand); find_pid != -1) {
auto collapser = rng::find(current_procs, find_pid, &proc_info::pid);
if (collapser != current_procs.end()) {
if (collapse == expand) {
collapser->collapsed = not collapser->collapsed;
} else if (collapse > -1) {
}
else if (collapse > -1) {
collapser->collapsed = true;
} else if (expand > -1) {
}
else if (expand > -1) {
collapser->collapsed = false;
}
if (Config::ints.at("proc_selected") > 0) locate_selection = true;
}
collapse = expand = -1;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<std::reference_wrapper<proc_info>> tree_procs;
vector<tree_proc> tree_procs;
tree_procs.reserve(current_procs.size());
for (auto& p : current_procs) {
if (not v_contains(found, p.ppid)) p.ppid = 0;
}
//? Stable sort to retain selected sorting among processes with the same parent
rng::stable_sort(current_procs, rng::less{}, &proc_info::ppid);
rng::stable_sort(current_procs, rng::less{}, & proc_info::ppid);
//? Start recursive iteration over processes with the lowest shared parent pids
for (auto &p : rng::equal_range(current_procs, current_procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
for (auto& p : rng::equal_range(current_procs, current_procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
_tree_gen(p, current_procs, tree_procs, 0, false, filter, false, no_update, should_filter);
}
//? Recursive sort over tree structure to account for collapsed processes in the tree
int index = 0;
tree_sort(tree_procs, sorting, reverse, index, current_procs.size());
//? Add tree begin symbol to first item if childless
if (tree_procs.front().children.empty())
tree_procs.front().entry.get().prefix.replace(tree_procs.front().entry.get().prefix.size() - 8, 8, " ┌─ ");
//? Add tree terminator symbol to last item if childless
if (tree_procs.back().children.empty())
tree_procs.back().entry.get().prefix.replace(tree_procs.back().entry.get().prefix.size() - 8, 8, " └─ ");
//? Final sort based on tree index
rng::stable_sort(current_procs, rng::less{}, &proc_info::tree_index);
rng::sort(current_procs, rng::less{}, & proc_info::tree_index);
//? Move current selection/view to the selected process when collapsing/expanding in the tree
if (locate_selection) {
int loc = rng::find(current_procs, Proc::selected_pid, &proc_info::pid)->tree_index;
if (Config::ints.at("proc_start") >= loc or Config::ints.at("proc_start") <= loc - Proc::select_max)
Config::ints.at("proc_start") = max(0, loc - 1);
Config::ints.at("proc_selected") = loc - Config::ints.at("proc_start") + 1;
}
}
numpids = (int)current_procs.size() - filter_found;