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
But will need G++ 10 or Clang 11 if compiling from source.
But will need G++ 10 if compiling from source.
## Screenshots
@ -165,7 +165,7 @@ Config files stored in "$HOME/.config/btop" folder
#### 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
#? Config file for btop v. 0.0.1

View File

@ -16,18 +16,12 @@ indent = tab
tab-size = 4
*/
#include <iostream>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include <tuple>
#include <thread>
#include <future>
#include <atomic>
#include <filesystem>
#include <unistd.h>
#include <btop_globs.h>
#include <btop_tools.h>
@ -149,7 +143,7 @@ int main(int argc, char **argv){
if (argc > 1) argumentParser(argc, argv);
//? 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;
if (Global::proc_path.empty()) {
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, ' ') +
"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){
(void) lcpu_s;
ostring += rjustify(to_string(lpid), 8) + " " + ljustify(lname, 16) + " " + ljustify(lcmd, Term::width - 66, true) + " " +
rjustify(to_string(lthread), 5) + " " + ljustify(luser, 10) + " " + rjustify(floating_humanizer(lmem, true), 5) + string(11, ' ');
ostring += (lcpu > 100) ? rjustify(to_string(lcpu), 3) + " " : rjustify(to_string(lcpu), 4);
for (Proc::proc_info& procs : plist){
ostring += rjustify(to_string(procs.pid), 8) + " " + ljustify(procs.name, 16) + " " + ljustify(procs.cmd, Term::width - 66, true) + " " +
rjustify(to_string(procs.threads), 5) + " " + ljustify(procs.user, 10) + " " + rjustify(floating_humanizer(procs.mem, true), 5) + string(11, ' ');
ostring += (procs.cpu_p > 100) ? rjustify(to_string(procs.cpu_p), 3) + " " : rjustify(to_string(procs.cpu_p), 4);
ostring += "\n";
if (lc++ > Term::height - 20) break;
}

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@ tab-size = 4
*/
#ifndef _btop_linux_included_
#define _btop_linux_included_
#define _btop_linux_included_ 1
#include <string>
#include <vector>
@ -39,29 +39,42 @@ using namespace std;
namespace Global {
const string SYSTEM = "linux";
const string System = "linux";
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 {
uint64_t tstamp;
long int clk_tck;
struct p_cache { string name, cmd, user; };
map<int, p_cache> cache;
struct p_cache {
string name, cmd, user;
uint64_t cpu_t = 0, cpu_s = 0;
};
map<uint, p_cache> cache;
map<string, string> uid_user;
fs::path passwd_path;
fs::file_time_type passwd_time;
map<int, uint64_t> cpu_times;
map<int, uint64_t> cpu_second;
uint counter = 0;
long page_size = sysconf(_SC_PAGE_SIZE);
}
atomic<bool> stop;
atomic<bool> running;
atomic<bool> stop (false);
atomic<bool> running (false);
vector<string> sort_vector = {
"pid",
"name",
@ -76,7 +89,7 @@ namespace Proc {
//* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c
struct proc_info {
int pid;
uint pid;
string name, cmd;
size_t threads;
string user;
@ -88,18 +101,20 @@ namespace Proc {
//* Collects process information from /proc and returns a vector of proc_info structs
auto collect(string sorting="pid", bool reverse=false, string filter=""){
running.store(true);
int pid;
uint pid;
uint64_t cpu_t, rss_mem;
double cpu, cpu_s;
bool new_cache;
size_t threads;
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;
if (since_last < 1) since_last = 1;
auto uptime = system_uptime();
auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7;
vector<string> pstat;
vector<proc_info> procs;
vector<uint> c_pids;
//* Update uid_user map if /etc/passwd changed since last run
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)){
if (stop.load()) {
procs.clear();
@ -132,50 +147,15 @@ namespace Proc {
pid_str = fs::path(d.path()).filename();
cpu = 0.0;
rss_mem = 0;
new_cache = false;
if (d.is_directory() && isdigit(pid_str[0])) {
pid = stoi(pid_str);
//* 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();
}
pid = stoul(pid_str);
c_pids.push_back(pid);
//* Cache program name, command and username
if (!cache.contains(pid)) {
name.clear(); cmd.clear(); user.clear();
new_cache = true;
if (fs::exists((string)d.path() + "/comm")) {
pread.clear(); name.clear();
ifstream pread((string)d.path() + "/comm");
@ -210,6 +190,54 @@ namespace Proc {
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
if (!filter.empty() &&
pid_str.find(filter) == string::npos && //? Pid
@ -218,7 +246,7 @@ namespace Proc {
cache[pid].user.find(filter) == string::npos //? User
) 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));
}
}
@ -235,15 +263,15 @@ namespace Proc {
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 5: return (reverse) ? a.mem < b.mem : a.mem > b.mem;
case 6: return (reverse) ? a.pid < b.pid : a.pid > b.pid;
case 7: 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.cpu_c < b.cpu_c : a.cpu_c > b.cpu_c;
}
return false;
}
);
//* 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;
for (size_t i = 0, offset = 0; i < procs.size(); i++) {
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
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;
cache.clear();
cpu_times.clear();
cpu_second.clear();
for (auto& p : c_pids){
r_cache[p] = cache[p];
}
cache = move(r_cache);
}
tstamp = time_ms();
@ -266,14 +297,13 @@ namespace Proc {
return procs;
}
//* Initialize needed variables for collect
void init(){
clk_tck = sysconf(_SC_CLK_TCK);
tstamp = time_ms();
stop.store(false);
passwd_path = (fs::exists(fs::path("/etc/passwd"))) ? fs::path("/etc/passwd") : passwd_path;
uint i = 0;
for (auto& item : sort_vector) sort_map[item] = i++;
// collect();
}
};

View File

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

View File

@ -17,16 +17,13 @@ tab-size = 4
*/
#ifndef _btop_tools_included_
#define _btop_tools_included_
#define _btop_tools_included_ 1
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include <iostream>
#include <chrono>
#include <thread>
#include <algorithm>
#include <fstream>
#include <regex>
#include <unistd.h>
@ -346,14 +343,6 @@ string sec_to_dhms(uint sec){
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
//* bit=True or defaults to bytes
//* start=int to set 1024 multiplier starting unit