Merge pull request #1306 from vandabbin/pause_proc_list

This commit is contained in:
Jakob P. Liljenberg
2025-10-27 22:16:18 +01:00
committed by GitHub
13 changed files with 261 additions and 63 deletions

View File

@@ -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

View File

@@ -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},

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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.",
"",

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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(&currentTime, 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(&currentTime, 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) {

View File

@@ -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) {

View File

@@ -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(&currentTime, 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(&currentTime, 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) {

View File

@@ -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(&currentTime, 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(&currentTime, 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) {

View File

@@ -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(&currentTime, 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(&currentTime, 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) {