changed proc cache to be more effective

This commit is contained in:
aristocratos 2021-05-15 13:24:24 +02:00
parent c007282317
commit 8364d856c8
8 changed files with 106 additions and 94 deletions

View File

@ -112,7 +112,7 @@ Look to the creators of the terminal emulator you use to fix these issues if the
None None
But will need G++ 10 or Clang 11 if compiling from source. But will need G++ 10 if compiling from source.
## Screenshots ## Screenshots
@ -165,7 +165,7 @@ Config files stored in "$HOME/.config/btop" folder
#### btop.cfg: (auto generated if not found) #### btop.cfg: (auto generated if not found)
"/etc/bpytop.conf" will be used as default seed for config file creation if it exists. ("/usr/local/etc/bpytop.conf" on BSD) "/etc/btop.conf" will be used as default seed for config file creation if it exists. ("/usr/local/etc/btop.conf" on BSD)
```bash ```bash
#? Config file for btop v. 0.0.1 #? Config file for btop v. 0.0.1

View File

@ -16,18 +16,12 @@ indent = tab
tab-size = 4 tab-size = 4
*/ */
#include <iostream>
#include <string> #include <string>
#include <cmath>
#include <vector> #include <vector>
#include <map>
#include <tuple>
#include <thread> #include <thread>
#include <future> #include <future>
#include <atomic> #include <atomic>
#include <filesystem>
#include <unistd.h>
#include <btop_globs.h> #include <btop_globs.h>
#include <btop_tools.h> #include <btop_tools.h>
@ -149,7 +143,7 @@ int main(int argc, char **argv){
if (argc > 1) argumentParser(argc, argv); if (argc > 1) argumentParser(argc, argv);
//? Init for Linux //? Init for Linux
if (Global::SYSTEM == "linux") { if (Global::System == "linux") {
Global::proc_path = (fs::is_directory(fs::path("/proc"))) ? fs::path("/proc") : Global::proc_path; Global::proc_path = (fs::is_directory(fs::path("/proc"))) ? fs::path("/proc") : Global::proc_path;
if (Global::proc_path.empty()) { if (Global::proc_path.empty()) {
cout << "ERROR: Proc filesystem not detected!" << endl; cout << "ERROR: Proc filesystem not detected!" << endl;
@ -293,11 +287,10 @@ int main(int argc, char **argv){
cout << Mv::restore << Mv::u(2) << Mv::r(20) << rjustify("Filter: " + filter + filter_cur + string(Term::width / 3, ' ') + cout << Mv::restore << Mv::u(2) << Mv::r(20) << rjustify("Filter: " + filter + filter_cur + string(Term::width / 3, ' ') +
"Sorting: " + Proc::sort_vector[sortint], Term::width - 22, true, filtering) << Mv::restore << flush; "Sorting: " + Proc::sort_vector[sortint], Term::width - 22, true, filtering) << Mv::restore << flush;
for (auto& [lpid, lname, lcmd, lthread, luser, lmem, lcpu, lcpu_s] : plist){ for (Proc::proc_info& procs : plist){
(void) lcpu_s; ostring += rjustify(to_string(procs.pid), 8) + " " + ljustify(procs.name, 16) + " " + ljustify(procs.cmd, Term::width - 66, true) + " " +
ostring += rjustify(to_string(lpid), 8) + " " + ljustify(lname, 16) + " " + ljustify(lcmd, Term::width - 66, true) + " " + rjustify(to_string(procs.threads), 5) + " " + ljustify(procs.user, 10) + " " + rjustify(floating_humanizer(procs.mem, true), 5) + string(11, ' ');
rjustify(to_string(lthread), 5) + " " + ljustify(luser, 10) + " " + rjustify(floating_humanizer(lmem, true), 5) + string(11, ' '); ostring += (procs.cpu_p > 100) ? rjustify(to_string(procs.cpu_p), 3) + " " : rjustify(to_string(procs.cpu_p), 4);
ostring += (lcpu > 100) ? rjustify(to_string(lcpu), 3) + " " : rjustify(to_string(lcpu), 4);
ostring += "\n"; ostring += "\n";
if (lc++ > Term::height - 20) break; if (lc++ > Term::height - 20) break;
} }

View File

@ -17,7 +17,7 @@ tab-size = 4
*/ */
#ifndef _btop_config_included_ #ifndef _btop_config_included_
#define _btop_config_included_ #define _btop_config_included_ 1
#include <string> #include <string>
#include <vector> #include <vector>

View File

@ -17,7 +17,7 @@ tab-size = 4
*/ */
#ifndef _btop_globs_included_ #ifndef _btop_globs_included_
#define _btop_globs_included_ #define _btop_globs_included_ 1
#include <string> #include <string>
#include <map> #include <map>

View File

@ -17,7 +17,7 @@ tab-size = 4
*/ */
#ifndef _btop_input_included_ #ifndef _btop_input_included_
#define _btop_input_included_ #define _btop_input_included_ 1
#include <string> #include <string>
#include <map> #include <map>

View File

@ -17,7 +17,7 @@ tab-size = 4
*/ */
#ifndef _btop_linux_included_ #ifndef _btop_linux_included_
#define _btop_linux_included_ #define _btop_linux_included_ 1
#include <string> #include <string>
#include <vector> #include <vector>
@ -39,29 +39,42 @@ using namespace std;
namespace Global { namespace Global {
const string SYSTEM = "linux"; const string System = "linux";
filesystem::path proc_path; filesystem::path proc_path;
} }
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
double system_uptime(){
string upstr;
ifstream pread("/proc/uptime");
getline(pread, upstr, ' ');
pread.close();
return stod(upstr);
}
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
namespace Proc { namespace Proc {
namespace { namespace {
uint64_t tstamp; uint64_t tstamp;
long int clk_tck; long int clk_tck;
struct p_cache { string name, cmd, user; }; struct p_cache {
map<int, p_cache> cache; string name, cmd, user;
uint64_t cpu_t = 0, cpu_s = 0;
};
map<uint, p_cache> cache;
map<string, string> uid_user; map<string, string> uid_user;
fs::path passwd_path; fs::path passwd_path;
fs::file_time_type passwd_time; fs::file_time_type passwd_time;
map<int, uint64_t> cpu_times;
map<int, uint64_t> cpu_second;
uint counter = 0; uint counter = 0;
long page_size = sysconf(_SC_PAGE_SIZE); long page_size = sysconf(_SC_PAGE_SIZE);
} }
atomic<bool> stop; atomic<bool> stop (false);
atomic<bool> running; atomic<bool> running (false);
vector<string> sort_vector = { vector<string> sort_vector = {
"pid", "pid",
"name", "name",
@ -76,7 +89,7 @@ namespace Proc {
//* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c //* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c
struct proc_info { struct proc_info {
int pid; uint pid;
string name, cmd; string name, cmd;
size_t threads; size_t threads;
string user; string user;
@ -88,18 +101,20 @@ namespace Proc {
//* Collects process information from /proc and returns a vector of proc_info structs //* Collects process information from /proc and returns a vector of proc_info structs
auto collect(string sorting="pid", bool reverse=false, string filter=""){ auto collect(string sorting="pid", bool reverse=false, string filter=""){
running.store(true); running.store(true);
int pid; uint pid;
uint64_t cpu_t, rss_mem; uint64_t cpu_t, rss_mem;
double cpu, cpu_s; double cpu, cpu_s;
bool new_cache;
size_t threads; size_t threads;
ifstream pread; ifstream pread;
string pid_str, name, cmd, attr, user, instr, uid, status, tmpstr, smap; string pid_str, name, cmd, attr, user, instr, uid, status, tmpstr;
auto since_last = time_ms() - tstamp; auto since_last = time_ms() - tstamp;
if (since_last < 1) since_last = 1; if (since_last < 1) since_last = 1;
auto uptime = system_uptime(); auto uptime = system_uptime();
auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7; auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7;
vector<string> pstat; vector<string> pstat;
vector<proc_info> procs; vector<proc_info> procs;
vector<uint> c_pids;
//* Update uid_user map if /etc/passwd changed since last run //* Update uid_user map if /etc/passwd changed since last run
if (!passwd_path.empty() && fs::last_write_time(passwd_path) != passwd_time) { if (!passwd_path.empty() && fs::last_write_time(passwd_path) != passwd_time) {
@ -121,7 +136,7 @@ namespace Proc {
} }
//* Iterate over all pid directories in /proc and get relevant values //* Iterate over all pids in /proc and get relevant values
for (auto& d: fs::directory_iterator(Global::proc_path)){ for (auto& d: fs::directory_iterator(Global::proc_path)){
if (stop.load()) { if (stop.load()) {
procs.clear(); procs.clear();
@ -132,50 +147,15 @@ namespace Proc {
pid_str = fs::path(d.path()).filename(); pid_str = fs::path(d.path()).filename();
cpu = 0.0; cpu = 0.0;
rss_mem = 0; rss_mem = 0;
new_cache = false;
if (d.is_directory() && isdigit(pid_str[0])) { if (d.is_directory() && isdigit(pid_str[0])) {
pid = stoi(pid_str); pid = stoul(pid_str);
c_pids.push_back(pid);
//* Get cpu usage, cpu cumulative and threads from /proc/[pid]/stat
if (fs::exists((string)d.path() + "/stat")) {
pread.clear(); pstat.clear();
ifstream pread((string)d.path() + "/stat");
if (pread.good()) while (getline(pread, instr, ' ')) pstat.push_back(instr);
pread.close();
if (pstat.size() < 37) continue;
//? Process number of threads
threads = stoul(pstat[19]);
//? Process utime + stime
cpu_t = stoull(pstat[13]) + stoull(pstat[14]);
if (!cpu_times.contains(pid)) cpu_times[pid] = cpu_t;
//? Cache process start time
if (!cpu_second.contains(pid)) cpu_second[pid] = stoull(pstat[21]);
//? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion
cpu = static_cast<double>(100000 * (cpu_t - cpu_times[pid]) / since_last) / clk_tck;
//? Process cumulative cpu usage since process start
cpu_s = static_cast<double>((cpu_t / clk_tck) / (uptime - (cpu_second[pid] / clk_tck)));
cpu_times[pid] = cpu_t;
}
//* Get RSS memory in bytes from /proc/[pid]/statm
if (fs::exists((string)d.path() + "/statm")) {
pread.clear(); tmpstr.clear();
ifstream pread((string)d.path() + "/statm");
if (pread.good()) {
pread.ignore(numeric_limits<streamsize>::max(), ' ');
pread >> rss_mem;
rss_mem *= page_size;
}
pread.close();
}
//* Cache program name, command and username //* Cache program name, command and username
if (!cache.contains(pid)) { if (!cache.contains(pid)) {
name.clear(); cmd.clear(); user.clear();
new_cache = true;
if (fs::exists((string)d.path() + "/comm")) { if (fs::exists((string)d.path() + "/comm")) {
pread.clear(); name.clear(); pread.clear(); name.clear();
ifstream pread((string)d.path() + "/comm"); ifstream pread((string)d.path() + "/comm");
@ -210,6 +190,54 @@ namespace Proc {
cache[pid] = p_cache(name, cmd, user); cache[pid] = p_cache(name, cmd, user);
} }
//* Get cpu usage, cpu cumulative and threads from /proc/[pid]/stat
if (fs::exists((string)d.path() + "/stat")) {
pread.clear(); pstat.clear();
ifstream pread((string)d.path() + "/stat");
if (pread.good()) while (getline(pread, instr, ' ')) pstat.push_back(instr);
pread.close();
if (pstat.size() < 37) continue;
//? Process number of threads
threads = stoul(pstat[19]);
//? Process utime + stime
cpu_t = stoull(pstat[13]) + stoull(pstat[14]);
//? Cache cpu times and cpu seconds
if (new_cache) {
cache[pid].cpu_t = cpu_t;
cache[pid].cpu_s = stoull(pstat[21]);
}
//? Cache process start time
// if (!cpu_second.contains(pid)) cpu_second[pid] = stoull(pstat[21]);
//? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion
cpu = static_cast<double>(100000 * (cpu_t - cache[pid].cpu_t) / since_last) / clk_tck;
//? Process cumulative cpu usage since process start
cpu_s = static_cast<double>((cpu_t / clk_tck) / (uptime - (cache[pid].cpu_s / clk_tck)));
//? Add latest cpu times to cache
cache[pid].cpu_t = cpu_t;
}
//* Get RSS memory in bytes from /proc/[pid]/statm
if (fs::exists((string)d.path() + "/statm")) {
pread.clear(); tmpstr.clear();
ifstream pread((string)d.path() + "/statm");
if (pread.good()) {
pread.ignore(numeric_limits<streamsize>::max(), ' ');
pread >> rss_mem;
rss_mem *= page_size;
}
pread.close();
}
// //* Match filter if applicable // //* Match filter if applicable
if (!filter.empty() && if (!filter.empty() &&
pid_str.find(filter) == string::npos && //? Pid pid_str.find(filter) == string::npos && //? Pid
@ -218,7 +246,7 @@ namespace Proc {
cache[pid].user.find(filter) == string::npos //? User cache[pid].user.find(filter) == string::npos //? User
) continue; ) continue;
//* Create tuple //* Create proc_info
procs.push_back(proc_info(pid, cache[pid].name, cache[pid].cmd, threads, cache[pid].user, rss_mem, cpu, cpu_s)); procs.push_back(proc_info(pid, cache[pid].name, cache[pid].cmd, threads, cache[pid].user, rss_mem, cpu, cpu_s));
} }
} }
@ -235,15 +263,15 @@ namespace Proc {
case 3: return (reverse) ? a.threads < b.threads : a.threads > b.threads; case 3: return (reverse) ? a.threads < b.threads : a.threads > b.threads;
case 4: return (reverse) ? a.user < b.user : a.user > b.user; case 4: return (reverse) ? a.user < b.user : a.user > b.user;
case 5: return (reverse) ? a.mem < b.mem : a.mem > b.mem; case 5: return (reverse) ? a.mem < b.mem : a.mem > b.mem;
case 6: return (reverse) ? a.pid < b.pid : a.pid > b.pid; case 6: return (reverse) ? a.cpu_p < b.cpu_p : a.cpu_p > b.cpu_p;
case 7: return (reverse) ? a.pid < b.pid : a.pid > b.pid; case 7: return (reverse) ? a.cpu_c < b.cpu_c : a.cpu_c > b.cpu_c;
} }
return false; return false;
} }
); );
//* When using "cpu lazy" sorting push processes with high cpu usage to the front regardless of cumulative usage //* When using "cpu lazy" sorting push processes with high cpu usage to the front regardless of cumulative usage
if (sortint == 6 && !reverse) { if (sortint == 7 && !reverse) {
double max = 10.0, target = 30.0; double max = 10.0, target = 30.0;
for (size_t i = 0, offset = 0; i < procs.size(); i++) { for (size_t i = 0, offset = 0; i < procs.size(); i++) {
if (i <= 5 && procs[i].cpu_p > max) max = procs[i].cpu_p; if (i <= 5 && procs[i].cpu_p > max) max = procs[i].cpu_p;
@ -254,11 +282,14 @@ namespace Proc {
} }
//* Clear all cached values at a regular interval to get rid of dead processes //* Clear all cached values at a regular interval to get rid of dead processes
if (++counter >= 10000 || (filter.empty() && cache.size() > procs.size() + 100)) { if (++counter >= 5 || (filter.empty() && cache.size() > procs.size() + 100)) {
map<uint, p_cache> r_cache;
counter = 0; counter = 0;
cache.clear(); for (auto& p : c_pids){
cpu_times.clear(); r_cache[p] = cache[p];
cpu_second.clear(); }
cache = move(r_cache);
} }
tstamp = time_ms(); tstamp = time_ms();
@ -266,14 +297,13 @@ namespace Proc {
return procs; return procs;
} }
//* Initialize needed variables for collect
void init(){ void init(){
clk_tck = sysconf(_SC_CLK_TCK); clk_tck = sysconf(_SC_CLK_TCK);
tstamp = time_ms(); tstamp = time_ms();
stop.store(false);
passwd_path = (fs::exists(fs::path("/etc/passwd"))) ? fs::path("/etc/passwd") : passwd_path; passwd_path = (fs::exists(fs::path("/etc/passwd"))) ? fs::path("/etc/passwd") : passwd_path;
uint i = 0; uint i = 0;
for (auto& item : sort_vector) sort_map[item] = i++; for (auto& item : sort_vector) sort_map[item] = i++;
// collect();
} }
}; };

View File

@ -17,7 +17,7 @@ tab-size = 4
*/ */
#ifndef _btop_theme_included_ #ifndef _btop_theme_included_
#define _btop_theme_included_ #define _btop_theme_included_ 1
#include <string> #include <string>
#include <cmath> #include <cmath>

View File

@ -17,16 +17,13 @@ tab-size = 4
*/ */
#ifndef _btop_tools_included_ #ifndef _btop_tools_included_
#define _btop_tools_included_ #define _btop_tools_included_ 1
#include <string> #include <string>
#include <cmath> #include <cmath>
#include <vector> #include <vector>
#include <map> #include <iostream>
#include <chrono> #include <chrono>
#include <thread>
#include <algorithm>
#include <fstream>
#include <regex> #include <regex>
#include <unistd.h> #include <unistd.h>
@ -346,14 +343,6 @@ string sec_to_dhms(uint sec){
return out; return out;
} }
double system_uptime(){
string upstr;
ifstream pread("/proc/uptime");
getline(pread, upstr, ' ');
pread.close();
return stod(upstr);
}
//* Scales up in steps of 1024 to highest possible unit and returns string with unit suffixed //* Scales up in steps of 1024 to highest possible unit and returns string with unit suffixed
//* bit=True or defaults to bytes //* bit=True or defaults to bytes
//* start=int to set 1024 multiplier starting unit //* start=int to set 1024 multiplier starting unit