mirror of https://github.com/aristocratos/btop.git
Proc::collect() optimization and started on Proc::_collect_details()
This commit is contained in:
parent
331665dc55
commit
3634633e21
29
Makefile
29
Makefile
|
@ -4,6 +4,20 @@ DOCDIR ?= $(PREFIX)/share/btop/doc
|
||||||
#Compiler and Linker
|
#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
|
#The Target Binary Program
|
||||||
TARGET := btop
|
TARGET := btop
|
||||||
|
|
||||||
|
@ -20,9 +34,6 @@ OBJEXT := o
|
||||||
CXXFLAGS := -std=c++20 -pthread -O3 -Wall -Wextra -Wno-stringop-overread -pedantic
|
CXXFLAGS := -std=c++20 -pthread -O3 -Wall -Wextra -Wno-stringop-overread -pedantic
|
||||||
INC := -I$(INCDIR) -I$(SRCDIR)
|
INC := -I$(INCDIR) -I$(SRCDIR)
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
|
||||||
#DO NOT EDIT BELOW THIS LINE
|
|
||||||
#---------------------------------------------------------------------------------
|
|
||||||
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
|
SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
|
||||||
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
||||||
|
|
||||||
|
@ -34,12 +45,12 @@ directories:
|
||||||
@mkdir -p $(TARGETDIR)
|
@mkdir -p $(TARGETDIR)
|
||||||
@mkdir -p $(BUILDDIR)
|
@mkdir -p $(BUILDDIR)
|
||||||
|
|
||||||
#Clean only Objecst
|
#Clean only Objects
|
||||||
clean:
|
clean:
|
||||||
@rm -rf $(BUILDDIR)
|
@rm -rf $(BUILDDIR)
|
||||||
|
|
||||||
#Full Clean, Objects and Binaries
|
#Full Clean, Objects and Binaries
|
||||||
dist-clean: clean
|
distclean: clean
|
||||||
@rm -rf $(TARGETDIR)
|
@rm -rf $(TARGETDIR)
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
@ -50,6 +61,14 @@ install:
|
||||||
@cp -pr themes $(DESTDIR)$(PREFIX)/share/btop
|
@cp -pr themes $(DESTDIR)$(PREFIX)/share/btop
|
||||||
@chmod 755 $(DESTDIR)$(PREFIX)/bin/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:
|
uninstall:
|
||||||
@rm -rf $(DESTDIR)$(PREFIX)/bin/btop
|
@rm -rf $(DESTDIR)$(PREFIX)/bin/btop
|
||||||
@rm -rf $(DESTDIR)$(DOCDIR)
|
@rm -rf $(DESTDIR)$(DOCDIR)
|
||||||
|
|
43
README.md
43
README.md
|
@ -17,7 +17,6 @@
|
||||||
* [Themes](#themes)
|
* [Themes](#themes)
|
||||||
* [Support and funding](#support-and-funding)
|
* [Support and funding](#support-and-funding)
|
||||||
* [Prerequisites](#prerequisites) (Read this if you are having issues!)
|
* [Prerequisites](#prerequisites) (Read this if you are having issues!)
|
||||||
* [Dependencies](#dependencies)
|
|
||||||
* [Screenshots](#screenshots)
|
* [Screenshots](#screenshots)
|
||||||
* [Installation](#installation)
|
* [Installation](#installation)
|
||||||
* [Configurability](#configurability)
|
* [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.
|
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`.
|
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.
|
Let me know if you want to contribute with new themes.
|
||||||
|
@ -82,15 +81,16 @@ Any support is greatly appreciated!
|
||||||
|
|
||||||
## Prerequisites
|
## 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))
|
* 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.
|
* 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)
|
* Wide characters (Are sometimes problematic in web-based terminals)
|
||||||
|
|
||||||
Also needs a UTF8 locale and a font that covers:
|
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 “Geometric Shapes” U+25A0 - U+25FF
|
||||||
* Unicode Block "Box Drawing" and "Block Elements" U+2500 - U+259F
|
* 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.
|
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.
|
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
|
## Screenshots
|
||||||
|
|
||||||
Main UI showing details for a selected process.
|
Main UI showing details for a selected process.
|
||||||
|
@ -134,7 +126,14 @@ Options menu.
|
||||||
|
|
||||||
#### Manual compilation and installation
|
#### 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
|
>Clone and compile
|
||||||
|
|
||||||
|
@ -142,26 +141,42 @@ Requires GCC/G++ 10 or higher!
|
||||||
git clone https://github.com/aristocratos/btop.git
|
git clone https://github.com/aristocratos/btop.git
|
||||||
cd btop
|
cd btop
|
||||||
make
|
make
|
||||||
|
# use "make -jX" where X is your number of cores for multithreaded compilation
|
||||||
```
|
```
|
||||||
|
|
||||||
>to install
|
>to install
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
sudo make install
|
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
|
>to uninstall
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
sudo make uninstall
|
sudo make uninstall
|
||||||
```
|
```
|
||||||
|
|
||||||
>to remove any compiled files
|
>to remove any object files
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make clean
|
make clean
|
||||||
```
|
```
|
||||||
|
|
||||||
|
>to remove all object files, binaries and created directories
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make distclean
|
||||||
|
```
|
||||||
|
|
||||||
## Configurability
|
## Configurability
|
||||||
|
|
||||||
All options changeable from within UI.
|
All options changeable from within UI.
|
||||||
|
|
|
@ -599,7 +599,7 @@ int main(int argc, char **argv){
|
||||||
size_t lc;
|
size_t lc;
|
||||||
string ostring;
|
string ostring;
|
||||||
uint64_t tsl, timestamp2, rcount = 0;
|
uint64_t tsl, timestamp2, rcount = 0;
|
||||||
list<uint64_t> avgtimes = {0};
|
list<uint64_t> avgtimes;
|
||||||
size_t timer = 2000;
|
size_t timer = 2000;
|
||||||
bool filtering = false;
|
bool filtering = false;
|
||||||
vector<string> greyscale;
|
vector<string> greyscale;
|
||||||
|
@ -657,8 +657,8 @@ int main(int argc, char **argv){
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (rcount > 0) avgtimes.push_front(timestamp);
|
avgtimes.push_front(timestamp);
|
||||||
if (avgtimes.size() > 100) avgtimes.pop_back();
|
if (avgtimes.size() > 30) avgtimes.pop_back();
|
||||||
cout << pbox << ostring << Fx::reset << "\n" << endl;
|
cout << pbox << ostring << Fx::reset << "\n" << endl;
|
||||||
cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 5) << " μs. Average: " <<
|
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() <<
|
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},
|
{"tty_mode", false},
|
||||||
{"force_tty", false},
|
{"force_tty", false},
|
||||||
{"lowcolor", false},
|
{"lowcolor", false},
|
||||||
|
{"show_detailed", false},
|
||||||
};
|
};
|
||||||
unordered_flat_map<string, bool> boolsTmp;
|
unordered_flat_map<string, bool> boolsTmp;
|
||||||
|
|
||||||
unordered_flat_map<string, int> ints = {
|
unordered_flat_map<string, int> ints = {
|
||||||
{"update_ms", 2000},
|
{"update_ms", 2000},
|
||||||
{"proc_update_mult", 2},
|
{"proc_update_mult", 2},
|
||||||
|
{"detailed_pid", 0},
|
||||||
};
|
};
|
||||||
unordered_flat_map<string, int> intsTmp;
|
unordered_flat_map<string, int> intsTmp;
|
||||||
|
|
||||||
|
@ -273,6 +275,7 @@ namespace Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
void unlock(){
|
void unlock(){
|
||||||
|
if (not locked) return;
|
||||||
atomic_wait_set(writelock);
|
atomic_wait_set(writelock);
|
||||||
|
|
||||||
for (auto& item : stringsTmp){
|
for (auto& item : stringsTmp){
|
||||||
|
|
|
@ -106,25 +106,25 @@ namespace Draw {
|
||||||
|
|
||||||
out = Fx::reset + lcolor;
|
out = Fx::reset + lcolor;
|
||||||
|
|
||||||
//* Draw horizontal lines
|
//? Draw horizontal lines
|
||||||
for (size_t hpos : {c.y, c.y + c.height - 1}){
|
for (size_t hpos : {c.y, c.y + c.height - 1}){
|
||||||
out += Mv::to(hpos, c.x) + Symbols::h_line * (c.width - 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)){
|
for (size_t hpos : iota(c.y + 1, c.y + c.height - 1)){
|
||||||
out += Mv::to(hpos, c.x) + Symbols::v_line +
|
out += Mv::to(hpos, c.x) + Symbols::v_line +
|
||||||
((c.fill) ? string(c.width - 2, ' ') : Mv::r(c.width - 2)) +
|
((c.fill) ? string(c.width - 2, ' ') : Mv::r(c.width - 2)) +
|
||||||
Symbols::v_line;
|
Symbols::v_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Draw corners
|
//? Draw corners
|
||||||
out += Mv::to(c.y, c.x) + Symbols::left_up +
|
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.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) + Symbols::left_down +
|
||||||
Mv::to(c.y + c.height - 1, c.x + c.width - 1) + Symbols::right_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()){
|
if (not c.title.empty()){
|
||||||
out += Mv::to(c.y, c.x + 2) + Symbols::title_left + Fx::b + numbering + Theme::c("title") + c.title +
|
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;
|
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;
|
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])];
|
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;
|
if (mult and i > data_offset) last = data_value;
|
||||||
|
|
|
@ -95,6 +95,7 @@ namespace Proc {
|
||||||
namespace {
|
namespace {
|
||||||
struct p_cache {
|
struct p_cache {
|
||||||
string name, cmd, user;
|
string name, cmd, user;
|
||||||
|
size_t name_offset;
|
||||||
uint64_t cpu_t = 0, cpu_s = 0;
|
uint64_t cpu_t = 0, cpu_s = 0;
|
||||||
string prefix = "";
|
string prefix = "";
|
||||||
size_t depth = 0;
|
size_t depth = 0;
|
||||||
|
@ -120,8 +121,10 @@ namespace Proc {
|
||||||
"cpu lazy",
|
"cpu lazy",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
detail_container detailed;
|
||||||
|
|
||||||
//* Generate process tree list
|
//* 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();
|
auto cur_pos = out_procs.size();
|
||||||
bool filtering = false;
|
bool filtering = false;
|
||||||
|
|
||||||
|
@ -150,14 +153,46 @@ namespace Proc {
|
||||||
out_procs.back().threads += p.threads;
|
out_procs.back().threads += p.threads;
|
||||||
}
|
}
|
||||||
else children++;
|
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 (collapsed or filtering) return;
|
||||||
|
|
||||||
if (out_procs.size() > cur_pos + 1 and not out_procs.back().prefix.ends_with("] "))
|
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.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;
|
vector<proc_info> current_procs;
|
||||||
|
@ -166,10 +201,12 @@ namespace Proc {
|
||||||
vector<proc_info>& collect(){
|
vector<proc_info>& collect(){
|
||||||
atomic_wait_set(collecting);
|
atomic_wait_set(collecting);
|
||||||
auto& sorting = Config::getS("proc_sorting");
|
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& filter = Config::getS("proc_filter");
|
||||||
auto& per_core = Config::getB("proc_per_core");
|
auto per_core = Config::getB("proc_per_core");
|
||||||
auto& tree = Config::getB("proc_tree");
|
auto tree = Config::getB("proc_tree");
|
||||||
|
auto show_detailed = Config::getB("show_detailed");
|
||||||
|
size_t detailed_pid = Config::getI("detailed_pid");
|
||||||
ifstream pread;
|
ifstream pread;
|
||||||
string long_string;
|
string long_string;
|
||||||
string short_str;
|
string short_str;
|
||||||
|
@ -178,6 +215,7 @@ namespace Proc {
|
||||||
procs.reserve((numpids + 10));
|
procs.reserve((numpids + 10));
|
||||||
int npids = 0;
|
int npids = 0;
|
||||||
int cmult = (per_core) ? Global::coreCount : 1;
|
int cmult = (per_core) ? Global::coreCount : 1;
|
||||||
|
bool got_detailed = false;
|
||||||
|
|
||||||
//* Update uid_user map if /etc/passwd changed since last run
|
//* 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) {
|
if (not Shared::passwd_path.empty() and fs::last_write_time(Shared::passwd_path) != Shared::passwd_time) {
|
||||||
|
@ -218,7 +256,8 @@ namespace Proc {
|
||||||
|
|
||||||
bool new_cache = false;
|
bool new_cache = false;
|
||||||
string pid_str = d.path().filename();
|
string pid_str = d.path().filename();
|
||||||
if (d.is_directory() and isdigit(pid_str[0])) {
|
if (not isdigit(pid_str[0])) continue;
|
||||||
|
|
||||||
npids++;
|
npids++;
|
||||||
proc_info new_proc (stoul(pid_str));
|
proc_info new_proc (stoul(pid_str));
|
||||||
|
|
||||||
|
@ -227,26 +266,25 @@ namespace Proc {
|
||||||
string name, cmd, user;
|
string name, cmd, user;
|
||||||
new_cache = true;
|
new_cache = true;
|
||||||
pread.open(d.path() / "comm");
|
pread.open(d.path() / "comm");
|
||||||
if (pread.good()) {
|
if (not pread.good()) continue;
|
||||||
getline(pread, name);
|
getline(pread, name);
|
||||||
pread.close();
|
pread.close();
|
||||||
}
|
size_t name_offset = rng::count(name, ' ');
|
||||||
else continue;
|
|
||||||
|
|
||||||
pread.open(d.path() / "cmdline");
|
pread.open(d.path() / "cmdline");
|
||||||
if (pread.good()) {
|
if (not pread.good()) continue;
|
||||||
string tmpstr;
|
long_string.clear();
|
||||||
while(getline(pread, tmpstr, '\0')) cmd += tmpstr + ' ';
|
while(getline(pread, long_string, '\0')) cmd += long_string + ' ';
|
||||||
pread.close();
|
pread.close();
|
||||||
if (not cmd.empty()) cmd.pop_back();
|
if (not cmd.empty()) cmd.pop_back();
|
||||||
}
|
|
||||||
else continue;
|
|
||||||
|
|
||||||
pread.open(d.path() / "status");
|
pread.open(d.path() / "status");
|
||||||
if (pread.good()) {
|
if (not pread.good()) continue;
|
||||||
string uid;
|
string uid;
|
||||||
while (not pread.eof()){
|
|
||||||
string line;
|
string line;
|
||||||
|
while (not pread.eof()){
|
||||||
getline(pread, line, ':');
|
getline(pread, line, ':');
|
||||||
if (line == "Uid") {
|
if (line == "Uid") {
|
||||||
pread.ignore();
|
pread.ignore();
|
||||||
|
@ -258,13 +296,13 @@ namespace Proc {
|
||||||
}
|
}
|
||||||
pread.close();
|
pread.close();
|
||||||
user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid;
|
user = (uid_user.contains(uid)) ? uid_user.at(uid) : uid;
|
||||||
}
|
|
||||||
else continue;
|
cache[new_proc.pid] = {name, cmd, user, name_offset};
|
||||||
cache[new_proc.pid] = {name, cmd, user};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Match filter if defined
|
//* Match filter if defined
|
||||||
if (not tree and not filter.empty()
|
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 pid_str.find(filter) == string::npos
|
||||||
and cache[new_proc.pid].name.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].cmd.find(filter) == string::npos
|
||||||
|
@ -278,70 +316,77 @@ namespace Proc {
|
||||||
|
|
||||||
//* Parse /proc/[pid]/stat
|
//* Parse /proc/[pid]/stat
|
||||||
pread.open(d.path() / "stat");
|
pread.open(d.path() / "stat");
|
||||||
if (pread.good()) {
|
if (not pread.good()) continue;
|
||||||
|
|
||||||
//? Check cached name for whitespace characters and set offset to get correct fields from stat file
|
//? Check cached value for whitespace characters in name and set offset to get correct fields from stat file
|
||||||
size_t offset = rng::count(cache.at(new_proc.pid).name, ' ');
|
size_t& offset = cache.at(new_proc.pid).name_offset;
|
||||||
|
short_str.clear();
|
||||||
size_t x = 0, next_x = 3;
|
size_t x = 0, next_x = 3;
|
||||||
uint64_t cpu_t = 0;
|
uint64_t cpu_t = 0;
|
||||||
try {
|
try {
|
||||||
while (not pread.eof()) {
|
for (;;) {
|
||||||
if (++x > 40 + offset) break;
|
while (++x - offset < next_x) {
|
||||||
|
|
||||||
if (x-offset < next_x) {
|
|
||||||
pread.ignore(SSmax, ' ');
|
pread.ignore(SSmax, ' ');
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
getline(pread, short_str, ' ');
|
getline(pread, short_str, ' ');
|
||||||
|
|
||||||
switch (x-offset) {
|
switch (x-offset) {
|
||||||
case 3: { //? Process state
|
case 3: { //? Process state
|
||||||
new_proc.state = short_str[0];
|
new_proc.state = short_str[0];
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
case 4: { //? Process parent pid
|
case 4: { //? Parent pid
|
||||||
new_proc.ppid = stoull(short_str);
|
new_proc.ppid = stoull(short_str);
|
||||||
next_x = 14;
|
next_x = 14;
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
case 14: { //? Process utime
|
case 14: { //? Process utime
|
||||||
cpu_t = stoull(short_str);
|
cpu_t = stoull(short_str);
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
case 15: { //? Process stime
|
case 15: { //? Process stime
|
||||||
cpu_t += stoull(short_str);
|
cpu_t += stoull(short_str);
|
||||||
next_x = 19;
|
next_x = 19;
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
case 19: { //? Process nice value
|
case 19: { //? Nice value
|
||||||
new_proc.p_nice = stoull(short_str);
|
new_proc.p_nice = stoull(short_str);
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
case 20: { //? Process number of threads
|
case 20: { //? Number of threads
|
||||||
new_proc.threads = stoull(short_str);
|
new_proc.threads = stoull(short_str);
|
||||||
next_x = (new_cache) ? 22 : 40;
|
next_x = (new_cache) ? 22 : 24;
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
case 22: { //? Cache cpu seconds
|
case 22: { //? Save cpu seconds to cache if missing
|
||||||
cache[new_proc.pid].cpu_s = stoull(short_str);
|
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;
|
next_x = 40;
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
case 40: { //? CPU number last executed on
|
case 40: { //? CPU number last executed on
|
||||||
new_proc.cpu_n = stoull(short_str);
|
new_proc.cpu_n = stoull(short_str);
|
||||||
break;
|
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();
|
pread.close();
|
||||||
|
|
||||||
}
|
if (x-offset < 24) continue;
|
||||||
catch (...) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x-offset < 22) continue;
|
// _parse_smaps(new_proc);
|
||||||
|
|
||||||
//? Process cpu usage since last update
|
//? 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;
|
new_proc.cpu_p = round(cmult * 1000 * (cpu_t - cache[new_proc.pid].cpu_t) / (cputimes - old_cputimes)) / 10.0;
|
||||||
|
@ -351,21 +396,20 @@ namespace Proc {
|
||||||
|
|
||||||
//? Update cache with latest cpu times
|
//? Update cache with latest cpu times
|
||||||
cache[new_proc.pid].cpu_t = cpu_t;
|
cache[new_proc.pid].cpu_t = cpu_t;
|
||||||
}
|
|
||||||
else continue;
|
|
||||||
|
|
||||||
//* Get RSS memory in bytes from /proc/[pid]/statm
|
//? Update the details info box for process if active
|
||||||
pread.open(d.path() / "statm");
|
if (show_detailed and new_proc.pid == detailed_pid) {
|
||||||
if (pread.good()) {
|
_collect_details(new_proc);
|
||||||
pread.ignore(SSmax, ' ');
|
got_detailed = true;
|
||||||
getline(pread, short_str, ' ');
|
|
||||||
pread.close();
|
|
||||||
new_proc.mem = stoull(short_str) * Shared::page_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Push process to vector
|
//? Push process to vector
|
||||||
procs.push_back(new_proc);
|
procs.push_back(new_proc);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (show_detailed and not got_detailed) {
|
||||||
|
detailed.entry.state = 'X';
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Sort processes
|
//* Sort processes
|
||||||
|
@ -403,10 +447,9 @@ namespace Proc {
|
||||||
//? Stable sort to retain selected sorting among processes with the same parent
|
//? Stable sort to retain selected sorting among processes with the same parent
|
||||||
rng::stable_sort(procs, rng::less{}, &proc_info::ppid);
|
rng::stable_sort(procs, rng::less{}, &proc_info::ppid);
|
||||||
|
|
||||||
string prefix = " ├─ ";
|
|
||||||
//? Start recursive iteration over processes with the lowest shared parent pids
|
//? 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)) {
|
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);
|
procs.swap(tree_procs);
|
||||||
}
|
}
|
||||||
|
|
BIN
src/btop_shared
BIN
src/btop_shared
Binary file not shown.
|
@ -32,7 +32,7 @@ namespace Global {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Tools {
|
namespace Tools {
|
||||||
//* Platform specific function for system_uptime
|
//* Platform specific function for system_uptime (seconds since last restart)
|
||||||
double system_uptime();
|
double system_uptime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ namespace Proc {
|
||||||
extern size_t numpids;
|
extern size_t numpids;
|
||||||
extern std::atomic<bool> stop;
|
extern std::atomic<bool> stop;
|
||||||
extern std::atomic<bool> collecting;
|
extern std::atomic<bool> collecting;
|
||||||
|
|
||||||
|
//? Contains the valid sorting options for processes
|
||||||
extern vector<string> sort_vector;
|
extern vector<string> sort_vector;
|
||||||
|
|
||||||
//* Container for process information
|
//* Container for process information
|
||||||
|
@ -60,6 +62,14 @@ namespace Proc {
|
||||||
string prefix = "";
|
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;
|
extern vector<proc_info> current_procs;
|
||||||
|
|
||||||
//* Collects and sorts process information from /proc, saves and returns reference to Proc::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)
|
#if (__GNUC__ > 10)
|
||||||
//* Redirects to atomic wait
|
//* Redirects to atomic wait
|
||||||
void atomic_wait(atomic<bool>& atom, bool val){
|
void atomic_wait(const atomic<bool>& atom, bool val){
|
||||||
atom.wait(val);
|
atom.wait(val);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
//* Crude implementation of atomic wait for GCC 10
|
//* 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));
|
while (atom == val) std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -225,7 +225,7 @@ namespace Tools {
|
||||||
string strf_time(string strf);
|
string strf_time(string strf);
|
||||||
|
|
||||||
//* Waits for <atom> to not be <val>
|
//* 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
|
//* 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);
|
void atomic_wait_set(std::atomic<bool>& atom, bool val=true);
|
||||||
|
|
Loading…
Reference in New Issue