mirror of
https://github.com/aristocratos/btop.git
synced 2025-12-23 22:29:08 -05:00
Merge pull request #1306 from vandabbin/pause_proc_list
This commit is contained in:
@@ -193,6 +193,7 @@ C++ version and continuation of [bashtop](https://github.com/aristocratos/bashto
|
||||
* Easy switching between sorting options.
|
||||
* Tree view of processes.
|
||||
* Send any signal to selected process.
|
||||
* Pause the process list.
|
||||
* UI menu for changing all config file options.
|
||||
* Auto scaling graph for network usage.
|
||||
* Shows IO activity and speeds for disks.
|
||||
@@ -1355,6 +1356,9 @@ proc_per_core = True
|
||||
#* Show process memory as bytes instead of percent.
|
||||
proc_mem_bytes = True
|
||||
|
||||
#* Choose to preserve last cpu and memory usage of dead processes for when paused.
|
||||
keep_dead_proc_usage = False
|
||||
|
||||
#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate)
|
||||
proc_info_smaps = False
|
||||
|
||||
|
||||
@@ -118,6 +118,8 @@ namespace Config {
|
||||
|
||||
{"proc_aggregate", "#* In tree-view, always accumulate child process resources in the parent process."},
|
||||
|
||||
{"keep_dead_proc_usage", "#* Should cpu and memory usage display be preserved for dead processes when paused."},
|
||||
|
||||
{"cpu_graph_upper", "#* Sets the CPU stat shown in upper half of the CPU graph, \"total\" is always available.\n"
|
||||
"#* Select from a list of detected attributes from the options menu."},
|
||||
|
||||
@@ -322,6 +324,8 @@ namespace Config {
|
||||
{"show_detailed", false},
|
||||
{"proc_filtering", false},
|
||||
{"proc_aggregate", false},
|
||||
{"pause_proc_list", false},
|
||||
{"keep_dead_proc_usage", false},
|
||||
#ifdef GPU_SUPPORT
|
||||
{"nvml_measure_pcie_speeds", true},
|
||||
{"rsmi_measure_pcie_speeds", true},
|
||||
|
||||
@@ -1545,7 +1545,8 @@ namespace Proc {
|
||||
auto start = Config::getI("proc_start");
|
||||
auto selected = Config::getI("proc_selected");
|
||||
auto last_selected = Config::getI("proc_last_selected");
|
||||
const int select_max = (Config::getB("show_detailed") ? Proc::select_max - 8 : Proc::select_max);
|
||||
const int select_max = (Config::getB("show_detailed") ? (Config::getB("pause_proc_list") ? Proc::select_max - 9 : Proc::select_max - 8) :
|
||||
(Config::getB("pause_proc_list") ? Proc::select_max - 1 : Proc::select_max));
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
|
||||
int numpids = Proc::numpids;
|
||||
@@ -1613,11 +1614,13 @@ namespace Proc {
|
||||
auto mem_bytes = Config::getB("proc_mem_bytes");
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
auto show_graphs = Config::getB("proc_cpu_graphs");
|
||||
const auto pause_proc_list = Config::getB("pause_proc_list");
|
||||
start = Config::getI("proc_start");
|
||||
selected = Config::getI("proc_selected");
|
||||
const int y = show_detailed ? Proc::y + 8 : Proc::y;
|
||||
const int height = show_detailed ? Proc::height - 8 : Proc::height;
|
||||
const int select_max = show_detailed ? Proc::select_max - 8 : Proc::select_max;
|
||||
const int select_max = show_detailed ? (pause_proc_list ? Proc::select_max - 9 : Proc::select_max - 8) :
|
||||
(pause_proc_list ? Proc::select_max - 1 : Proc::select_max);
|
||||
auto totalMem = Mem::get_totalMem();
|
||||
int numpids = Proc::numpids;
|
||||
if (force_redraw) redraw = true;
|
||||
@@ -1655,7 +1658,7 @@ namespace Proc {
|
||||
d_y = Proc::y;
|
||||
|
||||
//? Create cpu and mem graphs if process is alive
|
||||
if (alive) {
|
||||
if (alive or pause_proc_list) {
|
||||
detailed_cpu_graph = Draw::Graph{dgraph_width - 1, 7, "cpu", detailed.cpu_percent, graph_symbol, false, true};
|
||||
detailed_mem_graph = Draw::Graph{d_width / 3, 1, "", detailed.mem_bytes, graph_symbol, false, false, detailed.first_mem};
|
||||
}
|
||||
@@ -1812,8 +1815,8 @@ namespace Proc {
|
||||
const int item_width = floor((double)(d_width - 2) / min(item_fit, 8));
|
||||
|
||||
//? Graph part of box
|
||||
string cpu_str = (alive ? fmt::format("{:.2f}", detailed.entry.cpu_p) : "");
|
||||
if (alive) {
|
||||
string cpu_str = (alive or pause_proc_list ? fmt::format("{:.2f}", detailed.entry.cpu_p) : "");
|
||||
if (alive or pause_proc_list) {
|
||||
cpu_str.resize(4);
|
||||
if (cpu_str.ends_with('.')) { cpu_str.pop_back(); cpu_str.pop_back(); }
|
||||
}
|
||||
@@ -1979,10 +1982,18 @@ namespace Proc {
|
||||
+ (p_graphs.contains(p.pid) ? Mv::l(5) + c_color + p_graphs.at(p.pid)({(p.cpu_p >= 0.1 and p.cpu_p < 5 ? 5ll : (long long)round(p.cpu_p))}, data_same) : "") + end + ' '
|
||||
+ c_color + rjust(cpu_str, 4) + " " + end;
|
||||
if (lc++ > height - 5) break;
|
||||
else if (lc > height - 5 and pause_proc_list) break;
|
||||
}
|
||||
|
||||
out += Fx::reset;
|
||||
while (lc++ < height - 3) out += Mv::to(y+lc+1, x+1) + string(width - 2, ' ');
|
||||
if (pause_proc_list) {
|
||||
fmt::format_to(std::back_inserter(out), "{}{}{}{}{:^{}}{}",
|
||||
Mv::to(y + height - 2, x + 1),
|
||||
Theme::c("proc_pause_bg"), Theme::c("title"),
|
||||
Fx::b, "Process list paused", width - 2,
|
||||
Fx::reset);
|
||||
}
|
||||
|
||||
//? Draw scrollbar if needed
|
||||
if (numpids > select_max) {
|
||||
|
||||
@@ -327,7 +327,10 @@ namespace Input {
|
||||
Config::flip("proc_tree");
|
||||
no_update = false;
|
||||
}
|
||||
|
||||
else if (is_in(key, "F")) {
|
||||
Config::flip("pause_proc_list");
|
||||
redraw = true;
|
||||
}
|
||||
else if (key == "r")
|
||||
Config::flip("proc_reversed");
|
||||
|
||||
|
||||
@@ -195,6 +195,7 @@ namespace Menu {
|
||||
{"a", "Toggle auto scaling for the network graphs."},
|
||||
{"y", "Toggle synced scaling mode for network graphs."},
|
||||
{"f, /", "To enter a process filter. Start with ! for regex."},
|
||||
{"F", "Pause process list."},
|
||||
{"delete", "Clear any entered filter."},
|
||||
{"c", "Toggle per-core cpu usage of processes."},
|
||||
{"r", "Reverse sorting order in processes box."},
|
||||
@@ -828,6 +829,12 @@ namespace Menu {
|
||||
" ",
|
||||
"Will show percentage of total memory",
|
||||
"if False."},
|
||||
{"keep_dead_proc_usage",
|
||||
"Cpu and Mem usage for dead processes",
|
||||
"",
|
||||
"Set true if process should preserve the cpu",
|
||||
"and memory usage of when it died while",
|
||||
"paused."},
|
||||
{"proc_cpu_graphs",
|
||||
"Show cpu graph for each process.",
|
||||
"",
|
||||
|
||||
@@ -144,8 +144,8 @@ bool set_priority(pid_t pid, int priority) {
|
||||
}
|
||||
}
|
||||
|
||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, bool reverse, int& c_index, const int index_max, bool collapsed) {
|
||||
if (proc_vec.size() > 1) {
|
||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, bool reverse, bool paused, int& c_index, const int index_max, bool collapsed) {
|
||||
if (proc_vec.size() > 1 and not paused) {
|
||||
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;
|
||||
@@ -167,7 +167,7 @@ bool set_priority(pid_t pid, int priority) {
|
||||
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));
|
||||
tree_sort(r.children, sorting, reverse, paused, c_index, (collapsed or r.entry.get().collapsed or r.entry.get().tree_index == (size_t)index_max));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,14 +239,16 @@ bool set_priority(pid_t pid, int priority) {
|
||||
|
||||
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;
|
||||
if (p.state != 'X') {
|
||||
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;
|
||||
}
|
||||
else if (Config::getB("proc_aggregate")) {
|
||||
else if (Config::getB("proc_aggregate") and p.state != 'X') {
|
||||
cur_proc.cpu_p += p.cpu_p;
|
||||
cur_proc.cpu_c += p.cpu_c;
|
||||
cur_proc.mem += p.mem;
|
||||
|
||||
@@ -400,6 +400,7 @@ namespace Proc {
|
||||
uint64_t ppid{};
|
||||
uint64_t cpu_s{};
|
||||
uint64_t cpu_t{};
|
||||
uint64_t death_time{};
|
||||
string prefix{}; // defaults to ""
|
||||
size_t depth{};
|
||||
size_t tree_index{};
|
||||
@@ -442,8 +443,8 @@ namespace Proc {
|
||||
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree = false);
|
||||
|
||||
//* Recursive sort of process tree
|
||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting,
|
||||
bool reverse, int& c_index, const int index_max, bool collapsed = false);
|
||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, bool reverse, bool paused,
|
||||
int& c_index, const int index_max, bool collapsed = false);
|
||||
|
||||
auto matches_filter(const proc_info& proc, const std::string& filter) -> bool;
|
||||
|
||||
|
||||
@@ -88,7 +88,8 @@ namespace Theme {
|
||||
{ "upload_end", "#dcafde" },
|
||||
{ "process_start", "#80d0a3" },
|
||||
{ "process_mid", "#dcd179" },
|
||||
{ "process_end", "#d45454" }
|
||||
{ "process_end", "#d45454" },
|
||||
{ "proc_pause_bg", "#b54040" }
|
||||
};
|
||||
|
||||
const std::unordered_map<string, string> TTY_theme = {
|
||||
@@ -133,7 +134,8 @@ namespace Theme {
|
||||
{ "upload_end", "\x1b[95m" },
|
||||
{ "process_start", "\x1b[32m" },
|
||||
{ "process_mid", "\x1b[33m" },
|
||||
{ "process_end", "\x1b[31m" }
|
||||
{ "process_end", "\x1b[31m" },
|
||||
{ "proc_pause_bg", "\x1b[31m" }
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -59,6 +59,7 @@ tab-size = 4
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "../btop_config.hpp"
|
||||
#include "../btop_shared.hpp"
|
||||
@@ -998,6 +999,7 @@ namespace Proc {
|
||||
string current_sort;
|
||||
string current_filter;
|
||||
bool current_rev = false;
|
||||
bool is_tree_mode;
|
||||
|
||||
fs::file_time_type passwd_time;
|
||||
|
||||
@@ -1008,6 +1010,7 @@ namespace Proc {
|
||||
int filter_found = 0;
|
||||
|
||||
detail_container detailed;
|
||||
static std::unordered_set<size_t> dead_procs;
|
||||
|
||||
string get_status(char s) {
|
||||
if (s & SRUN) return "Running";
|
||||
@@ -1038,7 +1041,9 @@ namespace Proc {
|
||||
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s); // only interested in second granularity, so ignoring tc_usec
|
||||
// only interested in second granularity, so ignoring tc_usec
|
||||
if (detailed.entry.state != 'X') detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s);
|
||||
else detailed.elapsed = sec_to_dhms(detailed.entry.death_time);
|
||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||
|
||||
//? Get parent process name
|
||||
@@ -1076,14 +1081,17 @@ namespace Proc {
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
const auto pause_proc_list = Config::getB("pause_proc_list");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
bool tree_mode_change = tree != is_tree_mode;
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
}
|
||||
if (tree_mode_change) is_tree_mode = tree;
|
||||
|
||||
const int cmult = (per_core) ? Shared::coreCount : 1;
|
||||
bool got_detailed = false;
|
||||
@@ -1128,11 +1136,13 @@ namespace Proc {
|
||||
//? Check if pid already exists in current_procs
|
||||
bool no_cache = false;
|
||||
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
|
||||
if (find_old == current_procs.end()) {
|
||||
//? Only add new processes if not paused
|
||||
if (find_old == current_procs.end() and not pause_proc_list) {
|
||||
current_procs.push_back({pid});
|
||||
find_old = current_procs.end() - 1;
|
||||
no_cache = true;
|
||||
}
|
||||
else if (dead_procs.contains(pid)) continue;
|
||||
|
||||
auto &new_proc = *find_old;
|
||||
|
||||
@@ -1186,9 +1196,31 @@ namespace Proc {
|
||||
}
|
||||
}
|
||||
|
||||
//? 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());
|
||||
//? Clear dead processes from current_procs if not paused
|
||||
if (not pause_proc_list) {
|
||||
auto eraser = rng::remove_if(current_procs, [&](const auto& element) { return not v_contains(found, element.pid); });
|
||||
current_procs.erase(eraser.begin(), eraser.end());
|
||||
if (!dead_procs.empty()) dead_procs.clear();
|
||||
}
|
||||
//? Set correct state of dead processes if paused
|
||||
else {
|
||||
for (auto& r : current_procs) {
|
||||
if (rng::find(found, r.pid) == found.end()) {
|
||||
if (r.state != 'X') {
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
r.death_time = currentTime.tv_sec - r.cpu_s;
|
||||
}
|
||||
r.state = 'X';
|
||||
dead_procs.emplace(r.pid);
|
||||
//? Reset cpu usage for dead processes if paused and option is set
|
||||
if (!Config::getB("keep_dead_proc_usage")) {
|
||||
r.cpu_p = 0.0;
|
||||
r.mem = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//? Update the details info box for process if active
|
||||
if (show_detailed and got_detailed) {
|
||||
@@ -1222,7 +1254,7 @@ namespace Proc {
|
||||
}
|
||||
|
||||
//* Sort processes
|
||||
if (sorted_change or not no_update) {
|
||||
if ((sorted_change or tree_mode_change) or (not no_update and not pause_proc_list)) {
|
||||
proc_sorter(current_procs, sorting, reverse, tree);
|
||||
}
|
||||
|
||||
@@ -1267,8 +1299,10 @@ namespace Proc {
|
||||
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;
|
||||
if (!pause_proc_list) {
|
||||
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
|
||||
@@ -1281,7 +1315,7 @@ namespace Proc {
|
||||
|
||||
//? 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());
|
||||
tree_sort(tree_procs, sorting, reverse, (pause_proc_list and not (sorted_change or tree_mode_change)), index, current_procs.size());
|
||||
|
||||
//? Recursive construction of ASCII tree prefixes.
|
||||
for (auto t = tree_procs.begin(); t != tree_procs.end(); ++t) {
|
||||
|
||||
@@ -2772,6 +2772,7 @@ namespace Proc {
|
||||
string current_sort;
|
||||
string current_filter;
|
||||
bool current_rev{};
|
||||
bool is_tree_mode;
|
||||
|
||||
fs::file_time_type passwd_time;
|
||||
|
||||
@@ -2784,6 +2785,7 @@ namespace Proc {
|
||||
detail_container detailed;
|
||||
constexpr size_t KTHREADD = 2;
|
||||
static std::unordered_set<size_t> kernels_procs = {KTHREADD};
|
||||
static std::unordered_set<size_t> dead_procs;
|
||||
|
||||
//* Get detailed info for selected process
|
||||
static void _collect_details(const size_t pid, const uint64_t uptime, vector<proc_info>& procs) {
|
||||
@@ -2805,7 +2807,8 @@ namespace Proc {
|
||||
while (cmp_greater(detailed.cpu_percent.size(), width)) detailed.cpu_percent.pop_front();
|
||||
|
||||
//? Process runtime
|
||||
detailed.elapsed = sec_to_dhms(uptime - (detailed.entry.cpu_s / Shared::clkTck));
|
||||
if (detailed.entry.state != 'X') detailed.elapsed = sec_to_dhms(uptime - (detailed.entry.cpu_s / Shared::clkTck));
|
||||
else detailed.elapsed = sec_to_dhms(detailed.entry.death_time);
|
||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||
|
||||
//? Get parent process name
|
||||
@@ -2892,14 +2895,17 @@ namespace Proc {
|
||||
auto should_filter_kernel = Config::getB("proc_filter_kernel");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
const auto pause_proc_list = Config::getB("pause_proc_list");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
bool tree_mode_change = tree != is_tree_mode;
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
}
|
||||
if (tree_mode_change) is_tree_mode = tree;
|
||||
ifstream pread;
|
||||
string long_string;
|
||||
string short_str;
|
||||
@@ -2987,11 +2993,13 @@ namespace Proc {
|
||||
//? Check if pid already exists in current_procs
|
||||
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
|
||||
bool no_cache{};
|
||||
if (find_old == current_procs.end()) {
|
||||
//? Only add new processes if not paused
|
||||
if (find_old == current_procs.end() and not pause_proc_list) {
|
||||
current_procs.push_back({pid});
|
||||
find_old = current_procs.end() - 1;
|
||||
no_cache = true;
|
||||
}
|
||||
else if (dead_procs.contains(pid)) continue;
|
||||
|
||||
auto& new_proc = *find_old;
|
||||
|
||||
@@ -3146,9 +3154,27 @@ namespace Proc {
|
||||
}
|
||||
}
|
||||
|
||||
//? Clear dead processes from current_procs and remove kernel processes if enabled
|
||||
auto eraser = rng::remove_if(current_procs, [&](const auto& element){ return not v_contains(found, element.pid); });
|
||||
current_procs.erase(eraser.begin(), eraser.end());
|
||||
//? Clear dead processes from current_procs and remove kernel processes if enabled and not paused
|
||||
if (not pause_proc_list) {
|
||||
auto eraser = rng::remove_if(current_procs, [&](const auto& element){ return not v_contains(found, element.pid); });
|
||||
current_procs.erase(eraser.begin(), eraser.end());
|
||||
if (!dead_procs.empty()) dead_procs.clear();
|
||||
}
|
||||
//? Set correct state of dead processes if paused
|
||||
else {
|
||||
for (auto& r : current_procs) {
|
||||
if (rng::find(found, r.pid) == found.end()) {
|
||||
if (r.state != 'X') r.death_time = round(uptime) - (r.cpu_s / Shared::clkTck);
|
||||
r.state = 'X';
|
||||
dead_procs.emplace(r.pid);
|
||||
//? Reset cpu usage for dead processes if paused and option is set
|
||||
if (!Config::getB("keep_dead_proc_usage")) {
|
||||
r.cpu_p = 0.0;
|
||||
r.mem = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//? Update the details info box for process if active
|
||||
if (show_detailed and got_detailed) {
|
||||
@@ -3181,7 +3207,7 @@ namespace Proc {
|
||||
}
|
||||
|
||||
//* Sort processes
|
||||
if (sorted_change or not no_update) {
|
||||
if ((sorted_change or tree_mode_change) or (not no_update and not pause_proc_list)) {
|
||||
proc_sorter(current_procs, sorting, reverse, tree);
|
||||
}
|
||||
|
||||
@@ -3226,8 +3252,10 @@ namespace Proc {
|
||||
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;
|
||||
if (!pause_proc_list) {
|
||||
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
|
||||
@@ -3240,7 +3268,7 @@ namespace Proc {
|
||||
|
||||
//? 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());
|
||||
tree_sort(tree_procs, sorting, reverse, (pause_proc_list and not (sorted_change or tree_mode_change)), index, current_procs.size());
|
||||
|
||||
//? Recursive construction of ASCII tree prefixes.
|
||||
for (auto t = tree_procs.begin(); t != tree_procs.end(); ++t) {
|
||||
|
||||
@@ -65,6 +65,7 @@ tab-size = 4
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "../btop_config.hpp"
|
||||
#include "../btop_shared.hpp"
|
||||
@@ -1093,6 +1094,7 @@ namespace Proc {
|
||||
string current_sort;
|
||||
string current_filter;
|
||||
bool current_rev = false;
|
||||
bool is_tree_mode;
|
||||
|
||||
fs::file_time_type passwd_time;
|
||||
|
||||
@@ -1103,6 +1105,7 @@ namespace Proc {
|
||||
int filter_found = 0;
|
||||
|
||||
detail_container detailed;
|
||||
static std::unordered_set<size_t> dead_procs;
|
||||
|
||||
string get_status(char s) {
|
||||
if (s & LSRUN) return "Running";
|
||||
@@ -1133,7 +1136,9 @@ namespace Proc {
|
||||
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s); // only interested in second granularity, so ignoring tc_usec
|
||||
// only interested in second granularity, so ignoring tc_usec
|
||||
if (detailed.entry.state != 'X') detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s);
|
||||
else detailed.elapsed = sec_to_dhms(detailed.entry.death_time);
|
||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||
|
||||
//? Get parent process name
|
||||
@@ -1164,14 +1169,17 @@ namespace Proc {
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
const auto pause_proc_list = Config::getB("pause_proc_list");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
bool tree_mode_change = tree != is_tree_mode;
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
}
|
||||
if (tree_mode_change) is_tree_mode = tree;
|
||||
|
||||
const int cmult = (per_core) ? Shared::coreCount : 1;
|
||||
bool got_detailed = false;
|
||||
@@ -1204,11 +1212,13 @@ namespace Proc {
|
||||
//? Check if pid already exists in current_procs
|
||||
bool no_cache = false;
|
||||
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
|
||||
if (find_old == current_procs.end()) {
|
||||
//? Only add new processes if not paused
|
||||
if (find_old == current_procs.end() and not pause_proc_list) {
|
||||
current_procs.push_back({pid});
|
||||
find_old = current_procs.end() - 1;
|
||||
no_cache = true;
|
||||
}
|
||||
else if (dead_procs.contains(pid)) continue;
|
||||
|
||||
auto &new_proc = *find_old;
|
||||
|
||||
@@ -1261,9 +1271,31 @@ namespace Proc {
|
||||
}
|
||||
}
|
||||
|
||||
//? 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());
|
||||
//? Clear dead processes from current_procs if not paused
|
||||
if (not pause_proc_list) {
|
||||
auto eraser = rng::remove_if(current_procs, [&](const auto& element) { return not v_contains(found, element.pid); });
|
||||
current_procs.erase(eraser.begin(), eraser.end());
|
||||
if (!dead_procs.empty()) dead_procs.clear();
|
||||
}
|
||||
//? Set correct state of dead processes if paused
|
||||
else {
|
||||
for (auto& r : current_procs) {
|
||||
if (rng::find(found, r.pid) == found.end()) {
|
||||
if (r.state != 'X') {
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
r.death_time = currentTime.tv_sec - r.cpu_s;
|
||||
}
|
||||
r.state = 'X';
|
||||
dead_procs.emplace(r.pid);
|
||||
//? Reset cpu usage for dead processes if paused and option is set
|
||||
if (!Config::getB("keep_dead_proc_usage")) {
|
||||
r.cpu_p = 0.0;
|
||||
r.mem = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//? Update the details info box for process if active
|
||||
if (show_detailed and got_detailed) {
|
||||
@@ -1302,7 +1334,7 @@ namespace Proc {
|
||||
}
|
||||
|
||||
//* Sort processes
|
||||
if (sorted_change or not no_update) {
|
||||
if ((sorted_change or tree_mode_change) or (not no_update and not pause_proc_list)) {
|
||||
proc_sorter(current_procs, sorting, reverse, tree);
|
||||
}
|
||||
|
||||
@@ -1330,8 +1362,10 @@ namespace Proc {
|
||||
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;
|
||||
if (!pause_proc_list) {
|
||||
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
|
||||
@@ -1344,7 +1378,7 @@ namespace Proc {
|
||||
|
||||
//? 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());
|
||||
tree_sort(tree_procs, sorting, reverse, (pause_proc_list and not (sorted_change or tree_mode_change)), index, current_procs.size());
|
||||
|
||||
//? Recursive construction of ASCII tree prefixes.
|
||||
for (auto t = tree_procs.begin(); t != tree_procs.end(); ++t) {
|
||||
|
||||
@@ -62,6 +62,7 @@ tab-size = 4
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "../btop_config.hpp"
|
||||
#include "../btop_shared.hpp"
|
||||
@@ -956,6 +957,7 @@ namespace Proc {
|
||||
string current_sort;
|
||||
string current_filter;
|
||||
bool current_rev = false;
|
||||
bool is_tree_mode;
|
||||
|
||||
fs::file_time_type passwd_time;
|
||||
|
||||
@@ -966,6 +968,7 @@ namespace Proc {
|
||||
int filter_found = 0;
|
||||
|
||||
detail_container detailed;
|
||||
static std::unordered_set<size_t> dead_procs;
|
||||
|
||||
string get_status(char s) {
|
||||
if (s & SRUN) return "Running";
|
||||
@@ -996,7 +999,9 @@ namespace Proc {
|
||||
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s); // only interested in second granularity, so ignoring tc_usec
|
||||
// only interested in second granularity, so ignoring tc_usec
|
||||
if (detailed.entry.state != 'X') detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s);
|
||||
else detailed.elapsed = sec_to_dhms(detailed.entry.death_time);
|
||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||
|
||||
//? Get parent process name
|
||||
@@ -1027,14 +1032,17 @@ namespace Proc {
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
const auto pause_proc_list = Config::getB("pause_proc_list");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
bool tree_mode_change = tree != is_tree_mode;
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
}
|
||||
if (tree_mode_change) is_tree_mode = tree;
|
||||
|
||||
const int cmult = (per_core) ? Shared::coreCount : 1;
|
||||
bool got_detailed = false;
|
||||
@@ -1067,11 +1075,13 @@ namespace Proc {
|
||||
//? Check if pid already exists in current_procs
|
||||
bool no_cache = false;
|
||||
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
|
||||
if (find_old == current_procs.end()) {
|
||||
//? Only add new processes if not paused
|
||||
if (find_old == current_procs.end() and not pause_proc_list) {
|
||||
current_procs.push_back({pid});
|
||||
find_old = current_procs.end() - 1;
|
||||
no_cache = true;
|
||||
}
|
||||
else if (dead_procs.contains(pid)) continue;
|
||||
|
||||
auto &new_proc = *find_old;
|
||||
|
||||
@@ -1124,9 +1134,31 @@ namespace Proc {
|
||||
}
|
||||
}
|
||||
|
||||
//? 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());
|
||||
//? Clear dead processes from current_procs if not paused
|
||||
if (not pause_proc_list) {
|
||||
auto eraser = rng::remove_if(current_procs, [&](const auto& element) { return not v_contains(found, element.pid); });
|
||||
current_procs.erase(eraser.begin(), eraser.end());
|
||||
if (!dead_procs.empty()) dead_procs.clear();
|
||||
}
|
||||
//? Set correct state of dead processes if paused
|
||||
else {
|
||||
for (auto& r : current_procs) {
|
||||
if (rng::find(found, r.pid) == found.end()) {
|
||||
if (r.state != 'X') {
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
r.death_time = currentTime.tv_sec - r.cpu_s;
|
||||
}
|
||||
r.state = 'X';
|
||||
dead_procs.emplace(r.pid);
|
||||
//? Reset cpu usage for dead processes if paused and option is set
|
||||
if (!Config::getB("keep_dead_proc_usage")) {
|
||||
r.cpu_p = 0.0;
|
||||
r.mem = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//? Update the details info box for process if active
|
||||
if (show_detailed and got_detailed) {
|
||||
@@ -1160,7 +1192,7 @@ namespace Proc {
|
||||
}
|
||||
|
||||
//* Sort processes
|
||||
if (sorted_change or not no_update) {
|
||||
if ((sorted_change or tree_mode_change) or (not no_update and not pause_proc_list)) {
|
||||
proc_sorter(current_procs, sorting, reverse, tree);
|
||||
}
|
||||
|
||||
@@ -1205,8 +1237,10 @@ namespace Proc {
|
||||
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;
|
||||
if (!pause_proc_list) {
|
||||
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
|
||||
@@ -1219,7 +1253,7 @@ namespace Proc {
|
||||
|
||||
//? 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());
|
||||
tree_sort(tree_procs, sorting, reverse, (pause_proc_list and not (sorted_change or tree_mode_change)), index, current_procs.size());
|
||||
|
||||
//? Recursive construction of ASCII tree prefixes.
|
||||
for (auto t = tree_procs.begin(); t != tree_procs.end(); ++t) {
|
||||
|
||||
@@ -54,6 +54,7 @@ tab-size = 4
|
||||
#include <ranges>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "../btop_config.hpp"
|
||||
#include "../btop_shared.hpp"
|
||||
@@ -1036,6 +1037,7 @@ namespace Proc {
|
||||
string current_sort;
|
||||
string current_filter;
|
||||
bool current_rev = false;
|
||||
bool is_tree_mode;
|
||||
|
||||
fs::file_time_type passwd_time;
|
||||
|
||||
@@ -1046,6 +1048,7 @@ namespace Proc {
|
||||
int filter_found = 0;
|
||||
|
||||
detail_container detailed;
|
||||
static std::unordered_set<size_t> dead_procs;
|
||||
|
||||
string get_status(char s) {
|
||||
if (s & SRUN) return "Running";
|
||||
@@ -1076,7 +1079,9 @@ namespace Proc {
|
||||
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - (detailed.entry.cpu_s / 1'000'000));
|
||||
//? Get elapsed time if process isn't dead
|
||||
if (detailed.entry.state != 'X') detailed.elapsed = sec_to_dhms(currentTime.tv_sec - (detailed.entry.cpu_s / 1'000'000));
|
||||
else detailed.elapsed = sec_to_dhms(detailed.entry.death_time);
|
||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||
|
||||
//? Get parent process name
|
||||
@@ -1114,14 +1119,17 @@ namespace Proc {
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
const auto pause_proc_list = Config::getB("pause_proc_list");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
bool tree_mode_change = tree != is_tree_mode;
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
}
|
||||
if (tree_mode_change) is_tree_mode = tree;
|
||||
|
||||
const int cmult = (per_core) ? Shared::coreCount : 1;
|
||||
bool got_detailed = false;
|
||||
@@ -1176,11 +1184,13 @@ namespace Proc {
|
||||
//? Check if pid already exists in current_procs
|
||||
bool no_cache = false;
|
||||
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
|
||||
if (find_old == current_procs.end()) {
|
||||
//? Only add new processes if not paused
|
||||
if (find_old == current_procs.end() and not pause_proc_list) {
|
||||
current_procs.push_back({pid});
|
||||
find_old = current_procs.end() - 1;
|
||||
no_cache = true;
|
||||
}
|
||||
else if (dead_procs.contains(pid)) continue;
|
||||
|
||||
auto &new_proc = *find_old;
|
||||
|
||||
@@ -1262,9 +1272,31 @@ namespace Proc {
|
||||
}
|
||||
}
|
||||
|
||||
// //? 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());
|
||||
//? Clear dead processes from current_procs if not paused
|
||||
if (not pause_proc_list) {
|
||||
auto eraser = rng::remove_if(current_procs, [&](const auto& element) { return not v_contains(found, element.pid); });
|
||||
current_procs.erase(eraser.begin(), eraser.end());
|
||||
if (!dead_procs.empty()) dead_procs.clear();
|
||||
}
|
||||
//? Set correct state of dead processes if paused
|
||||
else {
|
||||
for (auto& r : current_procs) {
|
||||
if (rng::find(found, r.pid) == found.end()) {
|
||||
if (r.state != 'X') {
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
r.death_time = currentTime.tv_sec - (r.cpu_s / 1'000'000);
|
||||
}
|
||||
r.state = 'X';
|
||||
dead_procs.emplace(r.pid);
|
||||
//? Reset cpu usage for dead processes if paused and option is set
|
||||
if (!Config::getB("keep_dead_proc_usage")) {
|
||||
r.cpu_p = 0.0;
|
||||
r.mem = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//? Update the details info box for process if active
|
||||
if (show_detailed and got_detailed) {
|
||||
@@ -1298,7 +1330,7 @@ namespace Proc {
|
||||
}
|
||||
|
||||
//* Sort processes
|
||||
if (sorted_change or not no_update) {
|
||||
if ((sorted_change or tree_mode_change) or (not no_update and not pause_proc_list)) {
|
||||
proc_sorter(current_procs, sorting, reverse, tree);
|
||||
}
|
||||
|
||||
@@ -1343,8 +1375,10 @@ namespace Proc {
|
||||
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;
|
||||
if (!pause_proc_list) {
|
||||
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
|
||||
@@ -1357,7 +1391,7 @@ namespace Proc {
|
||||
|
||||
//? 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());
|
||||
tree_sort(tree_procs, sorting, reverse, (pause_proc_list and not (sorted_change or tree_mode_change)), index, current_procs.size());
|
||||
|
||||
//? Recursive construction of ASCII tree prefixes.
|
||||
for (auto t = tree_procs.begin(); t != tree_procs.end(); ++t) {
|
||||
|
||||
Reference in New Issue
Block a user