Merge branch 'main' into pr/lvxnull/624

This commit is contained in:
aristocratos 2024-01-03 16:49:24 +01:00
commit ea001bb93e
42 changed files with 4469 additions and 3173 deletions

View file

@ -1,4 +1,4 @@
[*.{cpp,h,sh,md,cfg,sample}]
[*.{cpp,h,hpp,sh,md,cfg,sample}]
indent_style = tab
indent_size = 4

View file

@ -29,6 +29,7 @@ Any bug that can be solved by just reading the [prerequisites](https://github.co
**Info (please complete the following information):**
- btop++ version: `btop -v`
- If using snap: `snap info btop`
- Binary: [self compiled or static binary from release]
- (If compiled) Compiler and version:
- Architecture: [x86_64, aarch64, etc.] `uname -m`
@ -40,7 +41,9 @@ Any bug that can be solved by just reading the [prerequisites](https://github.co
**Additional context**
contents of `~/.config/btop/btop.log`
Contents of `~/.config/btop/btop.log`
Note: The snap uses: `~/snap/btop/current/.config/btop`
(try running btop with `--debug` flag if btop.log is empty)

40
.github/workflows/cmake-freebsd.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: FreeBSD CMake
on:
push:
branches: main
tags-ignore: '*.*'
paths:
- '.github/workflows/cmake-freebsd.yml'
- 'CMakeLists.txt'
- 'include/**'
- 'src/*pp'
- 'src/freebsd/*pp'
pull_request:
branches: main
paths:
- '.github/workflows/cmake-freebsd.yml'
- 'CMakeLists.txt'
- 'include/**'
- 'src/*pp'
- 'src/freebsd/*pp'
jobs:
cmake_build_on_freebsd:
runs-on: ubuntu-22.04
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- name: Compile
uses: vmactions/freebsd-vm@v1
with:
release: '14.0'
usesh: true
prepare: pkg install -y cmake ninja
run: |
CXX=clang++ cmake -B build -G Ninja -DBTOP_STATIC=ON
cmake --build build --verbose

40
.github/workflows/cmake-linux.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: Linux CMake
on:
push:
branches: main
tags-ignore: '*.*'
paths:
- '.github/workflows/cmake-linux.yml'
- 'CMakeLists.txt'
- 'include/**'
- 'src/*pp'
- 'src/linux/*pp'
pull_request:
branches: main
paths:
- '.github/workflows/cmake-linux.yml'
- 'CMakeLists.txt'
- 'include/**'
- 'src/*pp'
- 'src/linux/*pp'
jobs:
cmake_build_on_linux:
runs-on: ubuntu-latest
container: alpine:edge
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- name: Install build tools
run: apk add --no-cache --update clang cmake lld ninja
- name: Configure
run: CXX=clang++ LDFLAGS=-fuse-ld=lld cmake -B build -G Ninja -DBTOP_STATIC=ON
- name: Compile
run: cmake --build build --verbose

47
.github/workflows/cmake-macos.yml vendored Normal file
View file

@ -0,0 +1,47 @@
name: macOS CMake
on:
push:
branches: main
tags-ignore: '*.*'
paths:
- '.github/workflows/cmake-macos.yml'
- 'CMakeLists.txt'
- 'include/**'
- 'src/*pp'
- 'src/osx/*pp'
pull_request:
branches: main
paths:
- '.github/workflows/cmake-macos.yml'
- 'CMakeLists.txt'
- 'include/**'
- 'src/*pp'
- 'src/osx/*pp'
jobs:
cmake_build_on_macos:
runs-on: macos-latest
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- name: Install build tools
run: |
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
brew update --quiet
brew install --force --overwrite cmake llvm@17 ninja
- name: Configure
run: |
export LLVM_PREFIX="$(brew --prefix llvm)"
export CXX="$LLVM_PREFIX/bin/clang++"
export CPPFLAGS="-I$LLVM_PREFIX/include"
export LDFLAGS="-L$LLVM_PREFIX/lib -L$LLVM_PREFIX/lib/c++ -Wl,-rpath,$LLVM_PREFIX/lib/c++ -fuse-ld=$LLVM_PREFIX/bin/ld64.lld"
cmake -B build -G Ninja
- name: Compile
run: cmake --build build --verbose

View file

@ -11,6 +11,7 @@ on:
- 'src/**'
- '!src/linux/**'
- '!src/osx/**'
- '!src/openbsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-freebsd.yml'
@ -21,34 +22,41 @@ on:
- 'src/**'
- '!src/linux/**'
- '!src/osx/**'
- '!src/openbsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-freebsd.yml'
jobs:
build-freebsd:
runs-on: macos-12
runs-on: ubuntu-22.04
timeout-minutes: 20
strategy:
matrix:
compiler: ["clang++", "g++"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Compile
uses: vmactions/freebsd-vm@v0
uses: vmactions/freebsd-vm@v1
with:
release: 13.2
release: '14.0'
usesh: true
prepare: |
pkg install -y gmake gcc11 coreutils git
git config --global --add safe.directory /Users/runner/work/btop/btop
pkg install -y gmake gcc coreutils git
git config --global --add safe.directory /home/runner/work/btop/btop
run: |
gmake STATIC=true STRIP=true
CXX=${{ matrix.compiler }} gmake STATIC=true STRIP=true
GIT_HASH=$(git rev-parse --short "$GITHUB_SHA")
mv bin/btop bin/btop-$GIT_HASH
COMPILER=$(echo ${{ matrix.compiler }} | sed 's/clang++/llvm/' | sed 's/g++/gcc/')
mv bin/btop bin/btop-"$COMPILER"-"$GIT_HASH"
ls -alh bin
- uses: actions/upload-artifact@v3
with:
name: btop-x86_64-FreeBSD-13.2
name: btop-x86_64-freebsd-14
path: 'bin/*'
if-no-files-found: error

View file

@ -11,6 +11,7 @@ on:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- '!src/openbsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-linux.yml'
@ -21,6 +22,7 @@ on:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- '!src/openbsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-linux.yml'

View file

@ -11,6 +11,7 @@ on:
- 'src/**'
- '!src/linux/**'
- '!src/freebsd/**'
- '!src/openbsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-macos.yml'
@ -21,6 +22,7 @@ on:
- 'src/**'
- '!src/linux/**'
- '!src/freebsd/**'
- '!src/openbsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-macos.yml'
@ -44,18 +46,18 @@ jobs:
with:
name: btop-x86_64-macos11-BigSur
path: 'bin/*'
build-macos12:
runs-on: macos-12
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
xcode-version: latest-stable
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Compile
run: |
make CXX=g++-12 ARCH=x86_64 STATIC=true STRIP=true

View file

@ -0,0 +1,58 @@
name: Continuous Build OpenBSD
on:
workflow_dispatch:
push:
branches:
- main
tags-ignore:
- '*.*'
paths:
- 'src/**'
- '!src/linux/**'
- '!src/osx/**'
- '!src/freebsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-openbsd.yml'
pull_request:
branches:
- main
paths:
- 'src/**'
- '!src/linux/**'
- '!src/osx/**'
- '!src/freebsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/continuous-build-openbsd.yml'
jobs:
build-openbsd:
runs-on: ubuntu-22.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Compile
uses: vmactions/openbsd-vm@v1
with:
release: '7.4'
usesh: true
prepare: |
pkg_add gmake gcc%11 g++%11 coreutils git
git config --global --add safe.directory /home/runner/work/btop/btop
run: |
gmake CXX=eg++ STATIC=true STRIP=true
GIT_HASH=$(git rev-parse --short "$GITHUB_SHA")
mv bin/btop bin/btop-GCC11-"$GIT_HASH"
ls -alh bin
- uses: actions/upload-artifact@v3
with:
name: btop-x86_64-openbsd-7.4
path: 'bin/*'
if-no-files-found: error

View file

@ -0,0 +1,42 @@
name: 🧪 Test snap can be built on x86_64
on:
workflow_dispatch:
push:
branches: [ main ]
tags-ignore:
- '*.*'
paths:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/test-snap-can-build.yml'
pull_request:
branches: [ main ]
paths:
- 'src/**'
- '!src/osx/**'
- '!src/freebsd/**'
- 'include/**'
- 'Makefile'
- '.github/workflows/test-snap-can-build.yml'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- uses: snapcore/action-build@v1
id: build
- uses: diddlesnaps/snapcraft-review-action@v1
with:
snap: ${{ steps.build.outputs.snap }}
isClassic: 'false'

3
.gitignore vendored
View file

@ -51,6 +51,9 @@ bin
btop
.*/
# Optional libraries
lib/rocm_smi_lib
# Don't ignore .github directory
!.github/

View file

@ -1,3 +1,49 @@
## v1.3.0
* Added Gpu Support | @romner-set | PR #529
* Enable macos clang | @muneebmahmed | PR #666
* Fix Apple Silicon CPUs misprinted | @masiboss | PR #679
* Cmake support for MacOS | @imwints | PR #675
* Elementarish theme: color update according to Elementary palette | @stradicat | PR #660
* Add alternative key codes for Delete, Insert, Home, End | @ivanp7 | PR #659
* Fix scrollbar not clearing sometimes. | @DecklynKern | PR #643
* Add keybind for toggling memory display mode in PROC box | @rahulaggarwal965 | PR #623
* Minor string initialization improvement | @imwints | PR #636
* Made disks statvfs logic asynchronous. | @crestfallnatwork | PR #633
* Fix signal list on non-linux/weird linux platforms | @lvxnull | PR #630
* Add option to accumulate a child's resources in parent in tree-view | @imwints | PR #618
* Add CMake support for Linux | @imwints | PR #589
* Horizon theme | @SidVeld | PR #610
* Fix short conversion of 1000-1023 *iB | @scorpion-26 | #609
* Fix integer overflows in btop_collect.cpp | @dorrellmw | #546
* Support compiling with LLVM | @imwints | #510
* Fix getting zfs pool name with '.' char in freebsd | @jfouquart | #602
* [macos/freebsd] support gcc13 | @joske | #600
* FreeBSD swap info | @rrveex | #560
* Create adwaita.theme | @flipflop133 | #485
+ Various fixes by @imwints, @simplepad, @joske, @gwena, @cpalv, @iambeingtracked, @mattico, @NexAdn
## v1.2.13
* Makefile: VERBOSE=true flag for Makefile to display all compiler commands and fixed so already set CXXFLAGS and LDFLAGS are displayed.

View file

@ -3,7 +3,7 @@
# CMake configuration for btop
#
cmake_minimum_required(VERSION 3.12)
cmake_minimum_required(VERSION 3.24)
# Disable in-source builds since they would override the Makefile
if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
@ -17,7 +17,12 @@ project("btop"
LANGUAGES CXX
)
# Make custom modules available
include(CheckCXXCompilerFlag)
include(CheckIncludeFileCXX)
include(CheckIPOSupported)
include(CMakeDependentOption)
# Make our Find<Package>.cmake files available
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
# When the build type is not set we can't fortify
@ -31,29 +36,19 @@ set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_COLOR_DIAGNOSTICS ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Options
option(BTOP_STATIC "Link btop statically" OFF)
option(BTOP_LTO "Enable LTO" ON)
option(BTOP_USE_MOLD "Use mold to link btop" OFF)
option(BTOP_PEDANTIC "Enable a bunch of additional warnings" OFF)
option(BTOP_WERROR "Compile with warnings as errors" OFF)
option(BTOP_GPU "Enable GPU support" ON)
cmake_dependent_option(BTOP_RSMI_STATIC "Link statically to ROCm SMI" OFF "BTOP_GPU" OFF)
if(BTOP_STATIC)
if(BTOP_STATIC AND NOT APPLE)
# Set this before calling find_package
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
find_package(devstat REQUIRED)
find_package(kvm REQUIRED)
if(BTOP_STATIC)
find_package(elf REQUIRED)
endif()
endif()
include(CheckCXXCompilerFlag)
include(CheckIPOSupported)
add_executable(btop
src/btop.cpp
src/btop_config.cpp
@ -65,76 +60,98 @@ add_executable(btop
src/btop_tools.cpp
)
# NOTE: Checks can be simplified with CMake 3.25
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_sources(btop PRIVATE
src/osx/btop_collect.cpp
src/osx/sensors.cpp
src/osx/smc.cpp
)
if(APPLE)
target_sources(btop PRIVATE src/osx/btop_collect.cpp src/osx/sensors.cpp src/osx/smc.cpp)
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
target_sources(btop PRIVATE src/freebsd/btop_collect.cpp)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
elseif(LINUX)
target_sources(btop PRIVATE src/linux/btop_collect.cpp)
else()
message(FATAL_ERROR "${CMAKE_SYSTEM_NAME} is not supported")
endif()
check_include_file_cxx(ranges CXX_HAVE_RANGES)
if(NOT CXX_HAVE_RANGES)
message(FATAL_ERROR "The compiler doesn't support <ranges>")
endif()
# Check for and enable LTO
check_ipo_supported(RESULT ipo_supported)
if(ipo_supported AND BTOP_LTO)
set_target_properties(btop PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON)
endif()
# TODO: enable more warnings in coordination with upstream
target_compile_options(btop PRIVATE
-Wall -Wextra -Wpedantic
-ftree-vectorize -fstack-clash-protection
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(btop PRIVATE
-Wheader-hygiene -Wgnu -Wthread-safety
)
endif()
target_compile_options(btop PRIVATE -Wall -Wextra -Wpedantic -ftree-vectorize)
if(BTOP_PEDANTIC)
target_compile_options(btop PRIVATE
-Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused
-Woverloaded-virtual -Wconversion -Wsign-conversion -Wdouble-promotion
-Wformat=2 -Wimplicit-fallthrough -Weffc++
-Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual
-Wconversion -Wsign-conversion -Wdouble-promotion -Wformat=2 -Wimplicit-fallthrough -Weffc++
$<$<CXX_COMPILER_ID:Clang>:-Wheader-hygiene -Wgnu -Wthread-safety>
$<$<CXX_COMPILER_ID:GNU>:-Wduplicated-cond -Wduplicated-branches -Wlogical-op>
$<$<CXX_COMPILER_ID:GNU>:-Wnull-dereference -Wuseless-cast>
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(btop PRIVATE
-Wduplicated-cond -Wduplicated-branches -Wlogical-op -Wnull-dereference
-Wuseless-cast
)
endif()
endif()
if(BTOP_WERROR)
target_compile_options(btop PRIVATE -Werror)
endif()
check_cxx_compiler_flag(-fstack-protector CXX_HAS_FSTACK_PROTECTOR)
if(CXX_HAS_FSTACK_PROTECTOR)
if(NOT APPLE)
target_compile_options(btop PRIVATE -fstack-clash-protection)
endif()
check_cxx_compiler_flag(-fstack-protector HAS_FSTACK_PROTECTOR)
if(HAS_FSTACK_PROTECTOR)
target_compile_options(btop PRIVATE -fstack-protector)
endif()
check_cxx_compiler_flag(-fcf-protection CXX_HAS_FCF_PROTECTION)
if(CXX_HAS_FCF_PROTECTION)
check_cxx_compiler_flag(-fcf-protection HAS_FCF_PROTECTION)
if(HAS_FCF_PROTECTION)
target_compile_options(btop PRIVATE -fcf-protection)
endif()
target_compile_definitions(btop PRIVATE
_FILE_OFFSET_BITS=64
_GLIBCXX_ASSERTIONS _LIBCPP_ENABLE_ASSERTIONS=1
$<$<CONFIG:Debug>:_GLIBCXX_ASSERTIONS _LIBCPP_ENABLE_ASSERTIONS=1>
# Only has an effect with optimizations enabled
$<$<NOT:$<CONFIG:Debug>>:_FORTIFY_SOURCE=2>
)
target_include_directories(btop SYSTEM PRIVATE include)
# mold
# Enable pthreads
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(btop Threads::Threads)
# Enable GPU support
if(LINUX AND BTOP_GPU)
target_compile_definitions(btop PRIVATE GPU_SUPPORT)
if(BTOP_RSMI_STATIC)
# ROCm doesn't properly add it's folders to the module path if `CMAKE_MODULE_PATH` is already
# set
# We could also manully append ROCm's path here
set(_CMAKE_MODULE_PATH CMAKE_MODULE_PATH)
unset(CMAKE_MODULE_PATH)
# NOTE: This might be problematic in the future if other sub projects depend on this or if
# btop starts producing libraries
# Build a static ROCm library
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
add_subdirectory(lib/rocm_smi_lib EXCLUDE_FROM_ALL)
add_library(ROCm INTERFACE)
# Export ROCm's properties to a target
target_compile_definitions(ROCm INTERFACE RSMI_STATIC)
target_include_directories(ROCm INTERFACE lib/rocm_smi_lib/include)
target_link_libraries(ROCm INTERFACE rocm_smi64)
set(CMAKE_MODULE_PATH _CMAKE_MODULE_PATH)
target_link_libraries(btop ROCm)
endif()
endif()
if(BTOP_USE_MOLD)
target_link_options(btop PRIVATE -fuse-ld=mold)
endif()
@ -144,18 +161,28 @@ if(BTOP_STATIC)
target_link_options(btop PRIVATE -static LINKER:--fatal-warnings)
endif()
# Add libraries
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(btop PRIVATE Threads::Threads)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_libraries(btop PRIVATE $<LINK_LIBRARY:FRAMEWORK,CoreFoundation)
target_link_libraries(btop PRIVATE $<LINK_LIBRARY:FRAMEWORK,IOKit)
# Other platform depdendent flags
if(APPLE)
target_link_libraries(btop
$<LINK_LIBRARY:FRAMEWORK,CoreFoundation> $<LINK_LIBRARY:FRAMEWORK,IOKit>
)
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
target_link_libraries(btop PRIVATE devstat::devstat kvm::kvm)
# Avoid version mismatch for libstdc++ when a specific version of GCC is installed and not the
# default one since all use the default ones RPATH
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
string(REGEX MATCH "^[0-9]+" GCC_VERSION_MAJOR "${CMAKE_CXX_COMPILER_VERSION}")
set_target_properties(btop PROPERTIES
INSTALL_RPATH "/usr/local/lib/gcc${GCC_VERSION_MAJOR}"
BUILD_WITH_INSTALL_RPATH TRUE
)
endif()
find_package(devstat REQUIRED)
target_link_libraries(btop devstat::devstat)
if(BTOP_STATIC)
target_link_libraries(btop PRIVATE elf::elf)
find_package(elf REQUIRED)
find_package(kvm REQUIRED)
target_link_libraries(btop elf::elf kvm::kvm)
endif()
endif()

136
Makefile
View file

@ -12,10 +12,7 @@ else
endif
ifneq ($(QUIET),true)
override PRE := info info-quiet
override QUIET := false
else
override PRE := info-quiet
endif
OLDCXX := $(CXXFLAGS)
@ -39,6 +36,20 @@ endif
override PLATFORM_LC := $(shell echo $(PLATFORM) | tr '[:upper:]' '[:lower:]')
#? GPU Support
ifeq ($(PLATFORM_LC)$(ARCH),linuxx86_64)
ifneq ($(STATIC),true)
GPU_SUPPORT := true
endif
endif
ifneq ($(GPU_SUPPORT),true)
GPU_SUPPORT := false
endif
ifeq ($(GPU_SUPPORT),true)
override ADDFLAGS += -DGPU_SUPPORT
endif
#? Compiler and Linker
ifeq ($(shell $(CXX) --version | grep clang >/dev/null 2>&1; echo $$?),0)
override CXX_IS_CLANG := true
@ -48,38 +59,23 @@ override CXX_VERSION_MAJOR := $(shell echo $(CXX_VERSION) | cut -d '.' -f 1)
CLANG_WORKS = false
GCC_WORKS = false
MIN_CLANG_VERSION = 16
ifeq ($(DEBUG),true)
override ADDFLAGS += -DBTOP_DEBUG
endif
#? Supported is Clang 16.0.0 and later
ifeq ($(CXX_IS_CLANG),true)
ifneq ($(shell test $(CXX_VERSION_MAJOR) -lt 16; echo $$?),0)
ifeq ($(shell $(CXX) --version | grep Apple >/dev/null 2>&1; echo $$?),0)
MIN_CLANG_VERSION := 15
endif
ifneq ($(shell test $(CXX_VERSION_MAJOR) -lt $(MIN_CLANG_VERSION); echo $$?),0)
CLANG_WORKS := true
endif
endif
ifeq ($(CLANG_WORKS),false)
#? Try to find a newer GCC version
ifeq ($(shell command -v g++-13 >/dev/null; echo $$?),0)
CXX := g++-13
else ifeq ($(shell command -v g++13 >/dev/null; echo $$?),0)
CXX := g++13
else ifeq ($(shell command -v g++-12 >/dev/null; echo $$?),0)
CXX := g++-12
else ifeq ($(shell command -v g++12 >/dev/null; echo $$?),0)
CXX := g++12
else ifeq ($(shell command -v g++-11 >/dev/null; echo $$?),0)
CXX := g++-11
else ifeq ($(shell command -v g++11 >/dev/null; echo $$?),0)
CXX := g++11
else ifeq ($(shell command -v g++ >/dev/null; echo $$?),0)
CXX := g++
else
GCC_NOT_FOUND := true
endif
ifndef GCC_NOT_FOUND
override CXX_VERSION := $(shell $(CXX) -dumpfullversion -dumpversion || echo 0)
override CXX_VERSION_MAJOR := $(shell echo $(CXX_VERSION) | cut -d '.' -f 1)
ifneq ($(shell test $(CXX_VERSION_MAJOR) -lt 10; echo $$?),0)
GCC_WORKS := true
endif
else
ifneq ($(shell test $(CXX_VERSION_MAJOR) -lt 10; echo $$?),0)
GCC_WORKS := true
endif
endif
@ -139,6 +135,12 @@ else ifeq ($(PLATFORM_LC),macos)
THREADS := $(shell sysctl -n hw.ncpu || echo 1)
override ADDFLAGS += -framework IOKit -framework CoreFoundation -Wno-format-truncation
SU_GROUP := wheel
else ifeq ($(PLATFORM_LC),openbsd)
PLATFORM_DIR := openbsd
THREADS := $(shell sysctl -n hw.ncpu || echo 1)
override ADDFLAGS += -lkvm
export MAKE = gmake
SU_GROUP := wheel
else
$(error $(shell printf "\033[1;91mERROR: \033[97mUnsupported platform ($(PLATFORM))\033[0m"))
endif
@ -206,24 +208,38 @@ endif
P := %%
#? Default Make
all: $(PRE) directories btop
ifeq ($(VERBOSE),true)
# Doesn't work with `&>`
override SUPPRESS := > /dev/null 2> /dev/null
else
override SUPPRESS :=
endif
#? Default Make
.ONESHELL:
all: | info rocm_smi info-quiet directories btop
ifneq ($(QUIET),true)
info:
@printf " $(BANNER)\n"
@printf "\033[1;92mPLATFORM \033[1;93m?| \033[0m$(PLATFORM)\n"
@printf "\033[1;96mARCH \033[1;93m?| \033[0m$(ARCH)\n"
@printf "\033[1;93mCXX \033[1;93m?| \033[0m$(CXX) \033[1;93m(\033[97m$(CXX_VERSION)\033[93m)\n"
@printf "\033[1;94mTHREADS \033[1;94m:| \033[0m$(THREADS)\n"
@printf "\033[1;92mREQFLAGS \033[1;91m!| \033[0m$(REQFLAGS)\n"
@printf "\033[1;91mWARNFLAGS \033[1;94m:| \033[0m$(WARNFLAGS)\n"
@printf "\033[1;94mOPTFLAGS \033[1;94m:| \033[0m$(OPTFLAGS)\n"
@printf "\033[1;93mLDCXXFLAGS \033[1;94m:| \033[0m$(LDCXXFLAGS)\n"
@printf "\033[1;95mCXXFLAGS \033[1;92m+| \033[0;37m\$$(\033[92mREQFLAGS\033[37m) \$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m) $(OLDCXX)\n"
@printf "\033[1;95mLDFLAGS \033[1;92m+| \033[0;37m\$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m) $(OLDLD)\n"
@printf "\033[1;92mPLATFORM \033[1;93m?| \033[0m$(PLATFORM)\n"
@printf "\033[1;96mARCH \033[1;93m?| \033[0m$(ARCH)\n"
@printf "\033[1;95mGPU_SUPPORT \033[1;94m:| \033[0m$(GPU_SUPPORT)\n"
@printf "\033[1;93mCXX \033[1;93m?| \033[0m$(CXX) \033[1;93m(\033[97m$(CXX_VERSION)\033[93m)\n"
@printf "\033[1;94mTHREADS \033[1;94m:| \033[0m$(THREADS)\n"
@printf "\033[1;92mREQFLAGS \033[1;91m!| \033[0m$(REQFLAGS)\n"
@printf "\033[1;91mWARNFLAGS \033[1;94m:| \033[0m$(WARNFLAGS)\n"
@printf "\033[1;94mOPTFLAGS \033[1;94m:| \033[0m$(OPTFLAGS)\n"
@printf "\033[1;93mLDCXXFLAGS \033[1;94m:| \033[0m$(LDCXXFLAGS)\n"
@printf "\033[1;95mCXXFLAGS \033[1;92m+| \033[0;37m\$$(\033[92mREQFLAGS\033[37m) \$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m) $(OLDCXX)\n"
@printf "\033[1;95mLDFLAGS \033[1;92m+| \033[0;37m\$$(\033[93mLDCXXFLAGS\033[37m) \$$(\033[94mOPTFLAGS\033[37m) \$$(\033[91mWARNFLAGS\033[37m) $(OLDLD)\n"
else
info:
@true
endif
info-quiet:
@sleep 0.1 2>/dev/null || true
info-quiet: | info rocm_smi
@printf "\n\033[1;92mBuilding btop++ \033[91m(\033[97mv$(BTOP_VERSION)\033[91m) \033[93m$(PLATFORM) \033[96m$(ARCH)\033[0m\n"
help:
@ -250,11 +266,13 @@ directories:
clean:
@printf "\033[1;91mRemoving: \033[1;97mbuilt objects...\033[0m\n"
@rm -rf $(BUILDDIR)
@test -e lib/rocm_smi_lib/build && cmake --build lib/rocm_smi_lib/build --target clean &> /dev/null || true
#? Clean Objects and Binaries
distclean: clean
@printf "\033[1;91mRemoving: \033[1;97mbuilt binaries...\033[0m\n"
@rm -rf $(TARGETDIR)
@test -e lib/rocm_smi_lib/build && rm -rf lib/rocm_smi_lib/build || true
install:
@printf "\033[1;92mInstalling binary to: \033[1;97m$(DESTDIR)$(PREFIX)/bin/btop\n"
@ -300,9 +318,35 @@ uninstall:
#? Pull in dependency info for *existing* .o files
-include $(OBJECTS:.$(OBJEXT)=.$(DEPEXT))
#? Compile rocm_smi
ifeq ($(GPU_SUPPORT)$(RSMI_STATIC),truetrue)
ROCM_DIR ?= lib/rocm_smi_lib
ROCM_BUILD_DIR := $(ROCM_DIR)/build
ifeq ($(DEBUG),true)
BUILD_TYPE := Debug
else
BUILD_TYPE := Release
endif
.ONESHELL:
rocm_smi:
@printf "\n\033[1;92mBuilding ROCm SMI static library\033[37m...\033[0m\n"
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
@$(QUIET) || printf "\033[1;97mRunning CMake...\033[0m\n"
CXX=$(CXX) cmake -S $(ROCM_DIR) -B $(ROCM_BUILD_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DBUILD_SHARED_LIBS=OFF $(SUPPRESS) || { printf "\033[1;91mCMake failed, continuing build without statically linking ROCm SMI\033[37m...\033[0m\n"; exit 0; }
@$(QUIET) || printf "\n\033[1;97mBuilding and linking...\033[0m\n"
@cmake --build $(ROCM_BUILD_DIR) -j -t rocm_smi64 $(SUPPRESS) || { printf "\033[1;91mMake failed, continuing build without statically linking ROCm SMI\033[37m...\033[0m\n"; exit 0; }
@printf "\033[1;92m100$(P)\033[10D\033[5C-> \033[1;37m$(ROCM_BUILD_DIR)/rocm_smi/librocm_smi64.a \033[1;93m(\033[1;97m$$(du -ah $(ROCM_BUILD_DIR)/rocm_smi/librocm_smi64.a | cut -f1)iB\033[1;93m)\033[0m\n"
@printf "\033[1;92mROCm SMI build complete in \033[92m(\033[97m$$($(DATE_CMD) -d @$$(expr $$(date +%s 2>/dev/null || echo "0") - $(TIMESTAMP) 2>/dev/null) -u +%Mm:%Ss 2>/dev/null | sed 's/^00m://' || echo "unknown")\033[92m)\033[0m\n"
@$(eval override LDFLAGS += $(ROCM_BUILD_DIR)/rocm_smi/librocm_smi64.a -DRSMI_STATIC) # TODO: this seems to execute every time, no matter if the compilation failed or succeeded
@$(eval override CXXFLAGS += -DRSMI_STATIC)
else
rocm_smi:
@true
endif
#? Link
.ONESHELL:
btop: $(OBJECTS) | directories
btop: $(OBJECTS) | rocm_smi directories
@sleep 0.2 2>/dev/null || true
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
@$(QUIET) || printf "\n\033[1;92mLinking and optimizing binary\033[37m...\033[0m\n"
@ -313,7 +357,7 @@ btop: $(OBJECTS) | directories
#? Compile
.ONESHELL:
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) | directories
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT) | rocm_smi directories
@sleep 0.3 2>/dev/null || true
@TSTAMP=$$(date +%s 2>/dev/null || echo "0")
@$(QUIET) || printf "\033[1;97mCompiling $<\033[0m\n"

537
README.md
View file

@ -7,6 +7,7 @@
![Linux](https://img.shields.io/badge/-Linux-grey?logo=linux)
![macOS](https://img.shields.io/badge/-OSX-black?logo=apple)
![FreeBSD](https://img.shields.io/badge/-FreeBSD-red?logo=freebsd)
![OpenBSD](https://img.shields.io/badge/-OpenBSD-black?logo=openbsd)
![Usage](https://img.shields.io/badge/Usage-System%20resource%20monitor-yellow)
![c++20](https://img.shields.io/badge/cpp-c%2B%2B20-green)
![latest_release](https://img.shields.io/github/v/tag/aristocratos/btop?label=release)
@ -17,6 +18,7 @@
[![Continuous Build Linux](https://github.com/aristocratos/btop/actions/workflows/continuous-build-linux.yml/badge.svg)](https://github.com/aristocratos/btop/actions/workflows/continuous-build-linux.yml)
[![Continuous Build macOS](https://github.com/aristocratos/btop/actions/workflows/continuous-build-macos.yml/badge.svg)](https://github.com/aristocratos/btop/actions/workflows/continuous-build-macos.yml)
[![Continuous Build FreeBSD](https://github.com/aristocratos/btop/actions/workflows/continuous-build-freebsd.yml/badge.svg)](https://github.com/aristocratos/btop/actions/workflows/continuous-build-freebsd.yml)
[![Continuous Build OpenBSD](https://github.com/aristocratos/btop/actions/workflows/continuous-build-openbsd.yml/badge.svg)](https://github.com/aristocratos/btop/actions/workflows/continuous-build-openbsd.yml)
## Index
@ -33,12 +35,34 @@
* [Compilation Linux](#compilation-linux)
* [Compilation macOS](#compilation-macos-osx)
* [Compilation FreeBSD](#compilation-freebsd)
* [Compilation OpenBSD](#compilation-openbsd)
* [GPU compatibility](#gpu-compatibility)
* [Installing the snap](#installing-the-snap)
* [Configurability](#configurability)
* [License](#license)
## News
##### 25 November 2023
GPU monitoring added for Linux!
Compile from git main to try it out.
Use keys `5`, `6`, `7` and `0` to show/hide the gpu monitoring boxes. `5` = Gpu 1, `6` = Gpu 2, etc.
Gpu stats/graphs can also be displayed in the "Cpu box" (not as verbose), see the cpu options menu for info and configuration.
Note that the binaries provided on the release page (when released) and the continuous builds will not have gpu support enabled.
Because the GPU support relies on loading of dynamic gpu libraries, gpu support will not work when also static linking.
See [Compilation Linux](#compilation-linux) for more info on how to compile with gpu monitoring support.
Many thanks to [@romner-set](https://github.com/romner-set) who wrote the vast majority of the implementation for GPU support.
Big update with version bump to 1.3 coming soon.
##### 28 August 2022
[![btop4win](https://github.com/aristocratos/btop4win/raw/master/Img/logo.png)](https://github.com/aristocratos/btop4win)
@ -305,22 +329,46 @@ Also needs a UTF8 locale and a font that covers:
## Compilation Linux
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
Requires at least GCC 10 or Clang 16.
The makefile also needs GNU coreutils and `sed` (should already be installed on any modern distribution).
<details>
### GPU compatibility
Btop++ supports NVIDIA and AMD GPUs out of the box on Linux x86_64, provided you have the correct drivers and libraries.
Compatibility with Intel GPUs using generic DRM calls is planned, as is compatibility for FreeBSD and macOS.
Gpu support will not work when static linking glibc (or musl, etc.)!
For x86_64 Linux the flag `GPU_SUPPORT` is automatically set to `true`, to manually disable gpu support set the flag to false, like:
`make GPU_SUPPORT=false` (or `cmake -DBTOP_GPU=false` with CMake)
* **NVIDIA**
You must use an official NVIDIA driver, both the closed-source and [open-source](https://github.com/NVIDIA/open-gpu-kernel-modules) ones have been verified to work.
In addition to that you must also have the `nvidia-ml` dynamic library installed, which should be included with the driver package of your distribution.
* **AMD**
AMDGPU data is queried using the [ROCm SMI](https://github.com/RadeonOpenCompute/rocm_smi_lib) library, which may or may not be packaged for your distribution. If your distribution doesn't provide a package, btop++ is statically linked to ROCm SMI with the `RSMI_STATIC=true` make flag.
This flag expects the ROCm SMI source code in `lib/rocm_smi_lib`, and compilation will fail if it's not there. The latest tested version is 5.6.x, which can be obtained with the following command:
```bash
git clone https://github.com/RadeonOpenCompute/rocm_smi_lib.git --depth 1 -b rocm-5.6.x lib/rocm_smi_lib
```
<details>
<summary>
### With Make
</summary>
1. **Install dependencies (example for Ubuntu 21.04 Hirsute)**
Use gcc-10 g++-10 if gcc-11 isn't available
```bash
sudo apt install coreutils sed git build-essential gcc-11 g++-11
```
@ -334,51 +382,51 @@ Also needs a UTF8 locale and a font that covers:
3. **Compile**
Append `VERBOSE=true` to display full compiler/linker commands.
Append `STATIC=true` for static compilation.
Notice! If using LDAP Authentication, usernames will show as UID number for LDAP users if compiling statically with glibc.
Append `QUIET=true` for less verbose output.
Append `STRIP=true` to force stripping of debug symbols (adds `-s` linker flag).
Append `ARCH=<architecture>` to manually set the target architecture.
If omitted the makefile uses the machine triple (output of `-dumpmachine` compiler parameter) to detect the target system.
Use `ADDFLAGS` variable for appending flags to both compiler and linker.
For example: `ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
If `g++` is linked to an older version of gcc on your system specify the correct version by appending `CXX=g++-10` or `CXX=g++-11`.
```bash
make
```
Options for make:
| Flag | Description |
|---------------------------------|-------------------------------------------------------------------------|
| `VERBOSE=true` | To display full compiler/linker commands |
| `STATIC=true` | For static compilation |
| `QUIET=true` | For less verbose output |
| `STRIP=true` | To force stripping of debug symbols (adds `-s` linker flag) |
| `DEBUG=true` | Sets OPTFLAGS to `-O0 -g` and enables more verbose debug logging |
| `ARCH=<architecture>` | To manually set the target architecture |
| `GPU_SUPPORT=<true\|false>` | Enable/disable GPU support (Enabled by default on X86_64 Linux) |
| `RSMI_STATIC=true` | To statically link the ROCm SMI library used for querying AMDGPU |
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
| `CXX=<compiler>` | Manualy set which compiler to use |
Example: `make ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
Notice! If using LDAP Authentication, usernames will show as UID number for LDAP users if compiling statically with glibc.
4. **Install**
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
Notice! Only use "sudo" when installing to a NON user owned directory.
```bash
sudo make install
```
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
Notice! Only use "sudo" when installing to a NON user owned directory.
5. **(Optional) Set suid bit to make btop always run as root (or other user)**
```bash
sudo make setuid
```
No need for `sudo` to enable signal sending to any process and to prevent /proc read permissions problems on some systems.
Run after make install and use same PREFIX if any was used at install.
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `root`
```bash
sudo make setuid
```
* **Uninstall**
```bash
@ -404,13 +452,10 @@ Also needs a UTF8 locale and a font that covers:
```
</details>
<details>
<summary>
### With CMake (Community maintained)
</summary>
1. **Install build dependencies**
@ -449,9 +494,11 @@ Also needs a UTF8 locale and a font that covers:
| `-DBTOP_USE_MOLD=<ON\|OFF>` | Use mold to link btop (OFF by default) |
| `-DBTOP_PEDANTIC=<ON\|OFF>` | Compile with additional warnings (OFF by default) |
| `-DBTOP_WERROR=<ON\|OFF>` | Compile with warnings as errors (OFF by default) |
| `-DBTOP_GPU=<ON\|OFF>` | Enable GPU support (ON by default) |
| `-DBTOP_RSMI_STATIC=<ON\|OFF>` | Build and link the ROCm SMI library statically (OFF by default) |
| `-DCMAKE_INSTALL_PREFIX=<path>` | The installation prefix ('/usr/local' by default) |
To force a compiler, run `CXX=<compiler> cmake -B build -G Ninja`
To force any other compiler, run `CXX=<compiler> cmake -B build -G Ninja`
4. **Install**
@ -478,14 +525,20 @@ Also needs a UTF8 locale and a font that covers:
## Compilation macOS OSX
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
Requires at least GCC 10 or Clang 16.
GCC 12 needed for macOS Ventura. If you get linker errors on Ventura you'll need to upgrade your command line tools (Version 14.0) is bugged.
With GCC, version 12 (or better) is needed for macOS Ventura. If you get linker errors on Ventura you'll need to upgrade your command line tools (Version 14.0) is bugged.
The makefile also needs GNU coreutils and `sed`.
Install and use Homebrew or MacPorts package managers for easy dependency installation
<details>
<summary>
### With Make
</summary>
1. **Install dependencies (example for Homebrew)**
```bash
@ -498,144 +551,49 @@ Also needs a UTF8 locale and a font that covers:
git clone https://github.com/aristocratos/btop.git
cd btop
```
3. **Compile**
Append `VERBOSE=true` to display full compiler/linker commands.
Append `STATIC=true` for static compilation (only libgcc and libstdc++ will be static!).
Append `QUIET=true` for less verbose output.
Append `STRIP=true` to force stripping of debug symbols (adds `-s` linker flag).
Append `ARCH=<architecture>` to manually set the target architecture.
If omitted the makefile uses the machine triple (output of `-dumpmachine` compiler parameter) to detect the target system.
Use `ADDFLAGS` variable for appending flags to both compiler and linker.
For example: `ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
```bash
gmake
```
Options for make:
| Flag | Description |
|---------------------------------|-------------------------------------------------------------------------|
| `VERBOSE=true` | To display full compiler/linker commands |
| `STATIC=true` | For static compilation (only libgcc and libstdc++) |
| `QUIET=true` | For less verbose output |
| `STRIP=true` | To force stripping of debug symbols (adds `-s` linker flag) |
| `DEBUG=true` | Sets OPTFLAGS to `-O0 -g` and enables more verbose debug logging |
| `ARCH=<architecture>` | To manually set the target architecture |
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
| `CXX=<compiler>` | Manualy set which compiler to use |
Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
4. **Install**
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
Notice! Only use "sudo" when installing to a NON user owned directory.
```bash
sudo gmake install
```
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
Notice! Only use "sudo" when installing to a NON user owned directory.
5. **(Recommended) Set suid bit to make btop always run as root (or other user)**
```bash
sudo gmake setuid
```
No need for `sudo` to see information for non user owned processes and to enable signal sending to any process.
Run after make install and use same PREFIX if any was used at install.
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel`
```bash
sudo gmake setuid
```
* **Uninstall**
```bash
sudo gmake uninstall
```
* **Remove any object files from source dir**
```bash
gmake clean
```
* **Remove all object files, binaries and created directories in source dir**
```bash
gmake distclean
```
* **Show help**
```bash
gmake help
```
## Compilation FreeBSD
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
Note that GNU make (`gmake`) is required to compile on FreeBSD.
<details>
<summary>
### With gmake
</summary>
1. **Install dependencies**
```bash
sudo pkg install gmake gcc11 coreutils git
```
2. **Clone repository**
```bash
git clone https://github.com/aristocratos/btop.git
cd btop
```
3. **Compile**
Append `VERBOSE=true` to display full compiler/linker commands.
Append `STATIC=true` for static compilation (only libgcc and libstdc++ will be static!).
Append `QUIET=true` for less verbose output.
Append `STRIP=true` to force stripping of debug symbols (adds `-s` linker flag).
Append `ARCH=<architecture>` to manually set the target architecture.
If omitted the makefile uses the machine triple (output of `-dumpmachine` compiler parameter) to detect the target system.
Use `ADDFLAGS` variable for appending flags to both compiler and linker.
For example: `ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
```bash
gmake
```
4. **Install**
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
Notice! Only use "sudo" when installing to a NON user owned directory.
```bash
sudo gmake install
```
5. **(Recommended) Set suid bit to make btop always run as root (or other user)**
No need for `sudo` to see information for non user owned processes and to enable signal sending to any process.
Run after make install and use same PREFIX if any was used at install.
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel`
```bash
sudo gmake setuid
```
* **Uninstall**
```bash
@ -661,13 +619,176 @@ Also needs a UTF8 locale and a font that covers:
```
</details>
<details>
<summary>
### With CMake (Community maintained)
</summary>
1. **Install build dependencies**
Requires Clang, CMake, Ninja and Git
```bash
brew update --quiet
brew install cmake git llvm ninja
```
2. **Clone the repository**
```bash
git clone https://github.com/aristocratos/btop.git && cd btop
```
3. **Compile**
```bash
# Configure
export LLVM_PREFIX="$(brew --prefix llvm)"
export CXX="$LLVM_PREFIX/bin/clang++"
export CPPFLAGS="-I$LLVM_PREFIX/include"
export LDFLAGS="-L$LLVM_PREFIX/lib -L$LLVM_PREFIX/lib/c++ -Wl,-rpath,$LLVM_PREFIX/lib/c++ -fuse-ld=$LLVM_PREFIX/bin/ld64.lld"
cmake -B build -G Ninja
# Build
cmake --build build
```
_**Note:** btop uses lots of C++ 20 features, so it's necessary to be specific about the compiler and the standard library. If you get a compile with Apple-Clang or GCC, feel free to add the instructions here._
This will automatically build a release version of btop.
Some useful options to pass to the configure step:
| Configure flag | Description |
|---------------------------------|-------------------------------------------------------------------------|
| `-DBTOP_LTO=<ON\|OFF>` | Enables link time optimization (ON by default) |
| `-DBTOP_USE_MOLD=<ON\|OFF>` | Use mold to link btop (OFF by default) |
| `-DBTOP_PEDANTIC=<ON\|OFF>` | Compile with additional warnings (OFF by default) |
| `-DBTOP_WERROR=<ON\|OFF>` | Compile with warnings as errors (OFF by default) |
| `-DCMAKE_INSTALL_PREFIX=<path>` | The installation prefix ('/usr/local' by default) |
To force any specific compiler, run `CXX=<compiler> cmake -B build -G Ninja`
4. **Install**
```bash
cmake --install build
```
May require root privileges
5. **Uninstall**
CMake doesn't generate an uninstall target by default. To remove installed files, run
```
cat build/install_manifest.txt | xargs rm -irv
```
6. **Cleanup build directory**
```bash
cmake --build build -t clean
```
</details>
## Compilation FreeBSD
Requires at least GCC 10 or Clang 16.
Note that GNU make (`gmake`) is required to compile on FreeBSD.
<details>
<summary>
### With gmake
</summary>
1. **Install dependencies**
```bash
sudo pkg install gmake gcc11 coreutils git
```
2. **Clone repository**
```bash
git clone https://github.com/aristocratos/btop.git
cd btop
```
3. **Compile**
```bash
gmake
```
Options for make:
| Flag | Description |
|---------------------------------|-------------------------------------------------------------------------|
| `VERBOSE=true` | To display full compiler/linker commands |
| `STATIC=true` | For static compilation (only libgcc and libstdc++) |
| `QUIET=true` | For less verbose output |
| `STRIP=true` | To force stripping of debug symbols (adds `-s` linker flag) |
| `DEBUG=true` | Sets OPTFLAGS to `-O0 -g` and enables more verbose debug logging |
| `ARCH=<architecture>` | To manually set the target architecture |
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
| `CXX=<compiler>` | Manualy set which compiler to use |
Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
4. **Install**
```bash
sudo gmake install
```
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
Notice! Only use "sudo" when installing to a NON user owned directory.
5. **(Recommended) Set suid bit to make btop always run as root (or other user)**
```bash
sudo gmake setuid
```
No need for `sudo` to see information for non user owned processes and to enable signal sending to any process.
Run after make install and use same PREFIX if any was used at install.
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel`
* **Uninstall**
```bash
sudo gmake uninstall
```
* **Remove any object files from source dir**
```bash
gmake clean
```
* **Remove all object files, binaries and created directories in source dir**
```bash
gmake distclean
```
* **Show help**
```bash
gmake help
```
</details>
<details>
<summary>
### With CMake (Community maintained)
</summary>
1. **Install build dependencies**
@ -690,7 +811,7 @@ Also needs a UTF8 locale and a font that covers:
```bash
git clone https://github.com/aristocratos/btop.git && cd btop
``````
```
3. **Compile**
@ -725,7 +846,7 @@ Also needs a UTF8 locale and a font that covers:
_**Note:** Static linking does not work with GCC._
To force a compiler, run `CXX=<compiler> cmake -B build -G Ninja`
To force any other compiler, run `CXX=<compiler> cmake -B build -G Ninja`
4. **Install**
@ -750,6 +871,100 @@ Also needs a UTF8 locale and a font that covers:
</details>
## Compilation OpenBSD
Requires at least GCC 10.
Note that GNU make (`gmake`) is required to compile on OpenBSD.
<details>
<summary>
### With gmake
</summary>
1. **Install dependencies**
```bash
pkg_add gmake gcc%11 g++%11 coreutils git
```
2. **Clone repository**
```bash
git clone https://github.com/aristocratos/btop.git
cd btop
```
3. **Compile**
```bash
gmake CXX=eg++
```
Options for make:
| Flag | Description |
|---------------------------------|-------------------------------------------------------------------------|
| `VERBOSE=true` | To display full compiler/linker commands |
| `STATIC=true` | For static compilation (only libgcc and libstdc++) |
| `QUIET=true` | For less verbose output |
| `STRIP=true` | To force stripping of debug symbols (adds `-s` linker flag) |
| `DEBUG=true` | Sets OPTFLAGS to `-O0 -g` and enables more verbose debug logging |
| `ARCH=<architecture>` | To manually set the target architecture |
| `ADDFLAGS=<flags>` | For appending flags to both compiler and linker |
| `CXX=<compiler>` | Manualy set which compiler to use |
Example: `gmake ADDFLAGS=-march=native` might give a performance boost if compiling only for your own system.
4. **Install**
```bash
sudo gmake install
```
Append `PREFIX=/target/dir` to set target, default: `/usr/local`
Notice! Only use "sudo" when installing to a NON user owned directory.
5. **(Recommended) Set suid bit to make btop always run as root (or other user)**
```bash
sudo gmake setuid
```
No need for `sudo` to see information for non user owned processes and to enable signal sending to any process.
Run after make install and use same PREFIX if any was used at install.
Set `SU_USER` and `SU_GROUP` to select user and group, default is `root` and `wheel`
* **Uninstall**
```bash
sudo gmake uninstall
```
* **Remove any object files from source dir**
```bash
gmake clean
```
* **Remove all object files, binaries and created directories in source dir**
```bash
gmake distclean
```
* **Show help**
```bash
gmake help
```
</details>
## Installing the snap
[![btop](https://snapcraft.io/btop/badge.svg)](https://snapcraft.io/btop)
@ -834,7 +1049,7 @@ graph_symbol_net = "default"
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
graph_symbol_proc = "default"
#* Manually set which boxes to show. Available values are "cpu mem net proc", separate values with whitespace.
#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace.
shown_boxes = "proc cpu mem net"
#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs.

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,7 @@ apps:
- network-observe
- home
- removable-media
- opengl
parts:
btop:
@ -44,10 +45,10 @@ parts:
source-type: git
plugin: make
make-parameters:
- CXX=g++-11
- PREFIX=/usr/local
- STATIC=true
- ADDFLAGS="-D SNAPPED"
build-packages:
- coreutils
- sed

View file

@ -32,6 +32,7 @@ tab-size = 4
#include <tuple>
#include <regex>
#include <chrono>
#include <utility>
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h>
@ -75,7 +76,7 @@ namespace Global {
{"#801414", "██████╔╝ ██║ ╚██████╔╝██║ ╚═╝ ╚═╝"},
{"#000000", "╚═════╝ ╚═╝ ╚═════╝ ╚═╝"},
};
const string Version = "1.2.13";
const string Version = "1.3.0";
int coreCount;
string overlay;
@ -183,8 +184,11 @@ void term_resize(bool force) {
if (force and refreshed) force = false;
}
else return;
static const array<string, 4> all_boxes = {"cpu", "mem", "net", "proc"};
#ifdef GPU_SUPPORT
static const array<string, 10> all_boxes = {"gpu5", "cpu", "mem", "net", "proc", "gpu0", "gpu1", "gpu2", "gpu3", "gpu4"};
#else
static const array<string, 5> all_boxes = {"", "cpu", "mem", "net", "proc"};
#endif
Global::resized = true;
if (Runner::active) Runner::stop();
Term::refresh();
@ -222,10 +226,18 @@ void term_resize(bool force) {
auto key = Input::get();
if (key == "q")
clean_quit(0);
else if (is_in(key, "1", "2", "3", "4")) {
Config::current_preset = -1;
Config::toggle_box(all_boxes.at(std::stoi(key) - 1));
boxes = Config::getS("shown_boxes");
else if (key.size() == 1 and isint(key)) {
auto intKey = stoi(key);
#ifdef GPU_SUPPORT
if ((intKey == 0 and Gpu::gpu_names.size() >= 5) or (intKey >= 5 and std::cmp_greater_equal(Gpu::gpu_names.size(), intKey - 4))) {
#else
if (intKey > 0 and intKey < 5) {
#endif
auto box = all_boxes.at(intKey);
Config::current_preset = -1;
Config::toggle_box(box);
boxes = Config::getS("shown_boxes");
}
}
}
min_size = Term::get_min_size(boxes);
@ -243,7 +255,7 @@ void clean_quit(int sig) {
Global::quitting = true;
Runner::stop();
if (Global::_runner_started) {
#ifdef __APPLE__
#if defined __APPLE__ || defined __OpenBSD__
if (pthread_join(Runner::runner_id, nullptr) != 0) {
Logger::warning("Failed to join _runner thread on exit!");
pthread_cancel(Runner::runner_id);
@ -258,6 +270,11 @@ void clean_quit(int sig) {
#endif
}
#ifdef GPU_SUPPORT
Gpu::Nvml::shutdown();
Gpu::Rsmi::shutdown();
#endif
Config::write();
if (Term::initialized) {
@ -274,7 +291,7 @@ void clean_quit(int sig) {
const auto excode = (sig != -1 ? sig : 0);
#ifdef __APPLE__
#if defined __APPLE__ || defined __OpenBSD__
_Exit(excode);
#else
quick_exit(excode);
@ -391,7 +408,9 @@ namespace Runner {
enum debug_actions {
collect_begin,
collect_done,
draw_begin,
draw_begin_only,
draw_done
};
@ -401,7 +420,7 @@ namespace Runner {
};
string debug_bg;
unordered_flat_map<string, array<uint64_t, 2>> debug_times;
std::unordered_map<string, array<uint64_t, 2>> debug_times;
class MyNumPunct : public std::numpunct<char>
{
@ -427,6 +446,13 @@ namespace Runner {
case collect_begin:
debug_times[name].at(collect) = time_micros();
return;
case collect_done:
debug_times[name].at(collect) = time_micros() - debug_times[name].at(collect);
debug_times["total"].at(collect) += debug_times[name].at(collect);
return;
case draw_begin_only:
debug_times[name].at(draw) = time_micros();
return;
case draw_begin:
debug_times[name].at(draw) = time_micros();
debug_times[name].at(collect) = debug_times[name].at(draw) - debug_times[name].at(collect);
@ -483,10 +509,14 @@ namespace Runner {
//! DEBUG stats
if (Global::debug) {
if (debug_bg.empty() or redraw)
Runner::debug_bg = Draw::createBox(2, 2, 33, 8, "", true, "μs");
if (debug_bg.empty() or redraw)
Runner::debug_bg = Draw::createBox(2, 2, 33,
#ifdef GPU_SUPPORT
9,
#else
8,
#endif
"", true, "μs");
debug_times.clear();
debug_times["total"] = {0, 0};
@ -496,6 +526,29 @@ namespace Runner {
//* Run collection and draw functions for all boxes
try {
#ifdef GPU_SUPPORT
//? GPU data collection
const bool gpu_in_cpu_panel = Gpu::gpu_names.size() > 0 and (
Config::getS("cpu_graph_lower").starts_with("gpu-") or Config::getS("cpu_graph_upper").starts_with("gpu-")
or (Gpu::shown == 0 and Config::getS("show_gpu_info") != "Off")
);
vector<unsigned int> gpu_panels = {};
for (auto& box : conf.boxes)
if (box.starts_with("gpu"))
gpu_panels.push_back(box.back()-'0');
vector<Gpu::gpu_info> gpus;
if (gpu_in_cpu_panel or not gpu_panels.empty()) {
if (Global::debug) debug_timer("gpu", collect_begin);
gpus = Gpu::collect(conf.no_update);
if (Global::debug) debug_timer("gpu", collect_done);
}
auto& gpus_ref = gpus;
#else
vector<Gpu::gpu_info> gpus_ref{};
#endif
//? CPU
if (v_contains(conf.boxes, "cpu")) {
try {
@ -515,7 +568,7 @@ namespace Runner {
if (Global::debug) debug_timer("cpu", draw_begin);
//? Draw box
if (not pause_output) output += Cpu::draw(cpu, conf.force_redraw, conf.no_update);
if (not pause_output) output += Cpu::draw(cpu, gpus_ref, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("cpu", draw_done);
}
@ -523,7 +576,24 @@ namespace Runner {
throw std::runtime_error("Cpu:: -> " + string{e.what()});
}
}
#ifdef GPU_SUPPORT
//? GPU
if (not gpu_panels.empty() and not gpus_ref.empty()) {
try {
if (Global::debug) debug_timer("gpu", draw_begin_only);
//? Draw box
if (not pause_output)
for (unsigned long i = 0; i < gpu_panels.size(); ++i)
output += Gpu::draw(gpus_ref[gpu_panels[i]], i, conf.force_redraw, conf.no_update);
if (Global::debug) debug_timer("gpu", draw_done);
}
catch (const std::exception& e) {
throw std::runtime_error("Gpu:: -> " + string{e.what()});
}
}
#endif
//? MEM
if (v_contains(conf.boxes, "mem")) {
try {
@ -583,6 +653,7 @@ namespace Runner {
throw std::runtime_error("Proc:: -> " + string{e.what()});
}
}
}
catch (const std::exception& e) {
Global::exit_error_msg = "Exception in runner thread -> " + string{e.what()};
@ -613,8 +684,9 @@ namespace Runner {
"{mv3}{hiFg}2 {mainFg}| Show MEM box"
"{mv4}{hiFg}3 {mainFg}| Show NET box"
"{mv5}{hiFg}4 {mainFg}| Show PROC box"
"{mv6}{hiFg}esc {mainFg}| Show menu"
"{mv7}{hiFg}q {mainFg}| Quit",
"{mv6}{hiFg}5-0 {mainFg}| Show GPU boxes"
"{mv7}{hiFg}esc {mainFg}| Show menu"
"{mv8}{hiFg}q {mainFg}| Quit",
"banner"_a = Draw::banner_gen(y, 0, true),
"titleFg"_a = Theme::c("title"), "b"_a = Fx::b, "hiFg"_a = Theme::c("hi_fg"), "mainFg"_a = Theme::c("main_fg"),
"mv1"_a = Mv::to(y+6, x),
@ -623,7 +695,8 @@ namespace Runner {
"mv4"_a = Mv::to(y+10, x),
"mv5"_a = Mv::to(y+11, x),
"mv6"_a = Mv::to(y+12, x-2),
"mv7"_a = Mv::to(y+13, x)
"mv7"_a = Mv::to(y+13, x-2),
"mv8"_a = Mv::to(y+14, x)
);
}
output += empty_bg;
@ -637,7 +710,11 @@ namespace Runner {
"post"_a = Theme::c("main_fg") + Fx::ub
);
static auto loc = std::locale(std::locale::classic(), new MyNumPunct);
#ifdef GPU_SUPPORT
for (const string name : {"cpu", "mem", "net", "proc", "gpu", "total"}) {
#else
for (const string name : {"cpu", "mem", "net", "proc", "total"}) {
#endif
if (not debug_times.contains(name)) debug_times[name] = {0,0};
const auto& [time_collect, time_draw] = debug_times.at(name);
if (name == "total") output += Fx::b;
@ -857,7 +934,7 @@ int main(int argc, char **argv) {
catch (...) { found.clear(); }
}
}
//
#ifdef __APPLE__
if (found.empty()) {
CFLocaleRef cflocale = CFLocaleCopyCurrent();
@ -901,7 +978,7 @@ int main(int argc, char **argv) {
Config::set("tty_mode", true);
Logger::info("Forcing tty mode: setting 16 color mode and using tty friendly graph symbols");
}
#ifndef __APPLE__
#if not defined __APPLE__ && not defined __OpenBSD__
else if (not Global::arg_tty and Term::current_tty.starts_with("/dev/tty")) {
Config::set("tty_mode", true);
Logger::info("Real tty detected: setting 16 color mode and using tty friendly graph symbols");

View file

@ -21,6 +21,7 @@ tab-size = 4
#include <fstream>
#include <ranges>
#include <string_view>
#include <utility>
#include <fmt/core.h>
@ -73,14 +74,16 @@ namespace Config {
"#* Note that \"tty\" only has half the horizontal resolution of the other two, so will show a shorter historical view."},
{"graph_symbol_cpu", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
#ifdef GPU_SUPPORT
{"graph_symbol_gpu", "# Graph symbol to use for graphs in gpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
#endif
{"graph_symbol_mem", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
{"graph_symbol_net", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
{"graph_symbol_proc", "# Graph symbol to use for graphs in cpu box, \"default\", \"braille\", \"block\" or \"tty\"."},
{"shown_boxes", "#* Manually set which boxes to show. Available values are \"cpu mem net proc\", separate values with whitespace."},
{"shown_boxes", "#* Manually set which boxes to show. Available values are \"cpu mem net proc\" and \"gpu0\" through \"gpu5\", separate values with whitespace."},
{"update_ms", "#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs."},
@ -114,7 +117,9 @@ namespace Config {
{"cpu_graph_lower", "#* Sets the CPU stat shown in lower half of the CPU graph, \"total\" is always available.\n"
"#* Select from a list of detected attributes from the options menu."},
#ifdef GPU_SUPPORT
{"show_gpu_info", "#* If gpu info should be shown in the cpu box. Available values = \"Auto\", \"On\" and \"Off\"."},
#endif
{"cpu_invert_lower", "#* Toggles if the lower CPU graph should be inverted."},
{"cpu_single_graph", "#* Set to True to completely disable the lower CPU graph."},
@ -194,21 +199,36 @@ namespace Config {
{"selected_battery", "#* Which battery to use if multiple are present. \"Auto\" for auto detection."},
{"log_level", "#* Set loglevel for \"~/.config/btop/btop.log\" levels are: \"ERROR\" \"WARNING\" \"INFO\" \"DEBUG\".\n"
"#* The level set includes all lower levels, i.e. \"DEBUG\" will show all logging info."}
"#* The level set includes all lower levels, i.e. \"DEBUG\" will show all logging info."},
#ifdef GPU_SUPPORT
{"nvml_measure_pcie_speeds",
"#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards."},
{"gpu_mirror_graph", "#* Horizontally mirror the GPU graph."},
{"custom_gpu_name0", "#* Custom gpu0 model name, empty string to disable."},
{"custom_gpu_name1", "#* Custom gpu1 model name, empty string to disable."},
{"custom_gpu_name2", "#* Custom gpu2 model name, empty string to disable."},
{"custom_gpu_name3", "#* Custom gpu3 model name, empty string to disable."},
{"custom_gpu_name4", "#* Custom gpu4 model name, empty string to disable."},
{"custom_gpu_name5", "#* Custom gpu5 model name, empty string to disable."},
#endif
};
unordered_flat_map<std::string_view, string> strings = {
std::unordered_map<std::string_view, string> strings = {
{"color_theme", "Default"},
{"shown_boxes", "cpu mem net proc"},
{"graph_symbol", "braille"},
{"presets", "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty"},
{"graph_symbol_cpu", "default"},
{"graph_symbol_gpu", "default"},
{"graph_symbol_mem", "default"},
{"graph_symbol_net", "default"},
{"graph_symbol_proc", "default"},
{"proc_sorting", "cpu lazy"},
{"cpu_graph_upper", "total"},
{"cpu_graph_lower", "total"},
{"cpu_graph_upper", "Auto"},
{"cpu_graph_lower", "Auto"},
{"cpu_sensor", "Auto"},
{"selected_battery", "Auto"},
{"cpu_core_map", ""},
@ -222,10 +242,19 @@ namespace Config {
{"proc_filter", ""},
{"proc_command", ""},
{"selected_name", ""},
#ifdef GPU_SUPPORT
{"custom_gpu_name0", ""},
{"custom_gpu_name1", ""},
{"custom_gpu_name2", ""},
{"custom_gpu_name3", ""},
{"custom_gpu_name4", ""},
{"custom_gpu_name5", ""},
{"show_gpu_info", "Auto"}
#endif
};
unordered_flat_map<std::string_view, string> stringsTmp;
std::unordered_map<std::string_view, string> stringsTmp;
unordered_flat_map<std::string_view, bool> bools = {
std::unordered_map<std::string_view, bool> bools = {
{"theme_background", true},
{"truecolor", true},
{"rounded_corners", true},
@ -271,10 +300,14 @@ namespace Config {
{"show_detailed", false},
{"proc_filtering", false},
{"proc_aggregate", false},
#ifdef GPU_SUPPORT
{"nvml_measure_pcie_speeds", true},
{"gpu_mirror_graph", true},
#endif
};
unordered_flat_map<std::string_view, bool> boolsTmp;
std::unordered_map<std::string_view, bool> boolsTmp;
unordered_flat_map<std::string_view, int> ints = {
std::unordered_map<std::string_view, int> ints = {
{"update_ms", 2000},
{"net_download", 100},
{"net_upload", 100},
@ -285,7 +318,7 @@ namespace Config {
{"proc_selected", 0},
{"proc_last_selected", 0},
};
unordered_flat_map<std::string_view, int> intsTmp;
std::unordered_map<std::string_view, int> intsTmp;
bool _locked(const std::string_view name) {
atomic_wait(writelock, true);
@ -321,7 +354,7 @@ namespace Config {
validError = "Malformatted preset in config value presets!";
return false;
}
if (not is_in(vals.at(0), "cpu", "mem", "net", "proc")) {
if (not is_in(vals.at(0), "cpu", "mem", "net", "proc", "gpu0", "gpu1", "gpu2", "gpu3", "gpu4", "gpu5")) {
validError = "Invalid box name in config value presets!";
return false;
}
@ -417,6 +450,11 @@ namespace Config {
else if (name == "shown_boxes" and not value.empty() and not check_boxes(value))
validError = "Invalid box name(s) in shown_boxes!";
#ifdef GPU_SUPPORT
else if (name == "show_gpu_info" and not v_contains(show_gpu_values, value))
validError = "Invalid value for show_gpu_info: " + value;
#endif
else if (name == "presets" and not presetsValid(value))
return false;
@ -519,6 +557,13 @@ namespace Config {
auto new_boxes = ssplit(boxes);
for (auto& box : new_boxes) {
if (not v_contains(valid_boxes, box)) return false;
#ifdef GPU_SUPPORT
if (box.starts_with("gpu")) {
size_t gpu_num = stoi(box.substr(3));
if (gpu_num == 0) gpu_num = 5;
if (std::cmp_greater(gpu_num, Gpu::gpu_names.size())) return false;
}
#endif
}
current_boxes = std::move(new_boxes);
return true;

View file

@ -22,11 +22,10 @@ tab-size = 4
#include <vector>
#include <filesystem>
#include <robin_hood.h>
#include <unordered_map>
using std::string;
using std::vector;
using robin_hood::unordered_flat_map;
//* Functions and variables for reading and writing the btop config file
namespace Config {
@ -34,18 +33,25 @@ namespace Config {
extern std::filesystem::path conf_dir;
extern std::filesystem::path conf_file;
extern unordered_flat_map<std::string_view, string> strings;
extern unordered_flat_map<std::string_view, string> stringsTmp;
extern unordered_flat_map<std::string_view, bool> bools;
extern unordered_flat_map<std::string_view, bool> boolsTmp;
extern unordered_flat_map<std::string_view, int> ints;
extern unordered_flat_map<std::string_view, int> intsTmp;
extern std::unordered_map<std::string_view, string> strings;
extern std::unordered_map<std::string_view, string> stringsTmp;
extern std::unordered_map<std::string_view, bool> bools;
extern std::unordered_map<std::string_view, bool> boolsTmp;
extern std::unordered_map<std::string_view, int> ints;
extern std::unordered_map<std::string_view, int> intsTmp;
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
const vector<string> valid_graph_symbols_def = { "default", "braille", "block", "tty" };
const vector<string> valid_boxes = { "cpu", "mem", "net", "proc" };
const vector<string> valid_boxes = {
"cpu", "mem", "net", "proc"
#ifdef GPU_SUPPORT
,"gpu0", "gpu1", "gpu2", "gpu3", "gpu4", "gpu5"
#endif
};
const vector<string> temp_scales = { "celsius", "fahrenheit", "kelvin", "rankine" };
#ifdef GPU_SUPPORT
const vector<string> show_gpu_values = { "Auto", "On", "Off" };
#endif
extern vector<string> current_boxes;
extern vector<string> preset_list;
extern vector<string> available_batteries;

File diff suppressed because it is too large Load diff

View file

@ -21,10 +21,9 @@ tab-size = 4
#include <string>
#include <vector>
#include <array>
#include <robin_hood.h>
#include <unordered_map>
#include <deque>
using robin_hood::unordered_flat_map;
using std::array;
using std::deque;
using std::string;
@ -108,7 +107,7 @@ namespace Draw {
long long offset;
long long last = 0, max_value = 0;
bool current = true, tty_mode = false;
unordered_flat_map<bool, vector<string>> graphs = { {true, {}}, {false, {}}};
std::unordered_map<bool, vector<string>> graphs = { {true, {}}, {false, {}}};
//* Create two representations of the graph to switch between to represent two values for each braille character
void _create(const deque<long long>& data, int data_offset);
@ -135,6 +134,6 @@ namespace Draw {
namespace Proc {
extern Draw::TextEdit filter;
extern unordered_flat_map<size_t, Draw::Graph> p_graphs;
extern unordered_flat_map<size_t, int> p_counters;
extern std::unordered_map<size_t, Draw::Graph> p_graphs;
extern std::unordered_map<size_t, int> p_counters;
}

View file

@ -23,6 +23,7 @@ tab-size = 4
#include <mutex>
#include <signal.h>
#include <sys/select.h>
#include <utility>
#include "btop_input.hpp"
#include "btop_tools.hpp"
@ -38,7 +39,7 @@ namespace rng = std::ranges;
namespace Input {
//* Map for translating key codes to readable values
const unordered_flat_map<string, string> Key_escapes = {
const std::unordered_map<string, string> Key_escapes = {
{"\033", "escape"},
{"\n", "enter"},
{" ", "space"},
@ -53,9 +54,13 @@ namespace Input {
{"[C", "right"},
{"OC", "right"},
{"[2~", "insert"},
{"[4h", "insert"},
{"[3~", "delete"},
{"[P", "delete"},
{"[H", "home"},
{"[1~", "home"},
{"[F", "end"},
{"[4~", "end"},
{"[5~", "page_up"},
{"[6~", "page_down"},
{"\t", "tab"},
@ -77,7 +82,7 @@ namespace Input {
sigset_t signal_mask;
std::atomic<bool> polling (false);
array<int, 2> mouse_pos;
unordered_flat_map<string, Mouse_loc> mouse_mappings;
std::unordered_map<string, Mouse_loc> mouse_mappings;
deque<string> history(50, "");
string old_filter;
@ -222,11 +227,21 @@ namespace Input {
Menu::show(Menu::Menus::Options);
return;
}
else if (is_in(key, "1", "2", "3", "4")) {
else if (key.size() == 1 and isint(key)) {
auto intKey = stoi(key);
#ifdef GPU_SUPPORT
static const array<string, 10> boxes = {"gpu5", "cpu", "mem", "net", "proc", "gpu0", "gpu1", "gpu2", "gpu3", "gpu4"};
if ((intKey == 0 and Gpu::gpu_names.size() < 5) or (intKey >= 5 and std::cmp_less(Gpu::gpu_names.size(), intKey - 4)))
return;
#else
static const array<string, 10> boxes = {"", "cpu", "mem", "net", "proc"};
if (intKey == 0 or intKey > 4)
return;
#endif
atomic_wait(Runner::active);
Config::current_preset = -1;
static const array<string, 4> boxes = {"cpu", "mem", "net", "proc"};
Config::toggle_box(boxes.at(std::stoi(key) - 1));
Config::toggle_box(boxes.at(intKey));
Draw::calcSizes();
Runner::run("all", false, true);
return;
@ -305,6 +320,9 @@ namespace Input {
else if (key == "c")
Config::flip("proc_per_core");
else if (key == "%")
Config::flip("proc_mem_bytes");
else if (key == "delete" and not Config::getS("proc_filter").empty())
Config::set("proc_filter", ""s);

View file

@ -21,10 +21,9 @@ tab-size = 4
#include <string>
#include <atomic>
#include <array>
#include <robin_hood.h>
#include <unordered_map>
#include <deque>
using robin_hood::unordered_flat_map;
using std::array;
using std::atomic;
using std::deque;
@ -44,7 +43,7 @@ namespace Input {
};
//? line, col, height, width
extern unordered_flat_map<string, Mouse_loc> mouse_mappings;
extern std::unordered_map<string, Mouse_loc> mouse_mappings;
//* Signal mask used during polling read
extern sigset_t signal_mask;

View file

@ -17,7 +17,7 @@ tab-size = 4
*/
#include <deque>
#include <robin_hood.h>
#include <unordered_map>
#include <array>
#include <signal.h>
#include <errno.h>
@ -31,7 +31,6 @@ tab-size = 4
#include "btop_draw.hpp"
#include "btop_shared.hpp"
using robin_hood::unordered_flat_map;
using std::array;
using std::ceil;
using std::max;
@ -123,7 +122,7 @@ namespace Menu {
#endif
};
unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
std::unordered_map<string, Input::Mouse_loc> mouse_mappings;
const array<array<string, 3>, 3> menu_normal = {
array<string, 3>{
@ -173,6 +172,7 @@ namespace Menu {
{"2", "Toggle MEM box."},
{"3", "Toggle NET box."},
{"4", "Toggle PROC box."},
{"5", "Toggle GPU box."},
{"d", "Toggle disks view in MEM box."},
{"F2, o", "Shows options."},
{"F1, ?, h", "Shows this window."},
@ -195,6 +195,7 @@ namespace Menu {
{"c", "Toggle per-core cpu usage of processes."},
{"r", "Reverse sorting order in processes box."},
{"e", "Toggle processes tree view."},
{"%", "Toggles memory display mode in processes box."},
{"Selected +, -", "Expand/collapse the selected process in tree view."},
{"Selected t", "Terminate selected process with SIGTERM - 15."},
{"Selected k", "Kill selected process with SIGKILL - 9."},
@ -270,6 +271,9 @@ namespace Menu {
"Manually set which boxes to show.",
"",
"Available values are \"cpu mem net proc\".",
#ifdef GPU_SUPPORT
"Or \"gpu0\" through \"gpu5\" for GPU boxes.",
#endif
"Separate values with whitespace.",
"",
"Toggle between presets with key \"p\"."},
@ -372,23 +376,49 @@ namespace Menu {
{"cpu_graph_upper",
"Cpu upper graph.",
"",
"Sets the CPU stat shown in upper half of",
"Sets the CPU/GPU stat shown in upper half of",
"the CPU graph.",
"",
"\"total\" = Total cpu usage.",
"CPU:",
"\"total\" = Total cpu usage. (Auto)",
"\"user\" = User mode cpu usage.",
"\"system\" = Kernel mode cpu usage.",
"+ more depending on kernel."},
"+ more depending on kernel.",
#ifdef GPU_SUPPORT
"",
"GPU:",
"\"gpu-totals\" = GPU usage split by device.",
"\"gpu-vram-totals\" = VRAM usage split by GPU.",
"\"gpu-pwr-totals\" = Power usage split by GPU.",
"\"gpu-average\" = Avg usage of all GPUs.",
"\"gpu-vram-total\" = VRAM usage of all GPUs.",
"\"gpu-pwr-total\" = Power usage of all GPUs.",
"Not all stats are supported on all devices."
#endif
},
{"cpu_graph_lower",
"Cpu lower graph.",
"",
"Sets the CPU stat shown in lower half of",
"Sets the CPU/GPU stat shown in lower half of",
"the CPU graph.",
"",
"CPU:",
"\"total\" = Total cpu usage.",
"\"user\" = User mode cpu usage.",
"\"system\" = Kernel mode cpu usage.",
"+ more depending on kernel."},
"+ more depending on kernel.",
#ifdef GPU_SUPPORT
"",
"GPU:",
"\"gpu-totals\" = GPU usage split/device. (Auto)",
"\"gpu-vram-totals\" = VRAM usage split by GPU.",
"\"gpu-pwr-totals\" = Power usage split by GPU.",
"\"gpu-average\" = Avg usage of all GPUs.",
"\"gpu-vram-total\" = VRAM usage of all GPUs.",
"\"gpu-pwr-total\" = Power usage of all GPUs.",
"Not all stats are supported on all devices."
#endif
},
{"cpu_invert_lower",
"Toggles orientation of the lower CPU graph.",
"",
@ -400,12 +430,24 @@ namespace Menu {
"to fit to box height.",
"",
"True or False."},
#ifdef GPU_SUPPORT
{"show_gpu_info",
"Show gpu info in cpu box.",
"",
"Toggles gpu stats in cpu box and the",
"gpu graph (if \"cpu_graph_lower\" is set to",
"\"Auto\").",
"",
"\"Auto\" to show when no gpu box is shown.",
"\"On\" to always show.",
"\"Off\" to never show."},
#endif
{"check_temp",
"Enable cpu temperature reporting.",
"",
"True or False."},
{"cpu_sensor",
"Cpu temperature sensor",
"Cpu temperature sensor.",
"",
"Select the sensor that corresponds to",
"your cpu temperature.",
@ -445,7 +487,7 @@ namespace Menu {
"Rankine, 0 = abosulte zero, 1 degree change",
"equals 1 degree change in Fahrenheit."},
{"show_cpu_freq",
"Show CPU frequency",
"Show CPU frequency.",
"",
"Can cause slowdowns on systems with many",
"cores and certain kernel versions."},
@ -461,6 +503,50 @@ namespace Menu {
"",
"True or False."},
},
#ifdef GPU_SUPPORT
{
{"nvml_measure_pcie_speeds",
"Measure PCIe throughput on NVIDIA cards.",
"",
"May impact performance on certain cards.",
"",
"True or False."},
{"graph_symbol_gpu",
"Graph symbol to use for graphs in gpu box.",
"",
"\"default\", \"braille\", \"block\" or \"tty\".",
"",
"\"default\" for the general default symbol.",},
{"gpu_mirror_graph",
"Horizontally mirror the GPU graph.",
"",
"True or False."},
{"custom_gpu_name0",
"Custom gpu0 model name in gpu stats box.",
"",
"Empty string to disable."},
{"custom_gpu_name1",
"Custom gpu1 model name in gpu stats box.",
"",
"Empty string to disable."},
{"custom_gpu_name2",
"Custom gpu2 model name in gpu stats box.",
"",
"Empty string to disable."},
{"custom_gpu_name3",
"Custom gpu3 model name in gpu stats box.",
"",
"Empty string to disable."},
{"custom_gpu_name4",
"Custom gpu4 model name in gpu stats box.",
"",
"Empty string to disable."},
{"custom_gpu_name5",
"Custom gpu5 model name in gpu stats box.",
"",
"Empty string to disable."},
},
#endif
{
{"mem_below_net",
"Mem box location.",
@ -1078,7 +1164,7 @@ namespace Menu {
static Draw::TextEdit editor;
static string warnings;
static bitset<8> selPred;
static const unordered_flat_map<string, std::reference_wrapper<const vector<string>>> optionsList = {
static const std::unordered_map<string, std::reference_wrapper<const vector<string>>> optionsList = {
{"color_theme", std::cref(Theme::themes)},
{"log_level", std::cref(Logger::log_levels)},
{"temp_scale", std::cref(Config::temp_scales)},
@ -1092,6 +1178,10 @@ namespace Menu {
{"cpu_graph_lower", std::cref(Cpu::available_fields)},
{"cpu_sensor", std::cref(Cpu::available_sensors)},
{"selected_battery", std::cref(Config::available_batteries)},
#ifdef GPU_SUPPORT
{"show_gpu_info", std::cref(Config::show_gpu_values)},
{"graph_symbol_gpu", std::cref(Config::valid_graph_symbols_def)},
#endif
};
auto tty_mode = Config::getB("tty_mode");
auto vim_keys = Config::getB("vim_keys");
@ -1143,7 +1233,8 @@ namespace Menu {
const auto& option = categories[selected_cat][item_height * page + selected][0];
if (selPred.test(isString) and Config::stringValid(option, editor.text)) {
Config::set(option, editor.text);
if (option == "custom_cpu_name") screen_redraw = true;
if (option == "custom_cpu_name" or option.starts_with("custom_gpu_name"))
screen_redraw = true;
else if (is_in(option, "shown_boxes", "presets")) {
screen_redraw = true;
Config::current_preset = -1;
@ -1224,7 +1315,7 @@ namespace Menu {
if (--selected_cat < 0) selected_cat = (int)categories.size() - 1;
page = selected = 0;
}
else if (is_in(key, "1", "2", "3", "4", "5") or key.starts_with("select_cat_")) {
else if (is_in(key, "1", "2", "3", "4", "5", "6") or key.starts_with("select_cat_")) {
selected_cat = key.back() - '0' - 1;
page = selected = 0;
}
@ -1276,7 +1367,7 @@ namespace Menu {
Logger::set(optList.at(i));
Logger::info("Logger set to " + optList.at(i));
}
else if (is_in(option, "proc_sorting", "cpu_sensor") or option.starts_with("graph_symbol") or option.starts_with("cpu_graph_"))
else if (is_in(option, "proc_sorting", "cpu_sensor", "show_gpu_info") or option.starts_with("graph_symbol") or option.starts_with("cpu_graph_"))
screen_redraw = true;
}
else
@ -1322,11 +1413,19 @@ namespace Menu {
//? Category buttons
out += Mv::to(y+7, x+4);
#ifdef GPU_SUPPORT
for (int i = 0; const auto& m : {"general", "cpu", "gpu", "mem", "net", "proc"}) {
#else
for (int i = 0; const auto& m : {"general", "cpu", "mem", "net", "proc"}) {
#endif
out += Fx::b + (i == selected_cat
? Theme::c("hi_fg") + '[' + Theme::c("title") + m + Theme::c("hi_fg") + ']'
: Theme::c("hi_fg") + to_string(i + 1) + Theme::c("title") + m + ' ')
#ifdef GPU_SUPPORT
+ Mv::r(7);
#else
+ Mv::r(10);
#endif
if (string button_name = "select_cat_" + to_string(i + 1); not editing and not mouse_mappings.contains(button_name))
mouse_mappings[button_name] = {y+6, x+2 + 15*i, 3, 15};
i++;

View file

@ -38,7 +38,7 @@ namespace Menu {
extern bool redraw;
//? line, col, height, width
extern unordered_flat_map<string, Input::Mouse_loc> mouse_mappings;
extern std::unordered_map<string, Input::Mouse_loc> mouse_mappings;
//* Creates a message box centered on screen
//? Height of box is determined by size of content vector

View file

@ -25,6 +25,18 @@ tab-size = 4
namespace rng = std::ranges;
using namespace Tools;
#ifdef GPU_SUPPORT
namespace Gpu {
vector<string> gpu_names;
vector<int> gpu_b_height_offsets;
std::unordered_map<string, deque<long long>> shared_gpu_percent = {
{"gpu-average", {}},
{"gpu-vram-total", {}},
{"gpu-pwr-total", {}},
};
long long gpu_pwr_total_max;
}
#endif
namespace Proc {
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree) {

View file

@ -26,10 +26,9 @@ tab-size = 4
#include <tuple>
#include <vector>
#include <ifaddrs.h>
#include <robin_hood.h>
#include <unordered_map>
#include <unistd.h>
using robin_hood::unordered_flat_map;
using std::array;
using std::atomic;
using std::deque;
@ -86,6 +85,91 @@ namespace Shared {
}
namespace Gpu {
#ifdef GPU_SUPPORT
extern vector<string> box;
extern int width, height, min_width, min_height;
extern vector<int> x_vec, y_vec;
extern vector<bool> redraw;
extern int shown;
extern vector<char> shown_panels;
extern vector<string> gpu_names;
extern vector<int> gpu_b_height_offsets;
extern long long gpu_pwr_total_max;
extern std::unordered_map<string, deque<long long>> shared_gpu_percent; // averages, power/vram total
const array mem_names { "used"s, "free"s };
//* Container for process information // TODO
/*struct proc_info {
unsigned int pid;
unsigned long long mem;
};*/
//* Container for supported Gpu::*::collect() functions
struct gpu_info_supported {
bool gpu_utilization = true,
mem_utilization = true,
gpu_clock = true,
mem_clock = true,
pwr_usage = true,
pwr_state = true,
temp_info = true,
mem_total = true,
mem_used = true,
pcie_txrx = true;
};
//* Per-device container for GPU info
struct gpu_info {
std::unordered_map<string, deque<long long>> gpu_percent = {
{"gpu-totals", {}},
{"gpu-vram-totals", {}},
{"gpu-pwr-totals", {}},
};
unsigned int gpu_clock_speed; // MHz
long long pwr_usage; // mW
long long pwr_max_usage = 255000;
long long pwr_state;
deque<long long> temp = {0};
long long temp_max = 110;
long long mem_total = 0;
long long mem_used = 0;
deque<long long> mem_utilization_percent = {0}; // TODO: properly handle GPUs that can't report some stats
long long mem_clock_speed = 0; // MHz
long long pcie_tx = 0; // KB/s
long long pcie_rx = 0;
gpu_info_supported supported_functions;
// vector<proc_info> graphics_processes = {}; // TODO
// vector<proc_info> compute_processes = {};
};
namespace Nvml {
extern bool shutdown();
}
namespace Rsmi {
extern bool shutdown();
}
//* Collect gpu stats and temperatures
auto collect(bool no_update = false) -> vector<gpu_info>&;
//* Draw contents of gpu box using <gpus> as source
string draw(const gpu_info& gpu, unsigned long index, bool force_redraw, bool data_same);
#else
struct gpu_info {
bool supported = false;
};
#endif
}
namespace Cpu {
extern string box;
extern int x, y, width, height, min_width, min_height;
@ -96,7 +180,7 @@ namespace Cpu {
extern tuple<int, long, string> current_bat;
struct cpu_info {
unordered_flat_map<string, deque<long long>> cpu_percent = {
std::unordered_map<string, deque<long long>> cpu_percent = {
{"total", {}},
{"user", {}},
{"nice", {}},
@ -119,11 +203,13 @@ namespace Cpu {
auto collect(bool no_update = false) -> cpu_info&;
//* Draw contents of cpu box using <cpu> as source
string draw(const cpu_info& cpu, bool force_redraw = false, bool data_same = false);
string draw(const cpu_info& cpu, const vector<Gpu::gpu_info>& gpu, bool force_redraw = false, bool data_same = false);
//* Parse /proc/cpu info for mapping of core ids
auto get_core_mapping() -> unordered_flat_map<int, int>;
extern unordered_flat_map<int, int> core_mapping;
auto get_core_mapping() -> std::unordered_map<int, int>;
extern std::unordered_map<int, int> core_mapping;
auto get_cpuHz() -> string;
//* Get battery info from /sys
auto get_battery() -> tuple<int, long, string>;
@ -155,13 +241,13 @@ namespace Mem {
};
struct mem_info {
unordered_flat_map<string, uint64_t> stats =
std::unordered_map<string, uint64_t> stats =
{{"used", 0}, {"available", 0}, {"cached", 0}, {"free", 0},
{"swap_total", 0}, {"swap_used", 0}, {"swap_free", 0}};
unordered_flat_map<string, deque<long long>> percent =
std::unordered_map<string, deque<long long>> percent =
{{"used", {}}, {"available", {}}, {"cached", {}}, {"free", {}},
{"swap_total", {}}, {"swap_used", {}}, {"swap_free", {}}};
unordered_flat_map<string, disk_info> disks;
std::unordered_map<string, disk_info> disks;
vector<string> disks_order;
};
@ -183,7 +269,7 @@ namespace Net {
extern string selected_iface;
extern vector<string> interfaces;
extern bool rescale;
extern unordered_flat_map<string, uint64_t> graph_max;
extern std::unordered_map<string, uint64_t> graph_max;
struct net_stat {
uint64_t speed{}; // defaults to 0
@ -195,14 +281,14 @@ namespace Net {
};
struct net_info {
unordered_flat_map<string, deque<long long>> bandwidth = { {"download", {}}, {"upload", {}} };
unordered_flat_map<string, net_stat> stat = { {"download", {}}, {"upload", {}} };
std::unordered_map<string, deque<long long>> bandwidth = { {"download", {}}, {"upload", {}} };
std::unordered_map<string, net_stat> stat = { {"download", {}}, {"upload", {}} };
string ipv4{}; // defaults to ""
string ipv6{}; // defaults to ""
bool connected{}; // defaults to false
};
extern unordered_flat_map<string, net_info> current_net;
extern std::unordered_map<string, net_info> current_net;
//* Collect net upload/download stats
auto collect(bool no_update=false) -> net_info&;
@ -235,7 +321,7 @@ namespace Proc {
};
//? Translation from process state char to explanative string
const unordered_flat_map<char, string> proc_states = {
const std::unordered_map<char, string> proc_states = {
{'R', "Running"},
{'S', "Sleeping"},
{'D', "Waiting"},

View file

@ -42,11 +42,11 @@ namespace Theme {
fs::path theme_dir;
fs::path user_theme_dir;
vector<string> themes;
unordered_flat_map<string, string> colors;
unordered_flat_map<string, array<int, 3>> rgbs;
unordered_flat_map<string, array<string, 101>> gradients;
std::unordered_map<string, string> colors;
std::unordered_map<string, array<int, 3>> rgbs;
std::unordered_map<string, array<string, 101>> gradients;
const unordered_flat_map<string, string> Default_theme = {
const std::unordered_map<string, string> Default_theme = {
{ "main_bg", "#00" },
{ "main_fg", "#cc" },
{ "title", "#ee" },
@ -91,7 +91,7 @@ namespace Theme {
{ "process_end", "#d45454" }
};
const unordered_flat_map<string, string> TTY_theme = {
const std::unordered_map<string, string> TTY_theme = {
{ "main_bg", "\x1b[0;40m" },
{ "main_fg", "\x1b[37m" },
{ "title", "\x1b[97m" },
@ -224,7 +224,7 @@ namespace Theme {
}
//* Generate colors and rgb decimal vectors for the theme
void generateColors(const unordered_flat_map<string, string>& source) {
void generateColors(const std::unordered_map<string, string>& source) {
vector<string> t_rgb;
string depth;
bool t_to_256 = Config::getB("lowcolor");
@ -372,7 +372,7 @@ namespace Theme {
//* Load a .theme file from disk
auto loadFile(const string& filename) {
unordered_flat_map<string, string> theme_out;
std::unordered_map<string, string> theme_out;
const fs::path filepath = filename;
if (not fs::exists(filepath))
return Default_theme;

View file

@ -22,12 +22,11 @@ tab-size = 4
#include <filesystem>
#include <string>
#include <vector>
#include <robin_hood.h>
#include <unordered_map>
using std::array;
using std::string;
using std::vector;
using robin_hood::unordered_flat_map;
namespace Theme {
extern std::filesystem::path theme_dir;
@ -54,9 +53,9 @@ namespace Theme {
//* Set current theme from current "color_theme" value in config
void setTheme();
extern unordered_flat_map<string, string> colors;
extern unordered_flat_map<string, array<int, 3>> rgbs;
extern unordered_flat_map<string, array<string, 101>> gradients;
extern std::unordered_map<string, string> colors;
extern std::unordered_map<string, array<int, 3>> rgbs;
extern std::unordered_map<string, array<string, 101>> gradients;
//* Return escape code for color <name>
inline const string& c(const string& name) { return colors.at(name); }

View file

@ -26,11 +26,12 @@ tab-size = 4
#include <utility>
#include <ranges>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include "robin_hood.h"
#include "unordered_map"
#include "widechar_width.hpp"
#include "btop_shared.hpp"
#include "btop_tools.hpp"
@ -42,7 +43,6 @@ using std::flush;
using std::max;
using std::string_view;
using std::to_string;
using robin_hood::unordered_flat_map;
using namespace std::literals; // to use operator""s
@ -89,12 +89,27 @@ namespace Term {
}
bool refresh(bool only_check) {
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) return false;
if (width != w.ws_col or height != w.ws_row) {
// Query dimensions of '/dev/tty' of the 'STDOUT_FILENO' isn't avaiable.
// This variable is set in those cases to avoid calls to ioctl
constinit static bool uses_dev_tty = false;
struct winsize wsize {};
if (uses_dev_tty || ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsize) < 0 || (wsize.ws_col == 0 && wsize.ws_row == 0)) {
Logger::error(R"(Couldn't determine terminal size of "STDOUT_FILENO"!)");
auto dev_tty = open("/dev/tty", O_RDONLY);
if (dev_tty != -1) {
ioctl(dev_tty, TIOCGWINSZ, &wsize);
close(dev_tty);
}
else {
Logger::error(R"(Couldn't determine terminal size of "/dev/tty"!)");
return false;
}
uses_dev_tty = true;
}
if (width != wsize.ws_col or height != wsize.ws_row) {
if (not only_check) {
width = w.ws_col;
height = w.ws_row;
width = wsize.ws_col;
height = wsize.ws_row;
}
return true;
}
@ -102,19 +117,31 @@ namespace Term {
}
auto get_min_size(const string& boxes) -> array<int, 2> {
bool cpu = boxes.find("cpu") != string::npos;
bool mem = boxes.find("mem") != string::npos;
bool net = boxes.find("net") != string::npos;
bool proc = boxes.find("proc") != string::npos;
int width = 0;
bool cpu = boxes.find("cpu") != string::npos;
bool mem = boxes.find("mem") != string::npos;
bool net = boxes.find("net") != string::npos;
bool proc = boxes.find("proc") != string::npos;
#ifdef GPU_SUPPORT
int gpu = 0;
if (not Gpu::gpu_names.empty())
for (char i = '0'; i <= '5'; ++i)
gpu += (boxes.find(std::string("gpu") + i) != string::npos);
#endif
int width = 0;
if (mem) width = Mem::min_width;
else if (net) width = Mem::min_width;
width += (proc ? Proc::min_width : 0);
if (cpu and width < Cpu::min_width) width = Cpu::min_width;
#ifdef GPU_SUPPORT
if (gpu != 0 and width < Gpu::min_width) width = Gpu::min_width;
#endif
int height = (cpu ? Cpu::min_height : 0);
if (proc) height += Proc::min_height;
else height += (mem ? Mem::min_height : 0) + (net ? Net::min_height : 0);
#ifdef GPU_SUPPORT
height += Gpu::min_height*gpu;
#endif
return { width, height };
}
@ -126,8 +153,10 @@ namespace Term {
tcgetattr(STDIN_FILENO, &initial_settings);
current_tty = (ttyname(STDIN_FILENO) != nullptr ? static_cast<string>(ttyname(STDIN_FILENO)) : "unknown");
//? Disable stream sync
//? Disable stream sync - this does not seem to work on OpenBSD
#ifndef __OpenBSD__
cout.sync_with_stdio(false);
#endif
//? Disable stream ties
cout.tie(nullptr);
@ -536,6 +565,74 @@ namespace Tools {
return (user != nullptr ? user : "");
}
DebugTimer::DebugTimer(const string name, bool start, bool delayed_report) : name(name), delayed_report(delayed_report) {
if (start)
this->start();
}
DebugTimer::~DebugTimer() {
if (running)
this->stop(true);
this->force_report();
}
void DebugTimer::start() {
if (running) return;
running = true;
start_time = time_micros();
}
void DebugTimer::stop(bool report) {
if (not running) return;
running = false;
elapsed_time = time_micros() - start_time;
if (report) this->report();
}
void DebugTimer::reset(bool restart) {
running = false;
start_time = 0;
elapsed_time = 0;
if (restart) this->start();
}
void DebugTimer::stop_rename_reset(const string &new_name, bool report, bool restart) {
this->stop(report);
name = new_name;
this->reset(restart);
}
void DebugTimer::report() {
string report_line;
if (start_time == 0 and elapsed_time == 0)
report_line = fmt::format("DebugTimer::report() warning -> Timer [{}] has not been started!", name);
else if (running)
report_line = fmt::format(custom_locale, "Timer [{}] (running) currently at {:L} μs", name, time_micros() - start_time);
else
report_line = fmt::format(custom_locale, "Timer [{}] took {:L} μs", name, elapsed_time);
if (delayed_report)
report_buffer.emplace_back(report_line);
else
Logger::log_write(log_level, report_line);
}
void DebugTimer::force_report() {
if (report_buffer.empty()) return;
for (const auto& line : report_buffer)
Logger::log_write(log_level, line);
report_buffer.clear();
}
uint64_t DebugTimer::elapsed() {
if (running)
return time_micros() - start_time;
return elapsed_time;
}
bool DebugTimer::is_running() {
return running;
}
}
namespace Logger {
@ -567,7 +664,7 @@ namespace Logger {
loglevel = v_index(log_levels, level);
}
void log_write(const size_t level, const string& msg) {
void log_write(const Level level, const string& msg) {
if (loglevel < level or logfile.empty()) return;
atomic_lock lck(busy, true);
lose_priv neutered{};

View file

@ -31,6 +31,10 @@ tab-size = 4
#include <vector>
#include <pthread.h>
#include <limits.h>
#include <unordered_map>
#ifdef BTOP_DEBUG
#include <source_location>
#endif
#ifndef HOST_NAME_MAX
#ifdef __APPLE__
#define HOST_NAME_MAX 255
@ -146,11 +150,46 @@ namespace Term {
void restore();
}
//* Simple logging implementation
namespace Logger {
const vector<string> log_levels = {
"DISABLED",
"ERROR",
"WARNING",
"INFO",
"DEBUG",
};
extern std::filesystem::path logfile;
enum Level : size_t {
DISABLED = 0,
ERROR = 1,
WARNING = 2,
INFO = 3,
DEBUG = 4,
};
//* Set log level, valid arguments: "DISABLED", "ERROR", "WARNING", "INFO" and "DEBUG"
void set(const string& level);
void log_write(const Level level, const string& msg);
inline void error(const string msg) { log_write(ERROR, msg); }
inline void warning(const string msg) { log_write(WARNING, msg); }
inline void info(const string msg) { log_write(INFO, msg); }
inline void debug(const string msg) { log_write(DEBUG, msg); }
}
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
namespace Tools {
constexpr auto SSmax = std::numeric_limits<std::streamsize>::max();
class MyNumPunct : public std::numpunct<char> {
protected:
virtual char do_thousands_sep() const { return '\''; }
virtual std::string do_grouping() const { return "\03"; }
};
size_t wide_ulen(const string& str);
size_t wide_ulen(const std::wstring& w_str);
@ -298,6 +337,50 @@ namespace Tools {
//* Add std::string operator * : Repeat string <str> <n> number of times
std::string operator*(const string& str, int64_t n);
template <typename K, typename T>
#ifdef BTOP_DEBUG
const T& safeVal(const std::unordered_map<K, T>& map, const K& key, const T& fallback = T{}, std::source_location loc = std::source_location::current()) {
if (map.contains(key)) {
return map.at(key);
} else {
Logger::error(fmt::format("safeVal() called with invalid key: [{}] in file: {} on line: {}", key, loc.file_name(), loc.line()));
return fallback;
}
};
#else
const T& safeVal(const std::unordered_map<K, T>& map, const K& key, const T& fallback = T{}) {
if (map.contains(key)) {
return map.at(key);
} else {
Logger::error(fmt::format("safeVal() called with invalid key: [{}] (Compile btop with DEBUG=true for more extensive logging!)", key));
return fallback;
}
};
#endif
template <typename T>
#ifdef BTOP_DEBUG
const T& safeVal(const std::vector<T>& vec, const size_t& index, const T& fallback = T{}, std::source_location loc = std::source_location::current()) {
if (index < vec.size()) {
return vec.at(index);
} else {
Logger::error(fmt::format("safeVal() called with invalid index: [{}] in file: {} on line: {}", index, loc.file_name(), loc.line()));
return fallback;
}
};
#else
const T& safeVal(const std::vector<T>& vec, const size_t& index, const T& fallback = T{}) {
if (index < vec.size()) {
return vec.at(index);
} else {
Logger::error(fmt::format("safeVal() called with invalid index: [{}] (Compile btop with DEBUG=true for more extensive logging!)", index));
return fallback;
}
};
#endif
//* Return current time in <strf> format
string strf_time(const string& strf);
@ -334,27 +417,40 @@ namespace Tools {
//* Convert a celsius value to celsius, fahrenheit, kelvin or rankin and return tuple with new value and unit.
auto celsius_to(const long long& celsius, const string& scale) -> tuple<long long, string>;
}
//* Simple logging implementation
namespace Logger {
const vector<string> log_levels = {
"DISABLED",
"ERROR",
"WARNING",
"INFO",
"DEBUG",
namespace Tools {
//* Creates a named timer that is started on construct (by default) and reports elapsed time in microseconds to Logger::debug() on destruct if running
//* Unless delayed_report is set to false, all reporting is buffered and delayed until DebugTimer is destructed or .force_report() is called
//* Usage example: Tools::DebugTimer timer(name:"myTimer", [start:true], [delayed_report:true]) // Create timer and start
//* timer.stop(); // Stop timer and report elapsed time
//* timer.stop_rename_reset("myTimer2"); // Stop timer, report elapsed time, rename timer, reset and restart
class DebugTimer {
uint64_t start_time{};
uint64_t elapsed_time{};
bool running{};
std::locale custom_locale = std::locale(std::locale::classic(), new Tools::MyNumPunct);
vector<string> report_buffer{};
public:
string name{};
bool delayed_report{};
Logger::Level log_level = Logger::DEBUG;
DebugTimer() = default;
DebugTimer(const string name, bool start = true, bool delayed_report = true);
~DebugTimer();
void start();
void stop(bool report = true);
void reset(bool restart = true);
//* Stops and reports (default), renames timer then resets and restarts (default)
void stop_rename_reset(const string& new_name, bool report = true, bool restart = true);
void report();
void force_report();
uint64_t elapsed();
bool is_running();
};
extern std::filesystem::path logfile;
//* Set log level, valid arguments: "DISABLED", "ERROR", "WARNING", "INFO" and "DEBUG"
void set(const string& level);
void log_write(const size_t level, const string& msg);
inline void error(const string msg) { log_write(1, msg); }
inline void warning(const string msg) { log_write(2, msg); }
inline void info(const string msg) { log_write(3, msg); }
inline void debug(const string msg) { log_write(4, msg); }
}

View file

@ -58,6 +58,7 @@ tab-size = 4
#include <regex>
#include <string>
#include <memory>
#include <utility>
#include "../btop_config.hpp"
#include "../btop_shared.hpp"
@ -74,7 +75,7 @@ using namespace Tools;
namespace Cpu {
vector<long long> core_old_totals;
vector<long long> core_old_idles;
vector<string> available_fields = {"total"};
vector<string> available_fields = {"Auto", "total"};
vector<string> available_sensors = {"Auto"};
cpu_info current_cpu;
bool got_sensors = false, cpu_temp_only = false;
@ -98,7 +99,7 @@ namespace Cpu {
string cpu_sensor;
vector<string> core_sensors;
unordered_flat_map<int, int> core_mapping;
std::unordered_map<int, int> core_mapping;
} // namespace Cpu
namespace Mem {
@ -169,17 +170,23 @@ namespace Shared {
Cpu::current_cpu.temp.insert(Cpu::current_cpu.temp.begin(), Shared::coreCount + 1, {});
Cpu::core_old_totals.insert(Cpu::core_old_totals.begin(), Shared::coreCount, 0);
Cpu::core_old_idles.insert(Cpu::core_old_idles.begin(), Shared::coreCount, 0);
Logger::debug("Init -> Cpu::collect()");
Cpu::collect();
for (auto &[field, vec] : Cpu::current_cpu.cpu_percent) {
if (not vec.empty() and not v_contains(Cpu::available_fields, field)) Cpu::available_fields.push_back(field);
}
Logger::debug("Init -> Cpu::get_cpuName()");
Cpu::cpuName = Cpu::get_cpuName();
Logger::debug("Init -> Cpu::get_sensors()");
Cpu::got_sensors = Cpu::get_sensors();
Logger::debug("Init -> Cpu::get_core_mapping()");
Cpu::core_mapping = Cpu::get_core_mapping();
//? Init for namespace Mem
Mem::old_uptime = system_uptime();
Logger::debug("Init -> Mem::collect()");
Mem::collect();
Logger::debug("Init -> Mem::get_zpools()");
Mem::get_zpools();
}
@ -204,7 +211,7 @@ namespace Cpu {
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
unordered_flat_map<string, long long> cpu_old = {
std::unordered_map<string, long long> cpu_old = {
{"totals", 0},
{"idles", 0},
{"user", 0},
@ -323,8 +330,8 @@ namespace Cpu {
return std::to_string(freq / 1000.0 ).substr(0, 3); // seems to be in MHz
}
auto get_core_mapping() -> unordered_flat_map<int, int> {
unordered_flat_map<int, int> core_map;
auto get_core_mapping() -> std::unordered_map<int, int> {
std::unordered_map<int, int> core_map;
if (cpu_temp_only) return core_map;
for (long i = 0; i < Shared::coreCount; i++) {
@ -557,7 +564,7 @@ namespace Mem {
}
}
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
void collect_disk(std::unordered_map<string, disk_info> &disks, std::unordered_map<string, string> &mapping) {
// this bit is for 'regular' mounts
static struct statinfo cur;
long double etime = 0;
@ -572,7 +579,7 @@ namespace Mem {
auto d = cur.dinfo->devices[i];
string devStatName = "/dev/" + string(d.device_name) + std::to_string(d.unit_number);
for (auto& [ignored, disk] : disks) { // find matching mountpoints - could be multiple as d.device_name is only ada (and d.unit_number is the device number), while the disk.dev is like /dev/ada0s1
if (disk.dev.string().rfind(devStatName, 0) == 0) {
if (disk.dev.string().rfind(devStatName, 0) == 0 and mapping.contains(disk.dev)) {
devstat_compute_statistics(&d, nullptr, etime, DSM_TOTAL_BYTES_READ, &total_bytes_read, DSM_TOTAL_BYTES_WRITE, &total_bytes_write, DSM_NONE);
assign_values(disk, total_bytes_read, total_bytes_write);
string mountpoint = mapping.at(disk.dev);
@ -581,7 +588,6 @@ namespace Mem {
}
}
Logger::debug("");
}
// this code is for ZFS mounts
@ -691,7 +697,7 @@ namespace Mem {
}
if (show_disks) {
unordered_flat_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
std::unordered_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
double uptime = system_uptime();
auto &disks_filter = Config::getS("disks_filter");
bool filter_exclude = false;
@ -807,13 +813,13 @@ namespace Mem {
} // namespace Mem
namespace Net {
unordered_flat_map<string, net_info> current_net;
std::unordered_map<string, net_info> current_net;
net_info empty_net = {};
vector<string> interfaces;
string selected_iface;
int errors = 0;
unordered_flat_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
unordered_flat_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
std::unordered_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
std::unordered_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
bool rescale = true;
uint64_t timestamp = 0;
@ -892,7 +898,7 @@ namespace Net {
} //else, ignoring family==AF_LINK (see man 3 getifaddrs)
}
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
std::unordered_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
size_t len;
if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) {
@ -966,7 +972,6 @@ namespace Net {
else
it++;
}
net.compact();
}
timestamp = new_timestamp;
@ -1037,7 +1042,7 @@ namespace Net {
namespace Proc {
vector<proc_info> current_procs;
unordered_flat_map<string, string> uid_user;
std::unordered_map<string, string> uid_user;
string current_sort;
string current_filter;
bool current_rev = false;

View file

@ -17,7 +17,8 @@ tab-size = 4
*/
#include <cstdlib>
#include <robin_hood.h>
#include <unordered_map>
#include <unordered_set>
#include <fstream>
#include <ranges>
#include <cmath>
@ -29,6 +30,14 @@ tab-size = 4
#include <net/if.h>
#include <arpa/inet.h> // for inet_ntop()
#include <filesystem>
#include <future>
#include <dlfcn.h>
#include <unordered_map>
#include <utility>
#if defined(RSMI_STATIC)
#include <rocm_smi/rocm_smi.h>
#endif
#if !(defined(STATIC_BUILD) && defined(__GLIBC__))
#include <pwd.h>
@ -48,18 +57,23 @@ using std::numeric_limits;
using std::round;
using std::streamsize;
using std::vector;
using std::future;
using std::async;
using std::pair;
namespace fs = std::filesystem;
namespace rng = std::ranges;
using namespace Tools;
using namespace std::literals; // for operator""s
using namespace std::chrono_literals;
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
namespace Cpu {
vector<long long> core_old_totals;
vector<long long> core_old_idles;
vector<string> available_fields;
vector<string> available_fields = {"Auto", "total"};
vector<string> available_sensors = {"Auto"};
cpu_info current_cpu;
fs::path freq_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq";
@ -83,10 +97,110 @@ namespace Cpu {
int64_t crit{}; // defaults to 0
};
unordered_flat_map<string, Sensor> found_sensors;
std::unordered_map<string, Sensor> found_sensors;
string cpu_sensor;
vector<string> core_sensors;
unordered_flat_map<int, int> core_mapping;
std::unordered_map<int, int> core_mapping;
}
namespace Gpu {
vector<gpu_info> gpus;
#ifdef GPU_SUPPORT
//? NVIDIA data collection
namespace Nvml {
//? NVML defines, structs & typedefs
#define NVML_DEVICE_NAME_BUFFER_SIZE 64
#define NVML_SUCCESS 0
#define NVML_TEMPERATURE_THRESHOLD_SHUTDOWN 0
#define NVML_CLOCK_GRAPHICS 0
#define NVML_CLOCK_MEM 2
#define NVML_TEMPERATURE_GPU 0
#define NVML_PCIE_UTIL_TX_BYTES 0
#define NVML_PCIE_UTIL_RX_BYTES 1
typedef void* nvmlDevice_t; // we won't be accessing any of the underlying struct's properties, so this is fine
typedef int nvmlReturn_t, // enums are basically ints
nvmlTemperatureThresholds_t,
nvmlClockType_t,
nvmlPstates_t,
nvmlTemperatureSensors_t,
nvmlPcieUtilCounter_t;
struct nvmlUtilization_t {unsigned int gpu, memory;};
struct nvmlMemory_t {unsigned long long total, free, used;};
//? Function pointers
const char* (*nvmlErrorString)(nvmlReturn_t);
nvmlReturn_t (*nvmlInit)();
nvmlReturn_t (*nvmlShutdown)();
nvmlReturn_t (*nvmlDeviceGetCount)(unsigned int*);
nvmlReturn_t (*nvmlDeviceGetHandleByIndex)(unsigned int, nvmlDevice_t*);
nvmlReturn_t (*nvmlDeviceGetName)(nvmlDevice_t, char*, unsigned int);
nvmlReturn_t (*nvmlDeviceGetPowerManagementLimit)(nvmlDevice_t, unsigned int*);
nvmlReturn_t (*nvmlDeviceGetTemperatureThreshold)(nvmlDevice_t, nvmlTemperatureThresholds_t, unsigned int*);
nvmlReturn_t (*nvmlDeviceGetUtilizationRates)(nvmlDevice_t, nvmlUtilization_t*);
nvmlReturn_t (*nvmlDeviceGetClockInfo)(nvmlDevice_t, nvmlClockType_t, unsigned int*);
nvmlReturn_t (*nvmlDeviceGetPowerUsage)(nvmlDevice_t, unsigned int*);
nvmlReturn_t (*nvmlDeviceGetPowerState)(nvmlDevice_t, nvmlPstates_t*);
nvmlReturn_t (*nvmlDeviceGetTemperature)(nvmlDevice_t, nvmlTemperatureSensors_t, unsigned int*);
nvmlReturn_t (*nvmlDeviceGetMemoryInfo)(nvmlDevice_t, nvmlMemory_t*);
nvmlReturn_t (*nvmlDeviceGetPcieThroughput)(nvmlDevice_t, nvmlPcieUtilCounter_t, unsigned int*);
//? Data
void* nvml_dl_handle;
bool initialized = false;
bool init();
bool shutdown();
template <bool is_init> bool collect(gpu_info* gpus_slice);
vector<nvmlDevice_t> devices;
unsigned int device_count = 0;
}
//? AMD data collection
namespace Rsmi {
#if !defined(RSMI_STATIC)
//? RSMI defines, structs & typedefs
#define RSMI_MAX_NUM_FREQUENCIES 32
#define RSMI_STATUS_SUCCESS 0
#define RSMI_MEM_TYPE_VRAM 0
#define RSMI_TEMP_CURRENT 0
#define RSMI_TEMP_TYPE_EDGE 0
#define RSMI_CLK_TYPE_MEM 4
#define RSMI_CLK_TYPE_SYS 0
#define RSMI_TEMP_MAX 1
typedef int rsmi_status_t,
rsmi_temperature_metric_t,
rsmi_clk_type_t,
rsmi_memory_type_t;
struct rsmi_frequencies_t {uint32_t num_supported, current, frequency[RSMI_MAX_NUM_FREQUENCIES];};
//? Function pointers
rsmi_status_t (*rsmi_init)(uint64_t);
rsmi_status_t (*rsmi_shut_down)();
rsmi_status_t (*rsmi_num_monitor_devices)(uint32_t*);
rsmi_status_t (*rsmi_dev_name_get)(uint32_t, char*, size_t);
rsmi_status_t (*rsmi_dev_power_cap_get)(uint32_t, uint32_t, uint64_t*);
rsmi_status_t (*rsmi_dev_temp_metric_get)(uint32_t, uint32_t, rsmi_temperature_metric_t, int64_t*);
rsmi_status_t (*rsmi_dev_busy_percent_get)(uint32_t, uint32_t*);
rsmi_status_t (*rsmi_dev_memory_busy_percent_get)(uint32_t, uint32_t*);
rsmi_status_t (*rsmi_dev_gpu_clk_freq_get)(uint32_t, rsmi_clk_type_t, rsmi_frequencies_t*);
rsmi_status_t (*rsmi_dev_power_ave_get)(uint32_t, uint32_t, uint64_t*);
rsmi_status_t (*rsmi_dev_memory_total_get)(uint32_t, rsmi_memory_type_t, uint64_t*);
rsmi_status_t (*rsmi_dev_memory_usage_get)(uint32_t, rsmi_memory_type_t, uint64_t*);
rsmi_status_t (*rsmi_dev_pci_throughput_get)(uint32_t, uint64_t*, uint64_t*, uint64_t*);
//? Data
void* rsmi_dl_handle;
#endif
bool initialized = false;
bool init();
bool shutdown();
template <bool is_init> bool collect(gpu_info* gpus_slice);
uint32_t device_count = 0;
}
#endif
}
namespace Mem {
@ -139,7 +253,7 @@ namespace Shared {
Cpu::collect();
if (Runner::coreNum_reset) Runner::coreNum_reset = false;
for (auto& [field, vec] : Cpu::current_cpu.cpu_percent) {
if (not vec.empty()) Cpu::available_fields.push_back(field);
if (not vec.empty() and not v_contains(Cpu::available_fields, field)) Cpu::available_fields.push_back(field);
}
Cpu::cpuName = Cpu::get_cpuName();
Cpu::got_sensors = Cpu::get_sensors();
@ -148,12 +262,32 @@ namespace Shared {
}
Cpu::core_mapping = Cpu::get_core_mapping();
//? Init for namespace Gpu
#ifdef GPU_SUPPORT
Gpu::Nvml::init();
Gpu::Rsmi::init();
if (not Gpu::gpu_names.empty()) {
for (auto const& [key, _] : Gpu::gpus[0].gpu_percent)
Cpu::available_fields.push_back(key);
for (auto const& [key, _] : Gpu::shared_gpu_percent)
Cpu::available_fields.push_back(key);
using namespace Gpu;
gpu_b_height_offsets.resize(gpus.size());
for (size_t i = 0; i < gpu_b_height_offsets.size(); ++i)
gpu_b_height_offsets[i] = gpus[i].supported_functions.gpu_utilization
+ gpus[i].supported_functions.pwr_usage
+ (gpus[i].supported_functions.mem_total or gpus[i].supported_functions.mem_used)
* (1 + 2*(gpus[i].supported_functions.mem_total and gpus[i].supported_functions.mem_used) + 2*gpus[i].supported_functions.mem_utilization);
}
#endif
//? Init for namespace Mem
Mem::old_uptime = system_uptime();
Mem::collect();
Logger::debug("Shared::init() : Initialized.");
}
}
namespace Cpu {
@ -167,7 +301,7 @@ namespace Cpu {
"irq"s, "softirq"s, "steal"s, "guest"s, "guest_nice"s
};
unordered_flat_map<string, long long> cpu_old = {
std::unordered_map<string, long long> cpu_old = {
{"totals", 0},
{"idles", 0},
{"user", 0},
@ -208,7 +342,7 @@ namespace Cpu {
}
auto name_vec = ssplit(name);
auto name_vec = ssplit(name, ' ');
if ((s_contains(name, "Xeon"s) or v_contains(name_vec, "Duo"s)) and v_contains(name_vec, "CPU"s)) {
auto cpu_pos = v_index(name_vec, "CPU"s);
@ -224,7 +358,7 @@ namespace Cpu {
}
else if (s_contains(name, "Intel"s) and v_contains(name_vec, "CPU"s)) {
auto cpu_pos = v_index(name_vec, "CPU"s);
if (cpu_pos < name_vec.size() - 1 and not name_vec.at(cpu_pos + 1).ends_with(')') and name_vec.at(cpu_pos + 1) != "@")
if (cpu_pos < name_vec.size() - 1 and not name_vec.at(cpu_pos + 1).ends_with(')') and name_vec.at(cpu_pos + 1).size() != 1)
name = name_vec.at(cpu_pos + 1);
else
name.clear();
@ -464,8 +598,8 @@ namespace Cpu {
return cpuhz;
}
auto get_core_mapping() -> unordered_flat_map<int, int> {
unordered_flat_map<int, int> core_map;
auto get_core_mapping() -> std::unordered_map<int, int> {
std::unordered_map<int, int> core_map;
if (cpu_temp_only) return core_map;
//? Try to get core mapping from /proc/cpuinfo
@ -538,7 +672,7 @@ namespace Cpu {
auto get_battery() -> tuple<int, long, string> {
if (not has_battery) return {0, 0, ""};
static string auto_sel;
static unordered_flat_map<string, battery> batteries;
static std::unordered_map<string, battery> batteries;
//? Get paths to needed files and check for valid values on first run
if (batteries.empty() and has_battery) {
@ -807,6 +941,572 @@ namespace Cpu {
}
}
#ifdef GPU_SUPPORT
namespace Gpu {
//? NVIDIA
namespace Nvml {
bool init() {
if (initialized) return false;
//? Dynamic loading & linking
//? Try possible library names for libnvidia-ml.so
const array libNvAlts = {
"libnvidia-ml.so",
"libnvidia-ml.so.1",
};
for (const auto& l : libNvAlts) {
nvml_dl_handle = dlopen(l, RTLD_LAZY);
if (nvml_dl_handle != nullptr) {
break;
}
}
if (!nvml_dl_handle) {
Logger::info("Failed to load libnvidia-ml.so, NVIDIA GPUs will not be detected: "s + dlerror());
return false;
}
auto load_nvml_sym = [&](const char sym_name[]) {
auto sym = dlsym(nvml_dl_handle, sym_name);
auto err = dlerror();
if (err != nullptr) {
Logger::error(string("NVML: Couldn't find function ") + sym_name + ": " + err);
return (void*)nullptr;
} else return sym;
};
#define LOAD_SYM(NAME) if ((NAME = (decltype(NAME))load_nvml_sym(#NAME)) == nullptr) return false
LOAD_SYM(nvmlErrorString);
LOAD_SYM(nvmlInit);
LOAD_SYM(nvmlShutdown);
LOAD_SYM(nvmlDeviceGetCount);
LOAD_SYM(nvmlDeviceGetHandleByIndex);
LOAD_SYM(nvmlDeviceGetName);
LOAD_SYM(nvmlDeviceGetPowerManagementLimit);
LOAD_SYM(nvmlDeviceGetTemperatureThreshold);
LOAD_SYM(nvmlDeviceGetUtilizationRates);
LOAD_SYM(nvmlDeviceGetClockInfo);
LOAD_SYM(nvmlDeviceGetPowerUsage);
LOAD_SYM(nvmlDeviceGetPowerState);
LOAD_SYM(nvmlDeviceGetTemperature);
LOAD_SYM(nvmlDeviceGetMemoryInfo);
LOAD_SYM(nvmlDeviceGetPcieThroughput);
#undef LOAD_SYM
//? Function calls
nvmlReturn_t result = nvmlInit();
if (result != NVML_SUCCESS) {
Logger::debug(std::string("Failed to initialize NVML, NVIDIA GPUs will not be detected: ") + nvmlErrorString(result));
return false;
}
//? Device count
result = nvmlDeviceGetCount(&device_count);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get device count: ") + nvmlErrorString(result));
return false;
}
if (device_count > 0) {
devices.resize(device_count);
gpus.resize(device_count);
gpu_names.resize(device_count);
initialized = true;
//? Check supported functions & get maximums
Nvml::collect<1>(gpus.data());
return true;
} else {initialized = true; shutdown(); return false;}
}
bool shutdown() {
if (!initialized) return false;
nvmlReturn_t result = nvmlShutdown();
if (NVML_SUCCESS == result) {
initialized = false;
dlclose(nvml_dl_handle);
} else Logger::warning(std::string("Failed to shutdown NVML: ") + nvmlErrorString(result));
return !initialized;
}
template <bool is_init> // collect<1> is called in Nvml::init(), and populates gpus.supported_functions
bool collect(gpu_info* gpus_slice) { // raw pointer to vector data, size == device_count
if (!initialized) return false;
nvmlReturn_t result;
std::thread pcie_tx_thread, pcie_rx_thread;
// DebugTimer nvTotalTimer("Nvidia Total");
for (unsigned int i = 0; i < device_count; ++i) {
if constexpr(is_init) {
//? Device Handle
result = nvmlDeviceGetHandleByIndex(i, devices.data() + i);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get device handle: ") + nvmlErrorString(result));
gpus[i].supported_functions = {false, false, false, false, false, false, false, false};
continue;
}
//? Device name
char name[NVML_DEVICE_NAME_BUFFER_SIZE];
result = nvmlDeviceGetName(devices[i], name, NVML_DEVICE_NAME_BUFFER_SIZE);
if (result != NVML_SUCCESS)
Logger::warning(std::string("NVML: Failed to get device name: ") + nvmlErrorString(result));
else {
gpu_names[i] = string(name);
for (const auto& brand : {"NVIDIA", "Nvidia", "(R)", "(TM)"}) {
gpu_names[i] = s_replace(gpu_names[i], brand, "");
}
gpu_names[i] = trim(gpu_names[i]);
}
//? Power usage
unsigned int max_power;
result = nvmlDeviceGetPowerManagementLimit(devices[i], &max_power);
if (result != NVML_SUCCESS)
Logger::warning(std::string("NVML: Failed to get maximum GPU power draw, defaulting to 225W: ") + nvmlErrorString(result));
else {
gpus[i].pwr_max_usage = max_power; // RSMI reports power in microWatts
gpu_pwr_total_max += max_power;
}
//? Get temp_max
unsigned int temp_max;
result = nvmlDeviceGetTemperatureThreshold(devices[i], NVML_TEMPERATURE_THRESHOLD_SHUTDOWN, &temp_max);
if (result != NVML_SUCCESS)
Logger::warning(std::string("NVML: Failed to get maximum GPU temperature, defaulting to 110°C: ") + nvmlErrorString(result));
else gpus[i].temp_max = (long long)temp_max;
}
//? PCIe link speeds, the data collection takes >=20ms each call so they run on separate threads
if (gpus_slice[i].supported_functions.pcie_txrx and (Config::getB("nvml_measure_pcie_speeds") or is_init)) {
pcie_tx_thread = std::thread([gpus_slice, i]() {
unsigned int tx;
nvmlReturn_t result = nvmlDeviceGetPcieThroughput(devices[i], NVML_PCIE_UTIL_TX_BYTES, &tx);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get PCIe TX throughput: ") + nvmlErrorString(result));
if constexpr(is_init) gpus_slice[i].supported_functions.pcie_txrx = false;
} else gpus_slice[i].pcie_tx = (long long)tx;
});
pcie_rx_thread = std::thread([gpus_slice, i]() {
unsigned int rx;
nvmlReturn_t result = nvmlDeviceGetPcieThroughput(devices[i], NVML_PCIE_UTIL_RX_BYTES, &rx);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get PCIe RX throughput: ") + nvmlErrorString(result));
} else gpus_slice[i].pcie_rx = (long long)rx;
});
}
// DebugTimer nvTimer("Nv utilization");
//? GPU & memory utilization
if (gpus_slice[i].supported_functions.gpu_utilization) {
nvmlUtilization_t utilization;
result = nvmlDeviceGetUtilizationRates(devices[i], &utilization);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get GPU utilization: ") + nvmlErrorString(result));
if constexpr(is_init) gpus_slice[i].supported_functions.gpu_utilization = false;
if constexpr(is_init) gpus_slice[i].supported_functions.mem_utilization = false;
} else {
gpus_slice[i].gpu_percent.at("gpu-totals").push_back((long long)utilization.gpu);
gpus_slice[i].mem_utilization_percent.push_back((long long)utilization.memory);
}
}
// nvTimer.stop_rename_reset("Nv clock");
//? Clock speeds
if (gpus_slice[i].supported_functions.gpu_clock) {
unsigned int gpu_clock;
result = nvmlDeviceGetClockInfo(devices[i], NVML_CLOCK_GRAPHICS, &gpu_clock);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get GPU clock speed: ") + nvmlErrorString(result));
if constexpr(is_init) gpus_slice[i].supported_functions.gpu_clock = false;
} else gpus_slice[i].gpu_clock_speed = (long long)gpu_clock;
}
if (gpus_slice[i].supported_functions.mem_clock) {
unsigned int mem_clock;
result = nvmlDeviceGetClockInfo(devices[i], NVML_CLOCK_MEM, &mem_clock);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get VRAM clock speed: ") + nvmlErrorString(result));
if constexpr(is_init) gpus_slice[i].supported_functions.mem_clock = false;
} else gpus_slice[i].mem_clock_speed = (long long)mem_clock;
}
// nvTimer.stop_rename_reset("Nv power");
//? Power usage & state
if (gpus_slice[i].supported_functions.pwr_usage) {
unsigned int power;
result = nvmlDeviceGetPowerUsage(devices[i], &power);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get GPU power usage: ") + nvmlErrorString(result));
if constexpr(is_init) gpus_slice[i].supported_functions.pwr_usage = false;
} else {
gpus_slice[i].pwr_usage = (long long)power;
gpus_slice[i].gpu_percent.at("gpu-pwr-totals").push_back(clamp((long long)round((double)gpus_slice[i].pwr_usage * 100.0 / (double)gpus_slice[i].pwr_max_usage), 0ll, 100ll));
}
}
if (gpus_slice[i].supported_functions.pwr_state) {
nvmlPstates_t pState;
result = nvmlDeviceGetPowerState(devices[i], &pState);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get GPU power state: ") + nvmlErrorString(result));
if constexpr(is_init) gpus_slice[i].supported_functions.pwr_state = false;
} else gpus_slice[i].pwr_state = static_cast<int>(pState);
}
// nvTimer.stop_rename_reset("Nv temp");
//? GPU temperature
if (gpus_slice[i].supported_functions.temp_info) {
if (Config::getB("check_temp")) {
unsigned int temp;
nvmlReturn_t result = nvmlDeviceGetTemperature(devices[i], NVML_TEMPERATURE_GPU, &temp);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get GPU temperature: ") + nvmlErrorString(result));
if constexpr(is_init) gpus_slice[i].supported_functions.temp_info = false;
} else gpus_slice[i].temp.push_back((long long)temp);
}
}
// nvTimer.stop_rename_reset("Nv mem");
//? Memory info
if (gpus_slice[i].supported_functions.mem_total) {
nvmlMemory_t memory;
result = nvmlDeviceGetMemoryInfo(devices[i], &memory);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get VRAM info: ") + nvmlErrorString(result));
if constexpr(is_init) gpus_slice[i].supported_functions.mem_total = false;
if constexpr(is_init) gpus_slice[i].supported_functions.mem_used = false;
} else {
gpus_slice[i].mem_total = memory.total;
gpus_slice[i].mem_used = memory.used;
//gpu.mem_free = memory.free;
auto used_percent = (long long)round((double)memory.used * 100.0 / (double)memory.total);
gpus_slice[i].gpu_percent.at("gpu-vram-totals").push_back(used_percent);
}
}
//? TODO: Processes using GPU
/*unsigned int proc_info_len;
nvmlProcessInfo_t* proc_info = 0;
result = nvmlDeviceGetComputeRunningProcesses_v3(device, &proc_info_len, proc_info);
if (result != NVML_SUCCESS) {
Logger::warning(std::string("NVML: Failed to get compute processes: ") + nvmlErrorString(result));
} else {
for (unsigned int i = 0; i < proc_info_len; ++i)
gpus_slice[i].graphics_processes.push_back({proc_info[i].pid, proc_info[i].usedGpuMemory});
}*/
// nvTimer.stop_rename_reset("Nv pcie thread join");
//? Join PCIE TX/RX threads
if constexpr(is_init) { // there doesn't seem to be a better way to do this, but this should be fine considering it's just 2 lines
pcie_tx_thread.join();
pcie_rx_thread.join();
} else if (gpus_slice[i].supported_functions.pcie_txrx and Config::getB("nvml_measure_pcie_speeds")) {
pcie_tx_thread.join();
pcie_rx_thread.join();
}
}
return true;
}
}
//? AMD
namespace Rsmi {
bool init() {
if (initialized) return false;
//? Dynamic loading & linking
#if !defined(RSMI_STATIC)
//? Try possible library paths and names for librocm_smi64.so
const array libRocAlts = {
"/opt/rocm/lib/librocm_smi64.so",
"librocm_smi64.so",
"librocm_smi64.so.5", // fedora
"librocm_smi64.so.1.0", // debian
};
for (const auto& l : libRocAlts) {
rsmi_dl_handle = dlopen(l, RTLD_LAZY);
if (rsmi_dl_handle != nullptr) {
break;
}
}
if (!rsmi_dl_handle) {
Logger::info("Failed to load librocm_smi64.so, AMD GPUs will not be detected: "s + dlerror());
return false;
}
auto load_rsmi_sym = [&](const char sym_name[]) {
auto sym = dlsym(rsmi_dl_handle, sym_name);
auto err = dlerror();
if (err != nullptr) {
Logger::error(string("ROCm SMI: Couldn't find function ") + sym_name + ": " + err);
return (void*)nullptr;
} else return sym;
};
#define LOAD_SYM(NAME) if ((NAME = (decltype(NAME))load_rsmi_sym(#NAME)) == nullptr) return false
LOAD_SYM(rsmi_init);
LOAD_SYM(rsmi_shut_down);
LOAD_SYM(rsmi_num_monitor_devices);
LOAD_SYM(rsmi_dev_name_get);
LOAD_SYM(rsmi_dev_power_cap_get);
LOAD_SYM(rsmi_dev_temp_metric_get);
LOAD_SYM(rsmi_dev_busy_percent_get);
LOAD_SYM(rsmi_dev_memory_busy_percent_get);
LOAD_SYM(rsmi_dev_gpu_clk_freq_get);
LOAD_SYM(rsmi_dev_power_ave_get);
LOAD_SYM(rsmi_dev_memory_total_get);
LOAD_SYM(rsmi_dev_memory_usage_get);
LOAD_SYM(rsmi_dev_pci_throughput_get);
#undef LOAD_SYM
#endif
//? Function calls
rsmi_status_t result = rsmi_init(0);
if (result != RSMI_STATUS_SUCCESS) {
Logger::debug("Failed to initialize ROCm SMI, AMD GPUs will not be detected");
return false;
}
//? Device count
result = rsmi_num_monitor_devices(&device_count);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to fetch number of devices");
return false;
}
if (device_count > 0) {
gpus.resize(gpus.size() + device_count);
gpu_names.resize(gpus.size() + device_count);
initialized = true;
//? Check supported functions & get maximums
Rsmi::collect<1>(gpus.data() + Nvml::device_count);
return true;
} else {initialized = true; shutdown(); return false;}
}
bool shutdown() {
if (!initialized) return false;
if (rsmi_shut_down() == RSMI_STATUS_SUCCESS) {
initialized = false;
#if !defined(RSMI_STATIC)
dlclose(rsmi_dl_handle);
#endif
} else Logger::warning("Failed to shutdown ROCm SMI");
return true;
}
template <bool is_init>
bool collect(gpu_info* gpus_slice) { // raw pointer to vector data, size == device_count, offset by Nvml::device_count elements
if (!initialized) return false;
rsmi_status_t result;
for (uint32_t i = 0; i < device_count; ++i) {
if constexpr(is_init) {
//? Device name
char name[NVML_DEVICE_NAME_BUFFER_SIZE]; // ROCm SMI does not provide a constant for this as far as I can tell, this should be good enough
result = rsmi_dev_name_get(i, name, NVML_DEVICE_NAME_BUFFER_SIZE);
if (result != RSMI_STATUS_SUCCESS)
Logger::warning("ROCm SMI: Failed to get device name");
else gpu_names[Nvml::device_count + i] = string(name);
//? Power usage
uint64_t max_power;
result = rsmi_dev_power_cap_get(i, 0, &max_power);
if (result != RSMI_STATUS_SUCCESS)
Logger::warning("ROCm SMI: Failed to get maximum GPU power draw, defaulting to 225W");
else {
gpus_slice[i].pwr_max_usage = (long long)(max_power/1000); // RSMI reports power in microWatts
gpu_pwr_total_max += gpus_slice[i].pwr_max_usage;
}
//? Get temp_max
int64_t temp_max;
result = rsmi_dev_temp_metric_get(i, RSMI_TEMP_TYPE_EDGE, RSMI_TEMP_MAX, &temp_max);
if (result != RSMI_STATUS_SUCCESS)
Logger::warning("ROCm SMI: Failed to get maximum GPU temperature, defaulting to 110°C");
else gpus_slice[i].temp_max = (long long)temp_max;
}
//? GPU utilization
if (gpus_slice[i].supported_functions.gpu_utilization) {
uint32_t utilization;
result = rsmi_dev_busy_percent_get(i, &utilization);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get GPU utilization");
if constexpr(is_init) gpus_slice[i].supported_functions.gpu_utilization = false;
} else gpus_slice[i].gpu_percent.at("gpu-totals").push_back((long long)utilization);
}
//? Memory utilization
if (gpus_slice[i].supported_functions.mem_utilization) {
uint32_t utilization;
result = rsmi_dev_memory_busy_percent_get(i, &utilization);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get VRAM utilization");
if constexpr(is_init) gpus_slice[i].supported_functions.mem_utilization = false;
} else gpus_slice[i].mem_utilization_percent.push_back((long long)utilization);
}
//? Clock speeds
if (gpus_slice[i].supported_functions.gpu_clock) {
rsmi_frequencies_t frequencies;
result = rsmi_dev_gpu_clk_freq_get(i, RSMI_CLK_TYPE_SYS, &frequencies);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get GPU clock speed: ");
if constexpr(is_init) gpus_slice[i].supported_functions.gpu_clock = false;
} else gpus_slice[i].gpu_clock_speed = (long long)frequencies.frequency[frequencies.current]/1000000; // Hz to MHz
}
if (gpus_slice[i].supported_functions.mem_clock) {
rsmi_frequencies_t frequencies;
result = rsmi_dev_gpu_clk_freq_get(i, RSMI_CLK_TYPE_MEM, &frequencies);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get VRAM clock speed: ");
if constexpr(is_init) gpus_slice[i].supported_functions.mem_clock = false;
} else gpus_slice[i].mem_clock_speed = (long long)frequencies.frequency[frequencies.current]/1000000; // Hz to MHz
}
//? Power usage & state
if (gpus_slice[i].supported_functions.pwr_usage) {
uint64_t power;
result = rsmi_dev_power_ave_get(i, 0, &power);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get GPU power usage");
if constexpr(is_init) gpus_slice[i].supported_functions.pwr_usage = false;
} else gpus_slice[i].gpu_percent.at("gpu-pwr-totals").push_back(clamp((long long)round((double)gpus_slice[i].pwr_usage * 100.0 / (double)gpus_slice[i].pwr_max_usage), 0ll, 100ll));
if constexpr(is_init) gpus_slice[i].supported_functions.pwr_state = false;
}
//? GPU temperature
if (gpus_slice[i].supported_functions.temp_info) {
if (Config::getB("check_temp") or is_init) {
int64_t temp;
result = rsmi_dev_temp_metric_get(i, RSMI_TEMP_TYPE_EDGE, RSMI_TEMP_CURRENT, &temp);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get GPU temperature");
if constexpr(is_init) gpus_slice[i].supported_functions.temp_info = false;
} else gpus_slice[i].temp.push_back((long long)temp/1000);
}
}
//? Memory info
if (gpus_slice[i].supported_functions.mem_total) {
uint64_t total;
result = rsmi_dev_memory_total_get(i, RSMI_MEM_TYPE_VRAM, &total);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get total VRAM");
if constexpr(is_init) gpus_slice[i].supported_functions.mem_total = false;
} else gpus_slice[i].mem_total = total;
}
if (gpus_slice[i].supported_functions.mem_used) {
uint64_t used;
result = rsmi_dev_memory_usage_get(i, RSMI_MEM_TYPE_VRAM, &used);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get VRAM usage");
if constexpr(is_init) gpus_slice[i].supported_functions.mem_used = false;
} else {
gpus_slice[i].mem_used = used;
if (gpus_slice[i].supported_functions.mem_total)
gpus_slice[i].gpu_percent.at("gpu-vram-totals").push_back((long long)round((double)used * 100.0 / (double)gpus_slice[i].mem_total));
}
}
//? PCIe link speeds
if (gpus_slice[i].supported_functions.pcie_txrx) {
uint64_t tx, rx;
result = rsmi_dev_pci_throughput_get(i, &tx, &rx, 0);
if (result != RSMI_STATUS_SUCCESS) {
Logger::warning("ROCm SMI: Failed to get PCIe throughput");
if constexpr(is_init) gpus_slice[i].supported_functions.pcie_txrx = false;
} else {
gpus_slice[i].pcie_tx = (long long)tx;
gpus_slice[i].pcie_rx = (long long)rx;
}
}
}
return true;
}
}
// TODO: Intel
//? Collect data from GPU-specific libraries
auto collect(bool no_update) -> vector<gpu_info>& {
if (Runner::stopping or (no_update and not gpus.empty())) return gpus;
// DebugTimer gpu_timer("GPU Total");
//* Collect data
Nvml::collect<0>(gpus.data()); // raw pointer to vector data, size == Nvml::device_count
Rsmi::collect<0>(gpus.data() + Nvml::device_count); // size = Rsmi::device_count
//* Calculate average usage
long long avg = 0;
long long mem_usage_total = 0;
long long mem_total = 0;
long long pwr_total = 0;
for (auto& gpu : gpus) {
if (gpu.supported_functions.gpu_utilization)
avg += gpu.gpu_percent.at("gpu-totals").back();
if (gpu.supported_functions.mem_used)
mem_usage_total += gpu.mem_used;
if (gpu.supported_functions.mem_total)
mem_total += gpu.mem_total;
if (gpu.supported_functions.pwr_usage)
mem_total += gpu.pwr_usage;
//* Trim vectors if there are more values than needed for graphs
if (width != 0) {
//? GPU & memory utilization
while (cmp_greater(gpu.gpu_percent.at("gpu-totals").size(), width * 2)) gpu.gpu_percent.at("gpu-totals").pop_front();
while (cmp_greater(gpu.mem_utilization_percent.size(), width)) gpu.mem_utilization_percent.pop_front();
//? Power usage
while (cmp_greater(gpu.gpu_percent.at("gpu-pwr-totals").size(), width)) gpu.gpu_percent.at("gpu-pwr-totals").pop_front();
//? Temperature
while (cmp_greater(gpu.temp.size(), 18)) gpu.temp.pop_front();
//? Memory usage
while (cmp_greater(gpu.gpu_percent.at("gpu-vram-totals").size(), width/2)) gpu.gpu_percent.at("gpu-vram-totals").pop_front();
}
}
shared_gpu_percent.at("gpu-average").push_back(avg / gpus.size());
if (mem_total != 0)
shared_gpu_percent.at("gpu-vram-total").push_back(mem_usage_total / mem_total);
if (gpu_pwr_total_max != 0)
shared_gpu_percent.at("gpu-pwr-total").push_back(pwr_total / gpu_pwr_total_max);
if (width != 0) {
while (cmp_greater(shared_gpu_percent.at("gpu-average").size(), width * 2)) shared_gpu_percent.at("gpu-average").pop_front();
while (cmp_greater(shared_gpu_percent.at("gpu-pwr-total").size(), width * 2)) shared_gpu_percent.at("gpu-pwr-total").pop_front();
while (cmp_greater(shared_gpu_percent.at("gpu-vram-total").size(), width * 2)) shared_gpu_percent.at("gpu-vram-total").pop_front();
}
return gpus;
}
}
#endif
namespace Mem {
bool has_swap{}; // defaults to false
vector<string> fstab;
@ -939,6 +1639,7 @@ namespace Mem {
auto only_physical = Config::getB("only_physical");
auto zfs_hide_datasets = Config::getB("zfs_hide_datasets");
auto& disks = mem.disks;
static std::unordered_map<string, future<pair<disk_info, int>>> disks_stats_promises;
ifstream diskread;
vector<string> filter;
@ -1078,31 +1779,47 @@ namespace Mem {
diskread.close();
//? Get disk/partition stats
bool new_ignored = false;
for (auto& [mountpoint, disk] : disks) {
if (std::error_code ec; not fs::exists(mountpoint, ec) or v_contains(ignore_list, mountpoint)) continue;
struct statvfs vfs;
if (statvfs(mountpoint.c_str(), &vfs) < 0) {
Logger::warning("Failed to get disk/partition stats for mount \""+ mountpoint + "\" with statvfs error code: " + to_string(errno) + ". Ignoring...");
ignore_list.push_back(mountpoint);
new_ignored = true;
for (auto it = disks.begin(); it != disks.end(); ) {
auto &[mountpoint, disk] = *it;
if (v_contains(ignore_list, mountpoint) or disk.name == "swap") {
it = disks.erase(it);
continue;
}
disk.total = vfs.f_blocks * vfs.f_frsize;
disk.free = (free_priv ? vfs.f_bfree : vfs.f_bavail) * vfs.f_frsize;
disk.used = disk.total - disk.free;
disk.used_percent = round((double)disk.used * 100 / disk.total);
disk.free_percent = 100 - disk.used_percent;
}
//? Remove any problematic disks added to the ignore_list
if (new_ignored) {
for (auto it = disks.begin(); it != disks.end();) {
if (v_contains(ignore_list, it->first))
if(auto promises_it = disks_stats_promises.find(mountpoint); promises_it != disks_stats_promises.end()){
auto& promise = promises_it->second;
if(promise.valid() &&
promise.wait_for(0s) == std::future_status::timeout) {
++it;
continue;
}
auto promise_res = promises_it->second.get();
if(promise_res.second != -1){
ignore_list.push_back(mountpoint);
Logger::warning("Failed to get disk/partition stats for mount \""+ mountpoint + "\" with statvfs error code: " + to_string(promise_res.second) + ". Ignoring...");
it = disks.erase(it);
else
it++;
continue;
}
auto &updated_stats = promise_res.first;
disk.total = updated_stats.total;
disk.free = updated_stats.free;
disk.used = updated_stats.used;
disk.used_percent = updated_stats.used_percent;
disk.free_percent = updated_stats.free_percent;
}
disks_stats_promises[mountpoint] = async(std::launch::async, [mountpoint, &free_priv]() -> pair<disk_info, int> {
struct statvfs vfs;
disk_info disk;
if (statvfs(mountpoint.c_str(), &vfs) < 0) {
return pair{disk, errno};
}
disk.total = vfs.f_blocks * vfs.f_frsize;
disk.free = (free_priv ? vfs.f_bfree : vfs.f_bavail) * vfs.f_frsize;
disk.used = disk.total - disk.free;
disk.used_percent = round((double)disk.used * 100 / disk.total);
disk.free_percent = 100 - disk.used_percent;
return pair{disk, -1};
});
++it;
}
//? Setup disks order in UI and add swap if enabled
@ -1245,29 +1962,32 @@ namespace Mem {
}
// looking through all files that start with 'objset' to find the one containing `device_name` object stats
for (const auto& file: fs::directory_iterator(zfs_pool_stat_path)) {
filename = file.path().filename();
if (filename.starts_with("objset")) {
filestream.open(file.path());
if (filestream.good()) {
// skip first two lines
for (int i = 0; i < 2; i++) filestream.ignore(numeric_limits<streamsize>::max(), '\n');
// skip characters until '7' is reached, indicating data type 7, next value will be object name
filestream.ignore(numeric_limits<streamsize>::max(), '7');
filestream >> name_compare;
if (name_compare == device_name) {
filestream.close();
if (access(file.path().c_str(), R_OK) == 0) {
return file.path();
} else {
Logger::debug("Can't access file: " + file.path().string());
return "";
try {
for (const auto& file: fs::directory_iterator(zfs_pool_stat_path)) {
filename = file.path().filename();
if (filename.starts_with("objset")) {
filestream.open(file.path());
if (filestream.good()) {
// skip first two lines
for (int i = 0; i < 2; i++) filestream.ignore(numeric_limits<streamsize>::max(), '\n');
// skip characters until '7' is reached, indicating data type 7, next value will be object name
filestream.ignore(numeric_limits<streamsize>::max(), '7');
filestream >> name_compare;
if (name_compare == device_name) {
filestream.close();
if (access(file.path().c_str(), R_OK) == 0) {
return file.path();
} else {
Logger::debug("Can't access file: " + file.path().string());
return "";
}
}
}
filestream.close();
}
filestream.close();
}
}
catch (fs::filesystem_error& e) {}
Logger::debug("Could not read directory: " + zfs_pool_stat_path.string());
return "";
@ -1354,13 +2074,13 @@ namespace Mem {
}
namespace Net {
unordered_flat_map<string, net_info> current_net;
std::unordered_map<string, net_info> current_net;
net_info empty_net = {};
vector<string> interfaces;
string selected_iface;
int errors{}; // defaults to 0
unordered_flat_map<string, uint64_t> graph_max = { {"download", {}}, {"upload", {}} };
unordered_flat_map<string, array<int, 2>> max_count = { {"download", {}}, {"upload", {}} };
std::unordered_map<string, uint64_t> graph_max = { {"download", {}}, {"upload", {}} };
std::unordered_map<string, array<int, 2>> max_count = { {"download", {}}, {"upload", {}} };
bool rescale{true};
uint64_t timestamp{}; // defaults to 0
@ -1499,7 +2219,6 @@ namespace Net {
else
it++;
}
net.compact();
}
timestamp = new_timestamp;
@ -1569,7 +2288,7 @@ namespace Net {
namespace Proc {
vector<proc_info> current_procs;
unordered_flat_map<string, string> uid_user;
std::unordered_map<string, string> uid_user;
string current_sort;
string current_filter;
bool current_rev{}; // defaults to false
@ -1584,7 +2303,7 @@ namespace Proc {
detail_container detailed;
constexpr size_t KTHREADD = 2;
static robin_hood::unordered_set<size_t> kernels_procs = {KTHREADD};
static std::unordered_set<size_t> kernels_procs = {KTHREADD};
//* Get detailed info for selected process
void _collect_details(const size_t pid, const uint64_t uptime, vector<proc_info>& procs) {
@ -2070,6 +2789,6 @@ namespace Tools {
catch (const std::invalid_argument&) {}
catch (const std::out_of_range&) {}
}
throw std::runtime_error("Failed get uptime from from " + string{Shared::procPath} + "/uptime");
throw std::runtime_error("Failed to get uptime from " + string{Shared::procPath} + "/uptime");
}
}

1295
src/openbsd/btop_collect.cpp Normal file

File diff suppressed because it is too large Load diff

157
src/openbsd/internal.h Normal file
View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2019-2021 Brian Callahan <bcallah@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
struct sysctls {
const char *name;
int mib0;
int mib1;
int mib2;
} sysctlnames[] = {
{ "hw.machine", CTL_HW, HW_MACHINE, 0 },
{ "hw.model", CTL_HW, HW_MODEL, 0 },
{ "hw.ncpu", CTL_HW, HW_NCPU, 0 },
{ "hw.byteorder", CTL_HW, HW_BYTEORDER, 0 },
{ "hw.pagesize", CTL_HW, HW_PAGESIZE, 0 },
{ "hw.disknames", CTL_HW, HW_DISKNAMES, 0 },
{ "hw.diskcount", CTL_HW, HW_DISKCOUNT, 0 },
{ "hw.sensors", CTL_HW, HW_SENSORS, 0 },
{ "hw.model", CTL_HW, HW_MODEL, 0 },
{ "hw.ncpu", CTL_HW, HW_NCPU, 0 },
{ "hw.byteorder", CTL_HW, HW_BYTEORDER, 0 },
{ "hw.pagesize", CTL_HW, HW_PAGESIZE, 0 },
{ "hw.disknames", CTL_HW, HW_DISKNAMES, 0 },
{ "hw.diskcount", CTL_HW, HW_DISKCOUNT, 0 },
{ "hw.sensors", CTL_HW, HW_SENSORS, 0 },
{ "hw.cpuspeed", CTL_HW, HW_CPUSPEED, 0 },
{ "hw.setperf", CTL_HW, HW_SETPERF, 0 },
{ "hw.vendor", CTL_HW, HW_VENDOR, 0 },
{ "hw.product", CTL_HW, HW_PRODUCT, 0 },
{ "hw.serialno", CTL_HW, HW_SERIALNO, 0 },
{ "hw.uuid", CTL_HW, HW_UUID, 0 },
{ "hw.physmem", CTL_HW, HW_PHYSMEM64, 0 },
{ "hw.usermem", CTL_HW, HW_USERMEM64, 0 },
{ "hw.ncpufound", CTL_HW, HW_NCPUFOUND, 0 },
{ "hw.allowpowerdown", CTL_HW, HW_ALLOWPOWERDOWN, 0 },
{ "hw.perfpolicy", CTL_HW, HW_PERFPOLICY, 0 },
{ "hw.smt", CTL_HW, HW_SMT, 0 },
{ "hw.ncpuonline", CTL_HW, HW_NCPUONLINE, 0 },
{ "hw.cpuspeed", CTL_HW, HW_CPUSPEED, 0 },
{ "hw.setperf", CTL_HW, HW_SETPERF, 0 },
{ "hw.vendor", CTL_HW, HW_VENDOR, 0 },
{ "hw.product", CTL_HW, HW_PRODUCT, 0 },
{ "hw.serialno", CTL_HW, HW_SERIALNO, 0 },
{ "hw.uuid", CTL_HW, HW_UUID, 0 },
{ "hw.physmem", CTL_HW, HW_PHYSMEM64, 0 },
{ "hw.usermem", CTL_HW, HW_USERMEM64, 0 },
{ "hw.ncpufound", CTL_HW, HW_NCPUFOUND, 0 },
{ "hw.allowpowerdown", CTL_HW, HW_ALLOWPOWERDOWN, 0 },
{ "hw.perfpolicy", CTL_HW, HW_PERFPOLICY, 0 },
{ "hw.smt", CTL_HW, HW_SMT, 0 },
{ "hw.ncpuonline", CTL_HW, HW_NCPUONLINE, 0 },
{ "kern.ostype", CTL_KERN, KERN_OSTYPE, 0 },
{ "kern.osrelease", CTL_KERN, KERN_OSRELEASE, 0 },
{ "kern.osrevision", CTL_KERN, KERN_OSREV, 0 },
{ "kern.version", CTL_KERN, KERN_VERSION, 0 },
{ "kern.maxvnodes", CTL_KERN, KERN_MAXVNODES, 0 },
{ "kern.maxproc", CTL_KERN, KERN_MAXPROC, 0 },
{ "kern.maxfiles", CTL_KERN, KERN_MAXFILES, 0 },
{ "kern.argmax", CTL_KERN, KERN_ARGMAX, 0 },
{ "kern.securelevel", CTL_KERN, KERN_SECURELVL, 0 },
{ "kern.hostname", CTL_KERN, KERN_HOSTNAME, 0 },
{ "kern.hostid", CTL_KERN, KERN_HOSTID, 0 },
{ "kern.clockrate", CTL_KERN, KERN_CLOCKRATE, 0 },
{ "kern.profiling", CTL_KERN, KERN_PROF, 0 },
{ "kern.posix1version", CTL_KERN, KERN_POSIX1, 0 },
{ "kern.ngroups", CTL_KERN, KERN_NGROUPS, 0 },
{ "kern.job_control", CTL_KERN, KERN_JOB_CONTROL, 0 },
{ "kern.saved_ids", CTL_KERN, KERN_SAVED_IDS, 0 },
{ "kern.boottime", CTL_KERN, KERN_BOOTTIME, 0 },
{ "kern.domainname", CTL_KERN, KERN_DOMAINNAME, 0 },
{ "kern.maxpartitions", CTL_KERN, KERN_MAXPARTITIONS, 0 },
{ "kern.rawpartition", CTL_KERN, KERN_RAWPARTITION, 0 },
{ "kern.maxthread", CTL_KERN, KERN_MAXTHREAD, 0 },
{ "kern.nthreads", CTL_KERN, KERN_NTHREADS, 0 },
{ "kern.osversion", CTL_KERN, KERN_OSVERSION, 0 },
{ "kern.somaxconn", CTL_KERN, KERN_SOMAXCONN, 0 },
{ "kern.sominconn", CTL_KERN, KERN_SOMINCONN, 0 },
{ "kern.nosuidcoredump", CTL_KERN, KERN_NOSUIDCOREDUMP, 0 },
{ "kern.fsync", CTL_KERN, KERN_FSYNC, 0 },
{ "kern.sysvmsg", CTL_KERN, KERN_SYSVMSG, 0 },
{ "kern.sysvsem", CTL_KERN, KERN_SYSVSEM, 0 },
{ "kern.sysvshm", CTL_KERN, KERN_SYSVSHM, 0 },
{ "kern.msgbufsize", CTL_KERN, KERN_MSGBUFSIZE, 0 },
{ "kern.malloc", CTL_KERN, KERN_MALLOCSTATS, 0 },
{ "kern.cp_time", CTL_KERN, KERN_CPTIME, 0 },
{ "kern.nchstats", CTL_KERN, KERN_NCHSTATS, 0 },
{ "kern.forkstat", CTL_KERN, KERN_FORKSTAT, 0 },
{ "kern.tty", CTL_KERN, KERN_TTY, 0 },
{ "kern.ccpu", CTL_KERN, KERN_CCPU, 0 },
{ "kern.fscale", CTL_KERN, KERN_FSCALE, 0 },
{ "kern.nprocs", CTL_KERN, KERN_NPROCS, 0 },
{ "kern.msgbuf", CTL_KERN, KERN_MSGBUF, 0 },
{ "kern.pool", CTL_KERN, KERN_POOL, 0 },
{ "kern.stackgap_random", CTL_KERN, KERN_STACKGAPRANDOM, 0 },
{ "kern.sysvipc_info", CTL_KERN, KERN_SYSVIPC_INFO, 0 },
{ "kern.allowkmem", CTL_KERN, KERN_ALLOWKMEM, 0 },
{ "kern.witnesswatch", CTL_KERN, KERN_WITNESSWATCH, 0 },
{ "kern.splassert", CTL_KERN, KERN_SPLASSERT, 0 },
{ "kern.procargs", CTL_KERN, KERN_PROC_ARGS, 0 },
{ "kern.nfiles", CTL_KERN, KERN_NFILES, 0 },
{ "kern.ttycount", CTL_KERN, KERN_TTYCOUNT, 0 },
{ "kern.numvnodes", CTL_KERN, KERN_NUMVNODES, 0 },
{ "kern.mbstat", CTL_KERN, KERN_MBSTAT, 0 },
{ "kern.witness", CTL_KERN, KERN_WITNESS, 0 },
{ "kern.seminfo", CTL_KERN, KERN_SEMINFO, 0 },
{ "kern.shminfo", CTL_KERN, KERN_SHMINFO, 0 },
{ "kern.intrcnt", CTL_KERN, KERN_INTRCNT, 0 },
{ "kern.watchdog", CTL_KERN, KERN_WATCHDOG, 0 },
{ "kern.proc", CTL_KERN, KERN_PROC, 0 },
{ "kern.maxclusters", CTL_KERN, KERN_MAXCLUSTERS, 0 },
{ "kern.evcount", CTL_KERN, KERN_EVCOUNT, 0 },
{ "kern.timecounter", CTL_KERN, KERN_TIMECOUNTER, 0 },
{ "kern.maxlocksperuid", CTL_KERN, KERN_MAXLOCKSPERUID, 0 },
{ "kern.cp_time2", CTL_KERN, KERN_CPTIME2, 0 },
{ "kern.bufcachepercent", CTL_KERN, KERN_CACHEPCT, 0 },
{ "kern.file", CTL_KERN, KERN_FILE, 0 },
{ "kern.wxabort", CTL_KERN, KERN_WXABORT, 0 },
{ "kern.consdev", CTL_KERN, KERN_CONSDEV, 0 },
{ "kern.netlivelocks", CTL_KERN, KERN_NETLIVELOCKS, 0 },
{ "kern.pool_debug", CTL_KERN, KERN_POOL_DEBUG, 0 },
{ "kern.proc_cwd", CTL_KERN, KERN_PROC_CWD, 0 },
{ "kern.proc_nobroadcastkill", CTL_KERN, KERN_PROC_NOBROADCASTKILL, 0 },
{ "kern.proc_vmap", CTL_KERN, KERN_PROC_VMMAP, 0 },
{ "kern.global_ptrace", CTL_KERN, KERN_GLOBAL_PTRACE, 0 },
{ "kern.consbufsize", CTL_KERN, KERN_CONSBUFSIZE, 0 },
{ "kern.consbuf", CTL_KERN, KERN_CONSBUF, 0 },
{ "kern.audio", CTL_KERN, KERN_AUDIO, 0 },
{ "kern.cpustats", CTL_KERN, KERN_CPUSTATS, 0 },
{ "kern.pfstatus", CTL_KERN, KERN_PFSTATUS, 0 },
{ "kern.timeout_stats", CTL_KERN, KERN_TIMEOUT_STATS, 0 },
{ "kern.utc_offset", CTL_KERN, KERN_UTC_OFFSET, 0 },
{ "vm.vmmeter", CTL_VM, VM_METER, 0 },
{ "vm.loadavg", CTL_VM, VM_LOADAVG, 0 },
{ "vm.psstrings", CTL_VM, VM_PSSTRINGS, 0 },
{ "vm.uvmexp", CTL_VM, VM_UVMEXP, 0 },
{ "vm.swapencrypt", CTL_VM, VM_SWAPENCRYPT, 0 },
{ "vm.nkmempages", CTL_VM, VM_NKMEMPAGES, 0 },
{ "vm.anonmin", CTL_VM, VM_ANONMIN, 0 },
{ "vm.vtextmin", CTL_VM, VM_VTEXTMIN, 0 },
{ "vm.vnodemin", CTL_VM, VM_VNODEMIN, 0 },
{ "vm.maxslp", CTL_VM, VM_MAXSLP, 0 },
{ "vm.uspace", CTL_VM, VM_USPACE, 0 },
{ "vm.malloc_conf", CTL_VM, VM_MALLOC_CONF, 0 },
{ NULL, 0, 0, 0 },
};

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2021 Brian Callahan <bcallah@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "internal.h"
#include "../btop_tools.hpp"
int
sysctlbyname(const char *name, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int i, mib[2];
for (i = 0; i < 132; i++) {
// for (i = 0; i < sizeof(sysctlnames) / sizeof(sysctlnames[0]); i++) {
if (!strcmp(name, sysctlnames[i].name)) {
mib[0] = sysctlnames[i].mib0;
mib[1] = sysctlnames[i].mib1;
return sysctl(mib, 2, oldp, oldlenp, newp, newlen);
}
}
errno = ENOENT;
return (-1);
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2019 Brian Callahan <bcallah@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/sysctl.h>
extern int sysctlbyname(const char *, void *, size_t *, void *, size_t);

View file

@ -16,6 +16,7 @@ indent = tab
tab-size = 4
*/
#include <Availability.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <arpa/inet.h>
@ -44,6 +45,7 @@ tab-size = 4
#include <netinet/in.h> // for inet_ntop
#include <unistd.h>
#include <stdexcept>
#include <utility>
#include <cmath>
#include <fstream>
@ -56,7 +58,9 @@ tab-size = 4
#include "../btop_shared.hpp"
#include "../btop_tools.hpp"
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
#include "sensors.hpp"
#endif
#include "smc.hpp"
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
@ -70,7 +74,7 @@ using namespace Tools;
namespace Cpu {
vector<long long> core_old_totals;
vector<long long> core_old_idles;
vector<string> available_fields = {"total"};
vector<string> available_fields = {"Auto", "total"};
vector<string> available_sensors = {"Auto"};
cpu_info current_cpu;
bool got_sensors = false, cpu_temp_only = false;
@ -95,7 +99,7 @@ namespace Cpu {
string cpu_sensor;
vector<string> core_sensors;
unordered_flat_map<int, int> core_mapping;
std::unordered_map<int, int> core_mapping;
} // namespace Cpu
namespace Mem {
@ -191,7 +195,7 @@ namespace Cpu {
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
unordered_flat_map<string, long long> cpu_old = {
std::unordered_map<string, long long> cpu_old = {
{"totals", 0},
{"idles", 0},
{"user", 0},
@ -236,7 +240,7 @@ namespace Cpu {
name += n + ' ';
}
name.pop_back();
for (const auto& replace : {"Processor", "CPU", "(R)", "(TM)", "Intel", "AMD", "Core"}) {
for (const auto& replace : {"Processor", "CPU", "(R)", "(TM)", "Intel", "AMD", "Apple", "Core"}) {
name = s_replace(name, replace, "");
name = s_replace(name, " ", " ");
}
@ -250,6 +254,7 @@ namespace Cpu {
Logger::debug("get_sensors(): show_coretemp=" + std::to_string(Config::getB("show_coretemp")) + " check_temp=" + std::to_string(Config::getB("check_temp")));
got_sensors = false;
if (Config::getB("show_coretemp") and Config::getB("check_temp")) {
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
ThermalSensors sensors;
if (sensors.getSensors() > 0) {
Logger::debug("M1 sensors found");
@ -257,6 +262,7 @@ namespace Cpu {
cpu_temp_only = true;
macM1 = true;
} else {
#endif
// try SMC (intel)
Logger::debug("checking intel");
SMCConnection smcCon;
@ -281,7 +287,9 @@ namespace Cpu {
// ignore, we don't have temp
got_sensors = false;
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
}
#endif
}
return got_sensors;
}
@ -290,11 +298,12 @@ namespace Cpu {
current_cpu.temp_max = 95; // we have no idea how to get the critical temp
try {
if (macM1) {
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
ThermalSensors sensors;
current_cpu.temp.at(0).push_back(sensors.getSensors());
if (current_cpu.temp.at(0).size() > 20)
current_cpu.temp.at(0).pop_front();
#endif
} else {
SMCConnection smcCon;
int threadsPerCore = Shared::coreCount / Shared::physicalCoreCount;
@ -329,8 +338,8 @@ namespace Cpu {
return std::to_string(freq / 1000.0 / 1000.0 / 1000.0).substr(0, 3);
}
auto get_core_mapping() -> unordered_flat_map<int, int> {
unordered_flat_map<int, int> core_map;
auto get_core_mapping() -> std::unordered_map<int, int> {
std::unordered_map<int, int> core_map;
if (cpu_temp_only) return core_map;
natural_t cpu_count;
@ -591,7 +600,7 @@ namespace Mem {
io_object_t &object;
};
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
void collect_disk(std::unordered_map<string, disk_info> &disks, std::unordered_map<string, string> &mapping) {
io_registry_entry_t drive;
io_iterator_t drive_list;
@ -708,7 +717,7 @@ namespace Mem {
}
if (show_disks) {
unordered_flat_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
std::unordered_map<string, string> mapping; // keep mapping from device -> mountpoint, since IOKit doesn't give us the mountpoint
double uptime = system_uptime();
auto &disks_filter = Config::getS("disks_filter");
bool filter_exclude = false;
@ -821,13 +830,13 @@ namespace Mem {
} // namespace Mem
namespace Net {
unordered_flat_map<string, net_info> current_net;
std::unordered_map<string, net_info> current_net;
net_info empty_net = {};
vector<string> interfaces;
string selected_iface;
int errors = 0;
unordered_flat_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
unordered_flat_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
std::unordered_map<string, uint64_t> graph_max = {{"download", {}}, {"upload", {}}};
std::unordered_map<string, array<int, 2>> max_count = {{"download", {}}, {"upload", {}}};
bool rescale = true;
uint64_t timestamp = 0;
@ -904,7 +913,7 @@ namespace Net {
} // else, ignoring family==AF_LINK (see man 3 getifaddrs)
}
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
std::unordered_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0};
size_t len;
if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) {
@ -978,7 +987,6 @@ namespace Net {
else
it++;
}
net.compact();
}
timestamp = new_timestamp;
@ -1049,7 +1057,7 @@ namespace Net {
namespace Proc {
vector<proc_info> current_procs;
unordered_flat_map<string, string> uid_user;
std::unordered_map<string, string> uid_user;
string current_sort;
string current_filter;
bool current_rev = false;

View file

@ -16,6 +16,8 @@ indent = tab
tab-size = 4
*/
#include <Availability.h>
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
#include "sensors.hpp"
#include <CoreFoundation/CoreFoundation.h>
@ -109,3 +111,4 @@ long long Cpu::ThermalSensors::getSensors() {
if (temps.empty()) return 0ll;
return round(std::accumulate(temps.begin(), temps.end(), 0ll) / temps.size());
}
#endif

View file

@ -16,9 +16,12 @@ indent = tab
tab-size = 4
*/
#include <Availability.h>
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
namespace Cpu {
class ThermalSensors {
public:
long long getSensors();
};
} // namespace Cpu
#endif

View file

@ -3,7 +3,7 @@
# By: Dennis Mayr
# Main bg
theme[main_bg]="#2b2b2b"
theme[main_bg]="#333333"
# Main text color
theme[main_fg]="#eee8d5"
@ -12,71 +12,71 @@ theme[main_fg]="#eee8d5"
theme[title]="#eee8d5"
# Higlight color for keyboard shortcuts
theme[hi_fg]="#dc322f"
theme[hi_fg]="#d1302c"
# Background color of selected item in processes box
theme[selected_bg]="#268bd2"
theme[selected_bg]="#268ad0"
# Foreground color of selected item in processes box
theme[selected_fg]="#eee8d5"
# Color of inactive/disabled text
theme[inactive_fg]="#586e75"
theme[inactive_fg]="#657b83"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#268bd2"
theme[proc_misc]="#268ad0"
# Cpu box outline color
theme[cpu_box]="#586e75"
theme[cpu_box]="#657b83"
# Memory/disks box outline color
theme[mem_box]="#586e75"
theme[mem_box]="#657b83"
# Net up/down box outline color
theme[net_box]="#586e75"
theme[net_box]="#657b83"
# Processes box outline color
theme[proc_box]="#586e75"
theme[proc_box]="#657b83"
# Box divider line and small boxes line color
theme[div_line]="#586e75"
theme[div_line]="#657b83"
# Temperature graph colors
theme[temp_start]="#859900"
theme[temp_mid]="#b58901"
theme[temp_end]="#dc322f"
theme[temp_mid]="#b28602"
theme[temp_end]="#d1302c"
# CPU graph colors
theme[cpu_start]="#859900"
theme[cpu_mid]="#b58901"
theme[cpu_end]="#dc322f"
theme[cpu_mid]="#b28602"
theme[cpu_end]="#d1302c"
# Mem/Disk free meter
theme[free_start]="#268bd2"
theme[free_start]="#268ad0"
theme[free_mid]="#6c71c4"
theme[free_end]="#2a9d95"
# Mem/Disk cached meter
theme[cached_start]="#268bd2"
theme[cached_start]="#268ad0"
theme[cached_mid]="#6c71c4"
theme[cached_end]="#dc322f"
theme[cached_end]="#d1302c"
# Mem/Disk available meter
theme[available_start]="#268bd2"
theme[available_start]="#268ad0"
theme[available_mid]="#6c71c4"
theme[available_end]="#dc322f"
theme[available_end]="#d1302c"
# Mem/Disk used meter
theme[used_start]="#859900"
theme[used_mid]="#b58901"
theme[used_end]="#dc322f"
theme[used_mid]="#b28602"
theme[used_end]="#d1302c"
# Download graph colors
theme[download_start]="#268bd2"
theme[download_start]="#268ad0"
theme[download_mid]="#6c71c4"
theme[download_end]="#dc322f"
theme[download_end]="#d1302c"
# Upload graph colors
theme[upload_start]="#268bd2"
theme[upload_start]="#268ad0"
theme[upload_mid]="#6c71c4"
theme[upload_end]="#dc322f"
theme[upload_end]="#d1302c"