diff --git a/btop.cpp b/btop.cpp index 893c272..a4ea95a 100644 --- a/btop.cpp +++ b/btop.cpp @@ -19,10 +19,12 @@ tab-size = 4 #include #include +#include #include #include #include #include +#include #include #include @@ -33,20 +35,27 @@ tab-size = 4 #include #if defined(__linux__) + #define LINUX 1 #include #elif defined(__unix__) || !defined(__APPLE__) && defined(__MACH__) #include #if defined(BSD) // #include + #error BSD support not yet implemented! #endif #elif defined(__APPLE__) && defined(__MACH__) #include #if TARGET_OS_MAC == 1 + #define OSX 1 // #include + #error OSX support not yet implemented! #endif +#else + #error Platform not supported or could not determine platform! #endif -using std::string, std::vector, std::array, std::map, std::atomic, std::endl, std::cout, std::views::iota; +using std::string, std::vector, std::array, std::map, std::atomic, std::endl, std::cout, std::views::iota, std::list, std::accumulate; +using std::flush, std::endl, std::future, std::string_literals::operator""s, std::future_status; using namespace Tools; @@ -133,21 +142,21 @@ string my_worker(int x){ //? --------------------------------------------- Main starts here! --------------------------------------------------- int main(int argc, char **argv){ - using namespace std; + // using namespace std; //? Init cout.setf(std::ios::boolalpha); if (argc > 1) argumentParser(argc, argv); - //? Init for Linux - if (Global::System == "linux") { + #if defined(LINUX) + //? Linux init 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; exit(1); } - } + #endif //? Initialize terminal and set options if (!Term::init()) { @@ -172,19 +181,22 @@ int main(int argc, char **argv){ int debug = 0; int tests = 0; + bool debuginit = false; // cout << Theme("main_bg") << Term::clear << flush; bool thread_test = false; - if (debug < 2) cout << Term::alt_screen << Term::hide_cursor << flush; + if (!debuginit) cout << Term::alt_screen << Term::hide_cursor << flush; cout << Theme::c("main_fg") << Theme::c("main_bg") << Term::clear << endl; cout << Mv::r(Term::width / 2 - Global::banner_width / 2) << Global::banner << endl; // cout << string(Term::width - 1, '-') << endl; + size_t blen = (Term::width > 200) ? 200 : Term::width; + if (Term::width > 203) cout << Mv::r(Term::width / 2 - blen / 2) << flush; int ill = 0; - for (int i : iota(0, (int)Term::width)){ - ill = (i <= (int)Term::width / 2) ? i : ill - 1; + for (int i : iota(0, (int)blen)){ + ill = (i <= (int)blen / 2) ? i : ill - 1; cout << Theme::g("used")[ill] << "-"; } cout << Fx::reset << endl; @@ -234,7 +246,7 @@ int main(int argc, char **argv){ while (outputs.size() < 10){ for (int i : iota(0, 10)){ - if (runners[i].valid() && runners[i].wait_for(chrono::milliseconds(10)) == future_status::ready) { + if (runners[i].valid() && runners[i].wait_for(std::chrono::milliseconds(10)) == future_status::ready) { outputs[i] = runners[i].get(); cout << "Thread " << i << " : " << outputs[i] << endl; } @@ -271,7 +283,8 @@ int main(int argc, char **argv){ uint lc; string ostring; uint64_t tsl, timestamp2; - uint timer = 2000; + list avgtimes; + uint timer = 1000; bool filtering = false; bool reversing = false; int sortint = Proc::sort_map["cpu lazy"]; @@ -300,7 +313,7 @@ int main(int argc, char **argv){ lc = 0; filter_cur = (filtering) ? Fx::bl + "█" + Fx::reset : ""; ostring = Mv::save + Mv::u(2) + Mv::r(20) + trans(rjust("Filter: " + filter + filter_cur + string(Term::width / 3, ' ') + - "Sorting: " + string(Proc::sort_array[sortint]), Term::width - 25, true, filtering)) + Mv::restore; + "Sorting: " + string(Proc::sort_array[sortint]), Term::width - 25, true, filtering)) + Mv::restore; for (auto& p : plist){ ostring += Mv::r(1) + greyscale[lc] + rjust(to_string(p.pid), 8) + " " + ljust(p.name, 16) + " " + ljust(p.cmd, Term::width - 66, true) + " " + @@ -310,8 +323,11 @@ int main(int argc, char **argv){ if (lc++ > Term::height - 21) break; } + avgtimes.push_front(timestamp); + if (avgtimes.size() > 100) avgtimes.pop_back(); cout << pbox << ostring << Fx::reset << "\n" << endl; - cout << "Processes call took: " << timestamp << "ms. Drawing took: " << time_ms() - timestamp2 << "ms." << endl; + cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << timestamp << "ms. Average: " << accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size() << + "ms of " << avgtimes.size() << " samples. Drawing took: " << time_ms() - timestamp2 << "ms. " << endl; while (time_ms() < tsl) { if (Input::poll(tsl - time_ms())) key = Input::get(); @@ -413,6 +429,6 @@ int main(int argc, char **argv){ if (debug == 1) Input::wait(); Term::restore(); - if (debug < 2) cout << Term::normal_screen << Term::show_cursor << flush; + if (!debuginit) cout << Term::normal_screen << Term::show_cursor << flush; return 0; } diff --git a/src/btop_linux.h b/src/btop_linux.h index 8fae095..3dcbb51 100644 --- a/src/btop_linux.h +++ b/src/btop_linux.h @@ -21,12 +21,15 @@ tab-size = 4 #include #include +#include #include #include #include #include #include #include +#include + #include @@ -35,7 +38,7 @@ tab-size = 4 #include -using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, std::unordered_map; +using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, std::unordered_map, std::deque, std::list; namespace fs = std::filesystem; using namespace Tools; @@ -61,6 +64,7 @@ double system_uptime(){ namespace Proc { namespace { uint64_t tstamp; + size_t numpids = 500; long int clk_tck; struct p_cache { string name, cmd, user; @@ -89,7 +93,7 @@ namespace Proc { }; unordered_map sort_map; - //* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c + //* proc_info: pid, name, cmd, threads, user, mem, cpu_p, cpu_c, state, tty, cpu_n, p_nice, ppid struct proc_info { uint pid; string name, cmd; @@ -97,16 +101,21 @@ namespace Proc { string user; uint64_t mem; double cpu_p, cpu_c; + char state; + int cpu_n, p_nice; + uint ppid; }; //* 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); - uint pid; + uint pid, ppid; uint64_t cpu_t, rss_mem; double cpu, cpu_s; bool new_cache; + char state; + int cpu_n, p_nice; size_t threads; ifstream pread; string pid_str, name, cmd, attr, user, instr, uid, status, tmpstr; @@ -115,8 +124,13 @@ namespace Proc { auto uptime = system_uptime(); auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7; vector pstat; + pstat.reserve(40); vector procs; + procs.reserve((numpids + 10)); vector c_pids; + c_pids.reserve((numpids + 10)); + numpids = 0; + uint parenthesis = 0; //* Update uid_user map if /etc/passwd changed since last run if (!passwd_path.empty() && fs::last_write_time(passwd_path) != passwd_time) { @@ -146,9 +160,10 @@ namespace Proc { stop.store(false); return procs; } + numpids++; pid_str = fs::path(d.path()).filename(); - cpu = 0.0; cpu_s = 0.0; cpu_t = 0; - rss_mem = 0; threads = 0; + cpu = 0.0; cpu_s = 0.0; cpu_t = 0; cpu_n = 0; + rss_mem = 0; threads = 0; state = '0'; ppid = 0; p_nice = 0; new_cache = false; if (d.is_directory() && isdigit(pid_str[0])) { pid = stoul(pid_str); @@ -194,27 +209,47 @@ namespace Proc { //* Get cpu usage, cpu cumulative and threads from /proc/[pid]/stat if (fs::exists((string)d.path() + "/stat")) { - pread.clear(); pstat.clear(); + pread.clear(); instr.clear(); pstat.clear(); pstat.reserve(40); pstat.resize(3, ""); ifstream pread((string)d.path() + "/stat"); - if (pread.good()) while (getline(pread, instr, ' ')) pstat.push_back(instr); + if (pread.good()) { + parenthesis = 1; + pread.ignore(numeric_limits::max(), '('); + while (parenthesis > 0 && !pread.eof()) { + instr = pread.get(); + if (instr == "(") ++parenthesis; + else if (instr == ")") --parenthesis; + } + pread.ignore(1); + while (getline(pread, instr, ' ') && pstat.size() < 40) pstat.push_back(instr); + } pread.close(); - if (pstat.size() < 37) continue; + if (pstat.size() < 22) continue; + + //? Process state + state = pstat[3][0]; + + //? Process parent pid + ppid = stoul(pstat[4]); + + //? Process nice value + p_nice = stoi(pstat[19]); //? Process number of threads - threads = stoul(pstat[19]); + threads = stoul(pstat[20]); //? Process utime + stime - cpu_t = stoull(pstat[13]) + stoull(pstat[14]); + cpu_t = stoull(pstat[14]) + stoull(pstat[15]); //? Cache cpu times and cpu seconds if (new_cache) { cache[pid].cpu_t = cpu_t; - cache[pid].cpu_s = stoull(pstat[21]); + cache[pid].cpu_s = stoull(pstat[22]); } - //? Cache process start time - // if (!cpu_second.contains(pid)) cpu_second[pid] = stoull(pstat[21]); + //? CPU number last executed on + if (pstat.size() > 39) cpu_n = stoi(pstat[39]); + //? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion cpu = static_cast(100000 * (cpu_t - cache[pid].cpu_t) / since_last) / clk_tck; @@ -228,7 +263,7 @@ namespace Proc { //* Get RSS memory in bytes from /proc/[pid]/statm if (fs::exists((string)d.path() + "/statm")) { - pread.clear(); tmpstr.clear(); + pread.clear(); ifstream pread((string)d.path() + "/statm"); if (pread.good()) { pread.ignore(numeric_limits::max(), ' '); @@ -238,22 +273,19 @@ namespace Proc { pread.close(); } - - - // //* Match filter if applicable + //* Match filter if defined if (!filter.empty() && - pid_str.find(filter) == string::npos && //? Pid - cache[pid].name.find(filter) == string::npos && //? Program - cache[pid].cmd.find(filter) == string::npos && //? Command - cache[pid].user.find(filter) == string::npos //? User + pid_str.find(filter) == string::npos && + cache[pid].name.find(filter) == string::npos && + cache[pid].cmd.find(filter) == string::npos && + cache[pid].user.find(filter) == string::npos ) continue; //* 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, state, cpu_n, p_nice, ppid)); } } - // auto st = time_ms(); //* Sort processes vector std::ranges::sort(procs, [&sortint, &reverse](proc_info& a, proc_info& b)