mirror of
https://github.com/aristocratos/btop.git
synced 2024-06-01 18:19:56 +12:00
Removed bad goto and added 100 microseconds sleep in thread manager loop to spare some cpu cycles
This commit is contained in:
parent
f9ed675d47
commit
ced3d47ebe
32
src/btop.cpp
32
src/btop.cpp
|
@ -347,10 +347,10 @@ namespace Runner {
|
|||
|
||||
//* Start collection functions for all boxes in async threads and draw in this thread when finished
|
||||
//? Starting order below based on mean time to finish
|
||||
while (box_mask.count() > 0) {
|
||||
if (stopping) break;
|
||||
try {
|
||||
//* PROC
|
||||
try {
|
||||
while (box_mask.count() > 0) {
|
||||
if (stopping) break;
|
||||
//? PROC
|
||||
if (box_mask.test(proc_present)) {
|
||||
if (not box_mask.test(proc_running)) {
|
||||
proc = async(Proc::collect, conf->no_update);
|
||||
|
@ -367,9 +367,10 @@ namespace Runner {
|
|||
throw std::runtime_error("Proc:: -> " + (string)e.what());
|
||||
}
|
||||
box_mask ^= proc_done;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//* MEM
|
||||
//? MEM
|
||||
if (box_mask.test(mem_present)) {
|
||||
if (not box_mask.test(mem_running)) {
|
||||
mem = async(Mem::collect, conf->no_update);
|
||||
|
@ -386,9 +387,10 @@ namespace Runner {
|
|||
throw std::runtime_error("Mem:: -> " + (string)e.what());
|
||||
}
|
||||
box_mask ^= mem_done;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//* NET
|
||||
//? NET
|
||||
if (box_mask.test(net_present)) {
|
||||
if (not box_mask.test(net_running)) {
|
||||
net = async(Net::collect, conf->no_update);
|
||||
|
@ -405,9 +407,10 @@ namespace Runner {
|
|||
throw std::runtime_error("Net:: -> " + (string)e.what());
|
||||
}
|
||||
box_mask ^= net_done;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//* CPU
|
||||
//? CPU
|
||||
if (box_mask.test(cpu_present)) {
|
||||
if (not box_mask.test(cpu_running)) {
|
||||
cpu = async(Cpu::collect, conf->no_update);
|
||||
|
@ -424,16 +427,17 @@ namespace Runner {
|
|||
throw std::runtime_error("Cpu:: -> " + (string)e.what());
|
||||
}
|
||||
box_mask ^= cpu_done;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
sleep_micros(100);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what();
|
||||
Global::thread_exception = true;
|
||||
Input::interrupt = true;
|
||||
stopping = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what();
|
||||
Global::thread_exception = true;
|
||||
Input::interrupt = true;
|
||||
stopping = true;
|
||||
}
|
||||
|
||||
if (stopping) {
|
||||
|
|
|
@ -962,8 +962,9 @@ namespace Proc {
|
|||
if (item_fit >= 4) out += cjust("IO/W:", item_width);
|
||||
if (item_fit >= 5) out += cjust("Parent:", item_width);
|
||||
if (item_fit >= 6) out += cjust("User:", item_width);
|
||||
if (item_fit >= 7) out += cjust("Nice:", item_width);
|
||||
if (item_fit >= 8) out += cjust("Threads:", item_width);
|
||||
if (item_fit >= 7) out += cjust("Threads:", item_width);
|
||||
if (item_fit >= 8) out += cjust("Nice:", item_width);
|
||||
|
||||
|
||||
//? Command line
|
||||
for (int i = 0; const auto& l : {'C', 'M', 'D'})
|
||||
|
@ -1084,8 +1085,9 @@ namespace Proc {
|
|||
if (item_fit >= 4) out += cjust(detailed.io_write, item_width);
|
||||
if (item_fit >= 5) out += cjust(detailed.parent, item_width, true);
|
||||
if (item_fit >= 6) out += cjust(detailed.entry.user, item_width, true);
|
||||
if (item_fit >= 7) out += cjust(to_string(detailed.entry.p_nice), item_width);
|
||||
if (item_fit >= 8) out += cjust(to_string(detailed.entry.threads), item_width);
|
||||
if (item_fit >= 7) out += cjust(to_string(detailed.entry.threads), item_width);
|
||||
if (item_fit >= 8) out += cjust(to_string(detailed.entry.p_nice), item_width);
|
||||
|
||||
|
||||
const double mem_p = (double)detailed.mem_bytes.back() * 100 / Shared::totalMem;
|
||||
string mem_str = to_string(mem_p);
|
||||
|
|
|
@ -1000,214 +1000,216 @@ namespace Proc {
|
|||
procs.reserve(current_procs.size() + 10);
|
||||
const int cmult = (per_core) ? Shared::coreCount : 1;
|
||||
bool got_detailed = false;
|
||||
|
||||
//* Use pids from last update if only changing filter, sorting or tree options
|
||||
if (no_update and not cache.empty()) {
|
||||
procs = current_procs;
|
||||
if (show_detailed and detailed_pid != detailed.last_pid) _collect_details(detailed_pid, round(uptime), procs);
|
||||
goto proc_no_update;
|
||||
}
|
||||
|
||||
//? Update uid_user map if /etc/passwd changed since last run
|
||||
if (not Shared::passwd_path.empty() and fs::last_write_time(Shared::passwd_path) != passwd_time) {
|
||||
string r_uid, r_user;
|
||||
passwd_time = fs::last_write_time(Shared::passwd_path);
|
||||
uid_user.clear();
|
||||
pread.open(Shared::passwd_path);
|
||||
if (pread.good()) {
|
||||
while (not pread.eof()) {
|
||||
getline(pread, r_user, ':');
|
||||
pread.ignore(SSmax, ':');
|
||||
getline(pread, r_uid, ':');
|
||||
uid_user[r_uid] = r_user;
|
||||
pread.ignore(SSmax, '\n');
|
||||
}
|
||||
}
|
||||
else {
|
||||
Shared::passwd_path.clear();
|
||||
}
|
||||
pread.close();
|
||||
}
|
||||
|
||||
//* Get cpu total times from /proc/stat
|
||||
cputimes = 0;
|
||||
pread.open(Shared::procPath / "stat");
|
||||
if (pread.good()) {
|
||||
pread.ignore(SSmax, ' ');
|
||||
for (uint64_t times; pread >> times; cputimes += times);
|
||||
pread.close();
|
||||
}
|
||||
else throw std::runtime_error("Failure to read /proc/stat");
|
||||
|
||||
//* Iterate over all pids in /proc
|
||||
for (const auto& d: fs::directory_iterator(Shared::procPath)) {
|
||||
if (Runner::stopping)
|
||||
return procs;
|
||||
if (pread.is_open()) pread.close();
|
||||
|
||||
const string pid_str = d.path().filename();
|
||||
if (not isdigit(pid_str[0])) continue;
|
||||
|
||||
proc_info new_proc (stoul(pid_str));
|
||||
|
||||
//* Cache program name, command and username
|
||||
if (not cache.contains(new_proc.pid)) {
|
||||
string name, cmd, user;
|
||||
pread.open(d.path() / "comm");
|
||||
if (not pread.good()) continue;
|
||||
getline(pread, name);
|
||||
pread.close();
|
||||
size_t name_offset = rng::count(name, ' ');
|
||||
|
||||
pread.open(d.path() / "cmdline");
|
||||
if (not pread.good()) continue;
|
||||
long_string.clear();
|
||||
while(getline(pread, long_string, '\0')) cmd += long_string + ' ';
|
||||
pread.close();
|
||||
if (not cmd.empty()) cmd.pop_back();
|
||||
|
||||
pread.open(d.path() / "status");
|
||||
if (not pread.good()) continue;
|
||||
string uid;
|
||||
string line;
|
||||
while (not pread.eof()) {
|
||||
getline(pread, line, ':');
|
||||
if (line == "Uid") {
|
||||
pread.ignore();
|
||||
getline(pread, uid, '\t');
|
||||
break;
|
||||
} else {
|
||||
//* ---------------------------------------------Collection start----------------------------------------------
|
||||
else {
|
||||
//? Update uid_user map if /etc/passwd changed since last run
|
||||
if (not Shared::passwd_path.empty() and fs::last_write_time(Shared::passwd_path) != passwd_time) {
|
||||
string r_uid, r_user;
|
||||
passwd_time = fs::last_write_time(Shared::passwd_path);
|
||||
uid_user.clear();
|
||||
pread.open(Shared::passwd_path);
|
||||
if (pread.good()) {
|
||||
while (not pread.eof()) {
|
||||
getline(pread, r_user, ':');
|
||||
pread.ignore(SSmax, ':');
|
||||
getline(pread, r_uid, ':');
|
||||
uid_user[r_uid] = r_user;
|
||||
pread.ignore(SSmax, '\n');
|
||||
}
|
||||
}
|
||||
else {
|
||||
Shared::passwd_path.clear();
|
||||
}
|
||||
pread.close();
|
||||
user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid;
|
||||
|
||||
cache[new_proc.pid] = {name, cmd, user, name_offset};
|
||||
}
|
||||
|
||||
new_proc.name = cache.at(new_proc.pid).name;
|
||||
new_proc.cmd = cache.at(new_proc.pid).cmd;
|
||||
new_proc.user = cache.at(new_proc.pid).user;
|
||||
//? Get cpu total times from /proc/stat
|
||||
cputimes = 0;
|
||||
pread.open(Shared::procPath / "stat");
|
||||
if (pread.good()) {
|
||||
pread.ignore(SSmax, ' ');
|
||||
for (uint64_t times; pread >> times; cputimes += times);
|
||||
pread.close();
|
||||
}
|
||||
else throw std::runtime_error("Failure to read /proc/stat");
|
||||
|
||||
//* Parse /proc/[pid]/stat
|
||||
pread.open(d.path() / "stat");
|
||||
if (not pread.good()) continue;
|
||||
//? Iterate over all pids in /proc
|
||||
for (const auto& d: fs::directory_iterator(Shared::procPath)) {
|
||||
if (Runner::stopping)
|
||||
return procs;
|
||||
if (pread.is_open()) pread.close();
|
||||
|
||||
//? Check cached value for whitespace characters in name and set offset to get correct fields from stat file
|
||||
size_t& offset = cache.at(new_proc.pid).name_offset;
|
||||
short_str.clear();
|
||||
size_t x = 0, next_x = 3;
|
||||
uint64_t cpu_t = 0;
|
||||
try {
|
||||
for (;;) {
|
||||
while (pread.good() and ++x - offset < next_x) {
|
||||
pread.ignore(SSmax, ' ');
|
||||
}
|
||||
if (pread.bad()) goto stat_loop_done;
|
||||
const string pid_str = d.path().filename();
|
||||
if (not isdigit(pid_str[0])) continue;
|
||||
|
||||
getline(pread, short_str, ' ');
|
||||
proc_info new_proc (stoul(pid_str));
|
||||
|
||||
switch (x-offset) {
|
||||
case 3: { //? Process state
|
||||
new_proc.state = short_str.at(0);
|
||||
continue;
|
||||
}
|
||||
case 4: { //? Parent pid
|
||||
new_proc.ppid = stoull(short_str);
|
||||
next_x = 14;
|
||||
continue;
|
||||
}
|
||||
case 14: { //? Process utime
|
||||
cpu_t = stoull(short_str);
|
||||
continue;
|
||||
}
|
||||
case 15: { //? Process stime
|
||||
cpu_t += stoull(short_str);
|
||||
next_x = 19;
|
||||
continue;
|
||||
}
|
||||
case 19: { //? Nice value
|
||||
new_proc.p_nice = stoull(short_str);
|
||||
continue;
|
||||
}
|
||||
case 20: { //? Number of threads
|
||||
new_proc.threads = stoull(short_str);
|
||||
if (cache.at(new_proc.pid).cpu_s == 0) {
|
||||
next_x = 22;
|
||||
cache.at(new_proc.pid).cpu_t = cpu_t;
|
||||
}
|
||||
else
|
||||
next_x = 24;
|
||||
continue;
|
||||
}
|
||||
case 22: { //? Save cpu seconds to cache if missing
|
||||
cache.at(new_proc.pid).cpu_s = stoull(short_str);
|
||||
next_x = 24;
|
||||
continue;
|
||||
}
|
||||
case 24: { //? RSS memory (can be inaccurate, but parsing smaps increases total cpu usage by ~20x)
|
||||
new_proc.mem = stoull(short_str) * Shared::pageSize;
|
||||
next_x = 39;
|
||||
continue;
|
||||
}
|
||||
case 39: { //? CPU number last executed on
|
||||
new_proc.cpu_n = stoull(short_str);
|
||||
goto stat_loop_done;
|
||||
//? Cache program name, command and username
|
||||
if (not cache.contains(new_proc.pid)) {
|
||||
string name, cmd, user;
|
||||
pread.open(d.path() / "comm");
|
||||
if (not pread.good()) continue;
|
||||
getline(pread, name);
|
||||
pread.close();
|
||||
size_t name_offset = rng::count(name, ' ');
|
||||
|
||||
pread.open(d.path() / "cmdline");
|
||||
if (not pread.good()) continue;
|
||||
long_string.clear();
|
||||
while(getline(pread, long_string, '\0')) cmd += long_string + ' ';
|
||||
pread.close();
|
||||
if (not cmd.empty()) cmd.pop_back();
|
||||
|
||||
pread.open(d.path() / "status");
|
||||
if (not pread.good()) continue;
|
||||
string uid;
|
||||
string line;
|
||||
while (not pread.eof()) {
|
||||
getline(pread, line, ':');
|
||||
if (line == "Uid") {
|
||||
pread.ignore();
|
||||
getline(pread, uid, '\t');
|
||||
break;
|
||||
} else {
|
||||
pread.ignore(SSmax, '\n');
|
||||
}
|
||||
}
|
||||
pread.close();
|
||||
user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid;
|
||||
|
||||
cache[new_proc.pid] = {name, cmd, user, name_offset};
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::invalid_argument&) { continue; }
|
||||
catch (const std::out_of_range&) { continue; }
|
||||
new_proc.name = cache.at(new_proc.pid).name;
|
||||
new_proc.cmd = cache.at(new_proc.pid).cmd;
|
||||
new_proc.user = cache.at(new_proc.pid).user;
|
||||
|
||||
stat_loop_done:
|
||||
pread.close();
|
||||
//? Parse /proc/[pid]/stat
|
||||
pread.open(d.path() / "stat");
|
||||
if (not pread.good()) continue;
|
||||
|
||||
if (x-offset < 24) continue;
|
||||
//? Check cached value for whitespace characters in name and set offset to get correct fields from stat file
|
||||
size_t& offset = cache.at(new_proc.pid).name_offset;
|
||||
short_str.clear();
|
||||
size_t x = 0, next_x = 3;
|
||||
uint64_t cpu_t = 0;
|
||||
try {
|
||||
for (;;) {
|
||||
while (pread.good() and ++x - offset < next_x) {
|
||||
pread.ignore(SSmax, ' ');
|
||||
}
|
||||
if (pread.bad()) goto stat_loop_done;
|
||||
|
||||
//? Process cpu usage since last update
|
||||
new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache.at(new_proc.pid).cpu_t) / max(1ul, cputimes - old_cputimes)) / 10.0;
|
||||
getline(pread, short_str, ' ');
|
||||
|
||||
//? Process cumulative cpu usage since process start
|
||||
new_proc.cpu_c = (double)cpu_t / max(1.0, (uptime * Shared::clkTck) - cache.at(new_proc.pid).cpu_s);
|
||||
switch (x-offset) {
|
||||
case 3: { //? Process state
|
||||
new_proc.state = short_str.at(0);
|
||||
continue;
|
||||
}
|
||||
case 4: { //? Parent pid
|
||||
new_proc.ppid = stoull(short_str);
|
||||
next_x = 14;
|
||||
continue;
|
||||
}
|
||||
case 14: { //? Process utime
|
||||
cpu_t = stoull(short_str);
|
||||
continue;
|
||||
}
|
||||
case 15: { //? Process stime
|
||||
cpu_t += stoull(short_str);
|
||||
next_x = 19;
|
||||
continue;
|
||||
}
|
||||
case 19: { //? Nice value
|
||||
new_proc.p_nice = stoull(short_str);
|
||||
continue;
|
||||
}
|
||||
case 20: { //? Number of threads
|
||||
new_proc.threads = stoull(short_str);
|
||||
if (cache.at(new_proc.pid).cpu_s == 0) {
|
||||
next_x = 22;
|
||||
cache.at(new_proc.pid).cpu_t = cpu_t;
|
||||
}
|
||||
else
|
||||
next_x = 24;
|
||||
continue;
|
||||
}
|
||||
case 22: { //? Save cpu seconds to cache if missing
|
||||
cache.at(new_proc.pid).cpu_s = stoull(short_str);
|
||||
next_x = 24;
|
||||
continue;
|
||||
}
|
||||
case 24: { //? RSS memory (can be inaccurate, but parsing smaps increases total cpu usage by ~20x)
|
||||
new_proc.mem = stoull(short_str) * Shared::pageSize;
|
||||
next_x = 39;
|
||||
continue;
|
||||
}
|
||||
case 39: { //? CPU number last executed on
|
||||
new_proc.cpu_n = stoull(short_str);
|
||||
goto stat_loop_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//? Update cache with latest cpu times
|
||||
cache.at(new_proc.pid).cpu_t = cpu_t;
|
||||
}
|
||||
catch (const std::invalid_argument&) { continue; }
|
||||
catch (const std::out_of_range&) { continue; }
|
||||
|
||||
stat_loop_done:
|
||||
pread.close();
|
||||
|
||||
if (x-offset < 24) continue;
|
||||
|
||||
//? Process cpu usage since last update
|
||||
new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache.at(new_proc.pid).cpu_t) / max(1ul, cputimes - old_cputimes)) / 10.0;
|
||||
|
||||
//? Process cumulative cpu usage since process start
|
||||
new_proc.cpu_c = (double)cpu_t / max(1.0, (uptime * Shared::clkTck) - cache.at(new_proc.pid).cpu_s);
|
||||
|
||||
//? Update cache with latest cpu times
|
||||
cache.at(new_proc.pid).cpu_t = cpu_t;
|
||||
|
||||
if (show_detailed and not got_detailed and new_proc.pid == detailed_pid) {
|
||||
got_detailed = true;
|
||||
}
|
||||
|
||||
//? Push process to vector
|
||||
procs.push_back(new_proc);
|
||||
|
||||
if (show_detailed and not got_detailed and new_proc.pid == detailed_pid) {
|
||||
got_detailed = true;
|
||||
}
|
||||
|
||||
//? Push process to vector
|
||||
procs.push_back(new_proc);
|
||||
//? Clear dead processes from cache at a regular interval
|
||||
if (++counter >= 1000 or (cache.size() > procs.size() + 100)) {
|
||||
counter = 0;
|
||||
unordered_flat_map<size_t, p_cache> r_cache;
|
||||
r_cache.reserve(procs.size());
|
||||
rng::for_each(procs, [&r_cache](const auto &p) {
|
||||
if (cache.contains(p.pid))
|
||||
r_cache[p.pid] = cache.at(p.pid);
|
||||
});
|
||||
cache = std::move(r_cache);
|
||||
}
|
||||
|
||||
//? Update the details info box for process if active
|
||||
if (show_detailed and got_detailed) {
|
||||
_collect_details(detailed_pid, round(uptime), procs);
|
||||
}
|
||||
else if (show_detailed and not got_detailed and detailed.status != "Dead") {
|
||||
detailed.status = "Dead";
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
old_cputimes = cputimes;
|
||||
current_procs = procs;
|
||||
}
|
||||
|
||||
//* Clear dead processes from cache at a regular interval
|
||||
if (++counter >= 1000 or (cache.size() > procs.size() + 100)) {
|
||||
counter = 0;
|
||||
unordered_flat_map<size_t, p_cache> r_cache;
|
||||
r_cache.reserve(procs.size());
|
||||
rng::for_each(procs, [&r_cache](const auto &p) {
|
||||
if (cache.contains(p.pid))
|
||||
r_cache[p.pid] = cache.at(p.pid);
|
||||
});
|
||||
cache = std::move(r_cache);
|
||||
}
|
||||
|
||||
//* Update the details info box for process if active
|
||||
if (show_detailed and got_detailed) {
|
||||
_collect_details(detailed_pid, round(uptime), procs);
|
||||
}
|
||||
else if (show_detailed and not got_detailed and detailed.status != "Dead") {
|
||||
detailed.status = "Dead";
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
old_cputimes = cputimes;
|
||||
current_procs = procs;
|
||||
|
||||
proc_no_update:
|
||||
//* ---------------------------------------------Collection done-----------------------------------------------
|
||||
|
||||
//* Match filter if defined
|
||||
if (not tree and not filter.empty()) {
|
||||
|
|
|
@ -229,6 +229,9 @@ namespace Tools {
|
|||
//* Put current thread to sleep for <ms> milliseconds
|
||||
inline void sleep_ms(const size_t& ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); }
|
||||
|
||||
//* Put current thread to sleep for <micros> microseconds
|
||||
inline void sleep_micros(const size_t& micros) { std::this_thread::sleep_for(std::chrono::microseconds(micros)); }
|
||||
|
||||
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||
string ljust(string str, const size_t x, const bool utf=false, const bool wide=false, const bool limit=true);
|
||||
|
||||
|
|
Loading…
Reference in a new issue