mirror of
https://github.com/aristocratos/btop.git
synced 2024-09-28 06:11:28 +02:00
Proc::collect() optimization and started on Proc::_collect_details()
This commit is contained in:
parent
331665dc55
commit
3634633e21
31
Makefile
31
Makefile
@ -2,7 +2,21 @@ PREFIX ?= /usr/local
|
||||
DOCDIR ?= $(PREFIX)/share/btop/doc
|
||||
|
||||
#Compiler and Linker
|
||||
CXX := g++
|
||||
CXX := g++
|
||||
|
||||
#If using g++ try to make sure we are using version 11 or 10
|
||||
ifeq ($(CXX),g++)
|
||||
CXX_VERSION = $(shell $(CXX) -dumpversion)
|
||||
ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
|
||||
ifneq ($(shell command -v g++-11),)
|
||||
CXX := g++-11
|
||||
else ifneq ($(shell test $(CXX_VERSION) -eq 10; echo $$?),0)
|
||||
ifneq ($(shell command -v g++-10),)
|
||||
CXX := g++-10
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
#The Target Binary Program
|
||||
TARGET := btop
|
||||
@ -20,9 +34,6 @@ OBJEXT := o
|
||||
CXXFLAGS := -std=c++20 -pthread -O3 -Wall -Wextra -Wno-stringop-overread -pedantic
|
||||
INC := -I$(INCDIR) -I$(SRCDIR)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
#DO NOT EDIT BELOW THIS LINE
|
||||
#---------------------------------------------------------------------------------
|
||||
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
|
||||
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
||||
|
||||
@ -34,12 +45,12 @@ directories:
|
||||
@mkdir -p $(TARGETDIR)
|
||||
@mkdir -p $(BUILDDIR)
|
||||
|
||||
#Clean only Objecst
|
||||
#Clean only Objects
|
||||
clean:
|
||||
@rm -rf $(BUILDDIR)
|
||||
|
||||
#Full Clean, Objects and Binaries
|
||||
dist-clean: clean
|
||||
distclean: clean
|
||||
@rm -rf $(TARGETDIR)
|
||||
|
||||
install:
|
||||
@ -50,6 +61,14 @@ install:
|
||||
@cp -pr themes $(DESTDIR)$(PREFIX)/share/btop
|
||||
@chmod 755 $(DESTDIR)$(PREFIX)/bin/btop
|
||||
|
||||
#Set suid bit for btop to root, will make btop run as root regardless of actual user
|
||||
su-setuid:
|
||||
@su --session-command "make as-root-setuid" root
|
||||
|
||||
as-root-setuid:
|
||||
@chown root:root $(DESTDIR)$(PREFIX)/bin/btop
|
||||
@chmod 4755 $(DESTDIR)$(PREFIX)/bin/btop
|
||||
|
||||
uninstall:
|
||||
@rm -rf $(DESTDIR)$(PREFIX)/bin/btop
|
||||
@rm -rf $(DESTDIR)$(DOCDIR)
|
||||
|
43
README.md
43
README.md
@ -17,7 +17,6 @@
|
||||
* [Themes](#themes)
|
||||
* [Support and funding](#support-and-funding)
|
||||
* [Prerequisites](#prerequisites) (Read this if you are having issues!)
|
||||
* [Dependencies](#dependencies)
|
||||
* [Screenshots](#screenshots)
|
||||
* [Installation](#installation)
|
||||
* [Configurability](#configurability)
|
||||
@ -67,7 +66,7 @@ Btop++ uses the same theme files as bpytop and bashtop (some color values missin
|
||||
|
||||
See [themes](https://github.com/aristocratos/btop/tree/master/themes) folder for available themes.
|
||||
|
||||
The `make install` command places the default themes in `[/usr/local]/share/btop/themes`.
|
||||
The `make install` command places the default themes in `[$PREFIX or /usr/local]/share/btop/themes`.
|
||||
User created themes should be placed in `$XDG_CONFIG_HOME/btop/themes` or `$HOME/.config/btop/themes`.
|
||||
|
||||
Let me know if you want to contribute with new themes.
|
||||
@ -82,15 +81,16 @@ Any support is greatly appreciated!
|
||||
|
||||
## Prerequisites
|
||||
|
||||
For correct display, a terminal with support for:
|
||||
For best experience, a terminal with support for:
|
||||
|
||||
* 24-bit truecolor ([See list of terminals with truecolor support](https://gist.github.com/XVilka/8346728))
|
||||
* 256-color terminals are supported through 24-bit to 256-color conversion when setting "truecolor" to False in the options or with "-lc/--low-color" argument.
|
||||
(16 color TTY mode now available as well.)
|
||||
* Wide characters (Are sometimes problematic in web-based terminals)
|
||||
|
||||
Also needs a UTF8 locale and a font that covers:
|
||||
|
||||
* Unicode Block “Braille Patterns” U+2800 - U+28FF
|
||||
* Unicode Block “Braille Patterns” U+2800 - U+28FF (Not needed in TTY mode or with graphs set to type: block or tty.)
|
||||
* Unicode Block “Geometric Shapes” U+25A0 - U+25FF
|
||||
* Unicode Block "Box Drawing" and "Block Elements" U+2500 - U+259F
|
||||
|
||||
@ -108,14 +108,6 @@ If text are misaligned and you are using Konsole or Yakuake, turning off "Bi-Dir
|
||||
Characters clipping in to each other or text/border misalignments is not bugs caused by bpytop, but most likely a fontconfig or terminal problem where the braille characters making up the graphs aren't rendered correctly.
|
||||
Look to the creators of the terminal emulator you use to fix these issues if the previous mentioned fixes don't work for you.
|
||||
|
||||
## Dependencies
|
||||
|
||||
None
|
||||
|
||||
### Compiling from source
|
||||
|
||||
Needs at least G++ 10, preferably 11 (or higher) to make use of some C++20 functionality.
|
||||
|
||||
## Screenshots
|
||||
|
||||
Main UI showing details for a selected process.
|
||||
@ -134,7 +126,14 @@ Options menu.
|
||||
|
||||
#### Manual compilation and installation
|
||||
|
||||
Requires GCC/G++ 10 or higher!
|
||||
Needs at least GCC/G++ 10, preferably 11 (or higher) to make use of some C++20 functionality.
|
||||
|
||||
>Install dependencies (Ubuntu 21.04 Hirsute)
|
||||
|
||||
``` bash
|
||||
sudo apt install git build-essential gcc-11 g++-11
|
||||
# On earlier Ubuntu versions that don't have gcc-11 and g++-11 replace with gcc-10 and g++-10
|
||||
```
|
||||
|
||||
>Clone and compile
|
||||
|
||||
@ -142,26 +141,42 @@ Requires GCC/G++ 10 or higher!
|
||||
git clone https://github.com/aristocratos/btop.git
|
||||
cd btop
|
||||
make
|
||||
# use "make -jX" where X is your number of cores for multithreaded compilation
|
||||
```
|
||||
|
||||
>to install
|
||||
|
||||
``` bash
|
||||
sudo make install
|
||||
# use "make install PREFIX=/target/dir" to set target, default: /usr/local
|
||||
# only use "sudo" when installing to a NON user owned directory
|
||||
```
|
||||
|
||||
>to make btop always run as root (to enable signal sending to any process and for /proc read permissions on some systems)
|
||||
|
||||
``` bash
|
||||
make su-setuid
|
||||
```
|
||||
|
||||
|
||||
>to uninstall
|
||||
|
||||
``` bash
|
||||
sudo make uninstall
|
||||
```
|
||||
|
||||
>to remove any compiled files
|
||||
>to remove any object files
|
||||
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
|
||||
>to remove all object files, binaries and created directories
|
||||
|
||||
```bash
|
||||
make distclean
|
||||
```
|
||||
|
||||
## Configurability
|
||||
|
||||
All options changeable from within UI.
|
||||
|
@ -599,7 +599,7 @@ int main(int argc, char **argv){
|
||||
size_t lc;
|
||||
string ostring;
|
||||
uint64_t tsl, timestamp2, rcount = 0;
|
||||
list<uint64_t> avgtimes = {0};
|
||||
list<uint64_t> avgtimes;
|
||||
size_t timer = 2000;
|
||||
bool filtering = false;
|
||||
vector<string> greyscale;
|
||||
@ -657,8 +657,8 @@ int main(int argc, char **argv){
|
||||
|
||||
|
||||
|
||||
if (rcount > 0) avgtimes.push_front(timestamp);
|
||||
if (avgtimes.size() > 100) avgtimes.pop_back();
|
||||
avgtimes.push_front(timestamp);
|
||||
if (avgtimes.size() > 30) avgtimes.pop_back();
|
||||
cout << pbox << ostring << Fx::reset << "\n" << endl;
|
||||
cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 5) << " μs. Average: " <<
|
||||
rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() <<
|
||||
|
@ -211,12 +211,14 @@ namespace Config {
|
||||
{"tty_mode", false},
|
||||
{"force_tty", false},
|
||||
{"lowcolor", false},
|
||||
{"show_detailed", false},
|
||||
};
|
||||
unordered_flat_map<string, bool> boolsTmp;
|
||||
|
||||
unordered_flat_map<string, int> ints = {
|
||||
{"update_ms", 2000},
|
||||
{"proc_update_mult", 2},
|
||||
{"detailed_pid", 0},
|
||||
};
|
||||
unordered_flat_map<string, int> intsTmp;
|
||||
|
||||
@ -273,6 +275,7 @@ namespace Config {
|
||||
}
|
||||
|
||||
void unlock(){
|
||||
if (not locked) return;
|
||||
atomic_wait_set(writelock);
|
||||
|
||||
for (auto& item : stringsTmp){
|
||||
|
@ -106,25 +106,25 @@ namespace Draw {
|
||||
|
||||
out = Fx::reset + lcolor;
|
||||
|
||||
//* Draw horizontal lines
|
||||
//? Draw horizontal lines
|
||||
for (size_t hpos : {c.y, c.y + c.height - 1}){
|
||||
out += Mv::to(hpos, c.x) + Symbols::h_line * (c.width - 1);
|
||||
}
|
||||
|
||||
//* Draw vertical lines and fill if enabled
|
||||
//? Draw vertical lines and fill if enabled
|
||||
for (size_t hpos : iota(c.y + 1, c.y + c.height - 1)){
|
||||
out += Mv::to(hpos, c.x) + Symbols::v_line +
|
||||
((c.fill) ? string(c.width - 2, ' ') : Mv::r(c.width - 2)) +
|
||||
Symbols::v_line;
|
||||
}
|
||||
|
||||
//* Draw corners
|
||||
//? Draw corners
|
||||
out += Mv::to(c.y, c.x) + Symbols::left_up +
|
||||
Mv::to(c.y, c.x + c.width - 1) + Symbols::right_up +
|
||||
Mv::to(c.y + c.height - 1, c.x) + Symbols::left_down +
|
||||
Mv::to(c.y + c.height - 1, c.x + c.width - 1) + Symbols::right_down;
|
||||
|
||||
//* Draw titles if defined
|
||||
//? Draw titles if defined
|
||||
if (not c.title.empty()){
|
||||
out += Mv::to(c.y, c.x + 2) + Symbols::title_left + Fx::b + numbering + Theme::c("title") + c.title +
|
||||
Fx::ub + lcolor + Symbols::title_right;
|
||||
@ -199,7 +199,7 @@ namespace Draw {
|
||||
if (no_zero and horizon == height - 1 and i != -1 and result[ai] == 0) result[ai] = 1;
|
||||
}
|
||||
}
|
||||
//? Generate braille symbol from 5x5 2D vector
|
||||
//? Generate graph symbol from 5x5 2D vector
|
||||
graphs[current][horizon] += (height == 1 and result[0] + result[1] == 0) ? Mv::r(1) : graph_symbol[(result[0] * 5 + result[1])];
|
||||
}
|
||||
if (mult and i > data_offset) last = data_value;
|
||||
|
@ -95,6 +95,7 @@ namespace Proc {
|
||||
namespace {
|
||||
struct p_cache {
|
||||
string name, cmd, user;
|
||||
size_t name_offset;
|
||||
uint64_t cpu_t = 0, cpu_s = 0;
|
||||
string prefix = "";
|
||||
size_t depth = 0;
|
||||
@ -120,8 +121,10 @@ namespace Proc {
|
||||
"cpu lazy",
|
||||
};
|
||||
|
||||
detail_container detailed;
|
||||
|
||||
//* Generate process tree list
|
||||
void _tree_gen(const proc_info& cur_proc, const vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth, const bool collapsed, const string& prefix, const string& filter, bool found){
|
||||
void _tree_gen(const proc_info& cur_proc, const vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false){
|
||||
auto cur_pos = out_procs.size();
|
||||
bool filtering = false;
|
||||
|
||||
@ -150,14 +153,46 @@ namespace Proc {
|
||||
out_procs.back().threads += p.threads;
|
||||
}
|
||||
else children++;
|
||||
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cache.at(cur_proc.pid).collapsed), prefix, filter, found);
|
||||
_tree_gen(p, in_procs, out_procs, cur_depth + 1, (collapsed ? true : cache.at(cur_proc.pid).collapsed), filter, found);
|
||||
}
|
||||
if (collapsed or filtering) return;
|
||||
|
||||
if (out_procs.size() > cur_pos + 1 and not out_procs.back().prefix.ends_with("] "))
|
||||
out_procs.back().prefix.replace(out_procs.back().prefix.size() - 8, 8, " └─ ");
|
||||
|
||||
out_procs.at(cur_pos).prefix = " │ "s * cur_depth + (children > 0 ? (cache.at(cur_proc.pid).collapsed ? "[+] " : "[-] ") : prefix);
|
||||
out_procs.at(cur_pos).prefix = " │ "s * cur_depth + (children > 0 ? (cache.at(cur_proc.pid).collapsed ? "[+] " : "[-] ") : " ├─ ");
|
||||
}
|
||||
|
||||
//* Get datiled info for selected process
|
||||
void _collect_details(proc_info p){
|
||||
fs::path pid_path = Shared::proc_path / std::to_string(p.pid);
|
||||
|
||||
detailed.entry = p;
|
||||
ifstream d_read;
|
||||
|
||||
//* Get RSS mem from smaps
|
||||
if (fs::exists(pid_path / "smaps")) {
|
||||
pid_path /= "smaps";
|
||||
d_read.open(pid_path);
|
||||
if (d_read.good()) {
|
||||
uint64_t rss = 0;
|
||||
string val;
|
||||
try {
|
||||
while (not d_read.eof()) {
|
||||
d_read.ignore(SSmax, 'R');
|
||||
if (d_read.peek() == 's') {
|
||||
d_read.ignore(SSmax, ':');
|
||||
getline(d_read, val, 'k');
|
||||
rss += stoull(val) << 10;
|
||||
}
|
||||
}
|
||||
detailed.entry.mem = rss;
|
||||
}
|
||||
catch (std::invalid_argument const&) {}
|
||||
catch (std::out_of_range const&) {}
|
||||
}
|
||||
d_read.close();
|
||||
}
|
||||
}
|
||||
|
||||
vector<proc_info> current_procs;
|
||||
@ -166,10 +201,12 @@ namespace Proc {
|
||||
vector<proc_info>& collect(){
|
||||
atomic_wait_set(collecting);
|
||||
auto& sorting = Config::getS("proc_sorting");
|
||||
auto& reverse = Config::getB("proc_reversed");
|
||||
auto reverse = Config::getB("proc_reversed");
|
||||
auto& filter = Config::getS("proc_filter");
|
||||
auto& per_core = Config::getB("proc_per_core");
|
||||
auto& tree = Config::getB("proc_tree");
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
size_t detailed_pid = Config::getI("detailed_pid");
|
||||
ifstream pread;
|
||||
string long_string;
|
||||
string short_str;
|
||||
@ -178,6 +215,7 @@ namespace Proc {
|
||||
procs.reserve((numpids + 10));
|
||||
int npids = 0;
|
||||
int cmult = (per_core) ? Global::coreCount : 1;
|
||||
bool got_detailed = false;
|
||||
|
||||
//* 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) != Shared::passwd_time) {
|
||||
@ -218,154 +256,160 @@ namespace Proc {
|
||||
|
||||
bool new_cache = false;
|
||||
string pid_str = d.path().filename();
|
||||
if (d.is_directory() and isdigit(pid_str[0])) {
|
||||
npids++;
|
||||
proc_info new_proc (stoul(pid_str));
|
||||
if (not isdigit(pid_str[0])) continue;
|
||||
|
||||
//* Cache program name, command and username
|
||||
if (not cache.contains(new_proc.pid)) {
|
||||
string name, cmd, user;
|
||||
new_cache = true;
|
||||
pread.open(d.path() / "comm");
|
||||
if (pread.good()) {
|
||||
getline(pread, name);
|
||||
pread.close();
|
||||
}
|
||||
else continue;
|
||||
npids++;
|
||||
proc_info new_proc (stoul(pid_str));
|
||||
|
||||
pread.open(d.path() / "cmdline");
|
||||
if (pread.good()) {
|
||||
string tmpstr;
|
||||
while(getline(pread, tmpstr, '\0')) cmd += tmpstr + ' ';
|
||||
pread.close();
|
||||
if (not cmd.empty()) cmd.pop_back();
|
||||
}
|
||||
else continue;
|
||||
//* Cache program name, command and username
|
||||
if (not cache.contains(new_proc.pid)) {
|
||||
string name, cmd, user;
|
||||
new_cache = true;
|
||||
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() / "status");
|
||||
if (pread.good()) {
|
||||
string uid;
|
||||
while (not pread.eof()){
|
||||
string line;
|
||||
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;
|
||||
|
||||
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');
|
||||
}
|
||||
else continue;
|
||||
cache[new_proc.pid] = {name, cmd, user};
|
||||
}
|
||||
pread.close();
|
||||
user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid;
|
||||
|
||||
//* Match filter if defined
|
||||
if (not tree and not filter.empty()
|
||||
and pid_str.find(filter) == string::npos
|
||||
and cache[new_proc.pid].name.find(filter) == string::npos
|
||||
and cache[new_proc.pid].cmd.find(filter) == string::npos
|
||||
and cache[new_proc.pid].user.find(filter) == string::npos) {
|
||||
if (new_cache) cache.erase(new_proc.pid);
|
||||
continue;
|
||||
}
|
||||
new_proc.name = cache[new_proc.pid].name;
|
||||
new_proc.cmd = cache[new_proc.pid].cmd;
|
||||
new_proc.user = cache[new_proc.pid].user;
|
||||
|
||||
//* Parse /proc/[pid]/stat
|
||||
pread.open(d.path() / "stat");
|
||||
if (pread.good()) {
|
||||
|
||||
//? Check cached name for whitespace characters and set offset to get correct fields from stat file
|
||||
size_t offset = rng::count(cache.at(new_proc.pid).name, ' ');
|
||||
size_t x = 0, next_x = 3;
|
||||
uint64_t cpu_t = 0;
|
||||
try {
|
||||
while (not pread.eof()) {
|
||||
if (++x > 40 + offset) break;
|
||||
|
||||
if (x-offset < next_x) {
|
||||
pread.ignore(SSmax, ' ');
|
||||
continue;
|
||||
}
|
||||
else
|
||||
getline(pread, short_str, ' ');
|
||||
|
||||
switch (x-offset) {
|
||||
case 3: { //? Process state
|
||||
new_proc.state = short_str[0];
|
||||
break;
|
||||
}
|
||||
case 4: { //? Process parent pid
|
||||
new_proc.ppid = stoull(short_str);
|
||||
next_x = 14;
|
||||
break;
|
||||
}
|
||||
case 14: { //? Process utime
|
||||
cpu_t = stoull(short_str);
|
||||
break;
|
||||
}
|
||||
case 15: { //? Process stime
|
||||
cpu_t += stoull(short_str);
|
||||
next_x = 19;
|
||||
break;
|
||||
}
|
||||
case 19: { //? Process nice value
|
||||
new_proc.p_nice = stoull(short_str);
|
||||
break;
|
||||
}
|
||||
case 20: { //? Process number of threads
|
||||
new_proc.threads = stoull(short_str);
|
||||
next_x = (new_cache) ? 22 : 40;
|
||||
break;
|
||||
}
|
||||
case 22: { //? Cache cpu seconds
|
||||
cache[new_proc.pid].cpu_s = stoull(short_str);
|
||||
next_x = 40;
|
||||
break;
|
||||
}
|
||||
case 40: { //? CPU number last executed on
|
||||
new_proc.cpu_n = stoull(short_str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pread.close();
|
||||
|
||||
}
|
||||
catch (...) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x-offset < 22) continue;
|
||||
|
||||
//? Process cpu usage since last update
|
||||
new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache[new_proc.pid].cpu_t) / (cputimes - old_cputimes)) / 10.0;
|
||||
|
||||
//? Process cumulative cpu usage since process start
|
||||
new_proc.cpu_c = ((double)cpu_t / Shared::clk_tck) / (uptime - (cache[new_proc.pid].cpu_s / Shared::clk_tck));
|
||||
|
||||
//? Update cache with latest cpu times
|
||||
cache[new_proc.pid].cpu_t = cpu_t;
|
||||
}
|
||||
else continue;
|
||||
|
||||
//* Get RSS memory in bytes from /proc/[pid]/statm
|
||||
pread.open(d.path() / "statm");
|
||||
if (pread.good()) {
|
||||
pread.ignore(SSmax, ' ');
|
||||
getline(pread, short_str, ' ');
|
||||
pread.close();
|
||||
new_proc.mem = stoull(short_str) * Shared::page_size;
|
||||
}
|
||||
|
||||
//? Push process to vector
|
||||
procs.push_back(new_proc);
|
||||
cache[new_proc.pid] = {name, cmd, user, name_offset};
|
||||
}
|
||||
|
||||
//* Match filter if defined
|
||||
if (not tree and not filter.empty()
|
||||
and not (show_detailed and new_proc.pid == detailed_pid)
|
||||
and pid_str.find(filter) == string::npos
|
||||
and cache[new_proc.pid].name.find(filter) == string::npos
|
||||
and cache[new_proc.pid].cmd.find(filter) == string::npos
|
||||
and cache[new_proc.pid].user.find(filter) == string::npos) {
|
||||
if (new_cache) cache.erase(new_proc.pid);
|
||||
continue;
|
||||
}
|
||||
new_proc.name = cache[new_proc.pid].name;
|
||||
new_proc.cmd = cache[new_proc.pid].cmd;
|
||||
new_proc.user = cache[new_proc.pid].user;
|
||||
|
||||
//* Parse /proc/[pid]/stat
|
||||
pread.open(d.path() / "stat");
|
||||
if (not pread.good()) 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 (++x - offset < next_x) {
|
||||
pread.ignore(SSmax, ' ');
|
||||
}
|
||||
|
||||
getline(pread, short_str, ' ');
|
||||
|
||||
switch (x-offset) {
|
||||
case 3: { //? Process state
|
||||
new_proc.state = short_str[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);
|
||||
next_x = (new_cache) ? 22 : 24;
|
||||
continue;
|
||||
}
|
||||
case 22: { //? Save cpu seconds to cache if missing
|
||||
cache[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::page_size;
|
||||
next_x = 40;
|
||||
continue;
|
||||
}
|
||||
case 40: { //? CPU number last executed on
|
||||
new_proc.cpu_n = stoull(short_str);
|
||||
goto stat_loop_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (std::invalid_argument const&) { continue; }
|
||||
catch (std::out_of_range const&) { continue; }
|
||||
catch (std::ios_base::failure const&) {}
|
||||
|
||||
stat_loop_done:
|
||||
pread.close();
|
||||
|
||||
if (x-offset < 24) continue;
|
||||
|
||||
// _parse_smaps(new_proc);
|
||||
|
||||
//? Process cpu usage since last update
|
||||
new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache[new_proc.pid].cpu_t) / (cputimes - old_cputimes)) / 10.0;
|
||||
|
||||
//? Process cumulative cpu usage since process start
|
||||
new_proc.cpu_c = ((double)cpu_t / Shared::clk_tck) / (uptime - (cache[new_proc.pid].cpu_s / Shared::clk_tck));
|
||||
|
||||
//? Update cache with latest cpu times
|
||||
cache[new_proc.pid].cpu_t = cpu_t;
|
||||
|
||||
//? Update the details info box for process if active
|
||||
if (show_detailed and new_proc.pid == detailed_pid) {
|
||||
_collect_details(new_proc);
|
||||
got_detailed = true;
|
||||
}
|
||||
|
||||
//? Push process to vector
|
||||
procs.push_back(new_proc);
|
||||
|
||||
}
|
||||
|
||||
if (show_detailed and not got_detailed) {
|
||||
detailed.entry.state = 'X';
|
||||
}
|
||||
|
||||
//* Sort processes
|
||||
@ -403,10 +447,9 @@ namespace Proc {
|
||||
//? Stable sort to retain selected sorting among processes with the same parent
|
||||
rng::stable_sort(procs, rng::less{}, &proc_info::ppid);
|
||||
|
||||
string prefix = " ├─ ";
|
||||
//? Start recursive iteration over processes with the lowest shared parent pids
|
||||
for (auto& p : rng::equal_range(procs, procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
|
||||
_tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, prefix, filter, false);
|
||||
_tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, filter);
|
||||
}
|
||||
procs.swap(tree_procs);
|
||||
}
|
||||
|
BIN
src/btop_shared
BIN
src/btop_shared
Binary file not shown.
@ -32,7 +32,7 @@ namespace Global {
|
||||
}
|
||||
|
||||
namespace Tools {
|
||||
//* Platform specific function for system_uptime
|
||||
//* Platform specific function for system_uptime (seconds since last restart)
|
||||
double system_uptime();
|
||||
}
|
||||
|
||||
@ -45,6 +45,8 @@ namespace Proc {
|
||||
extern size_t numpids;
|
||||
extern std::atomic<bool> stop;
|
||||
extern std::atomic<bool> collecting;
|
||||
|
||||
//? Contains the valid sorting options for processes
|
||||
extern vector<string> sort_vector;
|
||||
|
||||
//* Container for process information
|
||||
@ -60,6 +62,14 @@ namespace Proc {
|
||||
string prefix = "";
|
||||
};
|
||||
|
||||
//* Container for process info box
|
||||
struct detail_container {
|
||||
proc_info entry;
|
||||
string elapsed, parent, status, io_read, io_write;
|
||||
};
|
||||
|
||||
extern detail_container detailed;
|
||||
|
||||
extern vector<proc_info> current_procs;
|
||||
|
||||
//* Collects and sorts process information from /proc, saves and returns reference to Proc::current_procs;
|
||||
|
@ -362,12 +362,12 @@ namespace Tools {
|
||||
|
||||
#if (__GNUC__ > 10)
|
||||
//* Redirects to atomic wait
|
||||
void atomic_wait(atomic<bool>& atom, bool val){
|
||||
void atomic_wait(const atomic<bool>& atom, bool val){
|
||||
atom.wait(val);
|
||||
}
|
||||
#else
|
||||
//* Crude implementation of atomic wait for GCC 10
|
||||
void atomic_wait(atomic<bool>& atom, bool val){
|
||||
void atomic_wait(const atomic<bool>& atom, bool val){
|
||||
while (atom == val) std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
}
|
||||
#endif
|
||||
|
@ -225,7 +225,7 @@ namespace Tools {
|
||||
string strf_time(string strf);
|
||||
|
||||
//* Waits for <atom> to not be <val>
|
||||
void atomic_wait(std::atomic<bool>& atom, bool val=true);
|
||||
void atomic_wait(const std::atomic<bool>& atom, bool val=true);
|
||||
|
||||
//* Waits for <atom> to not be <val> and then sets it to <val> again
|
||||
void atomic_wait_set(std::atomic<bool>& atom, bool val=true);
|
||||
|
Loading…
Reference in New Issue
Block a user