chore: migrate repository to standard knowledge base layout

This commit is contained in:
tukuaiai
2026-05-02 03:29:06 +08:00
parent 40a721c24d
commit 628a3bc832
565 changed files with 687 additions and 711 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
#include <span>
#include <string_view>
[[nodiscard]] auto btop_main(std::span<const std::string_view> args) -> int;
@@ -0,0 +1,216 @@
// SPDX-License-Identifier: Apache-2.0
#include "btop_cli.hpp"
#include <algorithm>
#include <expected>
#include <filesystem>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <string_view>
#include <fmt/base.h>
#include <fmt/format.h>
#include "btop_shared.hpp"
#include "config.h"
using namespace std::string_view_literals;
static constexpr auto BOLD = "\033[1m"sv;
static constexpr auto BOLD_UNDERLINE = "\033[1;4m"sv;
static constexpr auto BOLD_RED = "\033[1;31m"sv;
static constexpr auto YELLOW = "\033[33m"sv;
static constexpr auto RESET = "\033[0m"sv;
static void version() noexcept {
if constexpr (GIT_COMMIT.empty()) {
fmt::println("btop version: {}{}{}", BOLD, Global::Version, RESET);
} else {
fmt::println("btop version: {}{}+{}{}", BOLD, Global::Version, GIT_COMMIT, RESET);
}
}
static void build_info() noexcept {
fmt::println("Compiled with: {} ({})", COMPILER, COMPILER_VERSION);
fmt::println("Configured with: {}", CONFIGURE_COMMAND);
}
static void error(std::string_view msg) noexcept {
fmt::println("{}error:{} {}\n", BOLD_RED, RESET, msg);
}
namespace Cli {
[[nodiscard]] auto parse(const std::span<const std::string_view> args) noexcept -> Result {
Cli cli {};
for (auto it = args.begin(); it != args.end(); ++it) {
auto arg = *it;
if (arg == "-h" || arg == "--help") {
usage();
help();
return std::unexpected { 0 };
}
if (arg == "-v" || arg == "-V") {
version();
return std::unexpected { 0 };
}
if (arg == "--version") {
version();
build_info();
return std::unexpected { 0 };
}
if (arg == "-d" || arg == "--debug") {
cli.debug = true;
continue;
}
if (arg == "--force-utf") {
cli.force_utf = true;
continue;
}
if (arg == "-l" || arg == "--low-color") {
cli.low_color = true;
continue;
}
if (arg == "-t" || arg == "--tty") {
if (cli.force_tty.has_value()) {
error("tty mode can't be set twice");
return std::unexpected { 1 };
}
cli.force_tty = std::make_optional(true);
continue;
}
if (arg == "--no-tty") {
if (cli.force_tty.has_value()) {
error("tty mode can't be set twice");
return std::unexpected { 1 };
}
cli.force_tty = std::make_optional(false);
continue;
}
if (arg == "-c" || arg == "--config") {
// This flag requires an argument.
if (++it == args.end()) {
error("Config requires an argument");
return std::unexpected { 1 };
}
auto arg = *it;
auto config_file = stdfs::path { arg };
if (stdfs::is_directory(config_file)) {
error("Config file can't be a directory");
return std::unexpected { 1 };
}
cli.config_file = std::make_optional(config_file);
continue;
}
if (arg == "-f" || arg == "--filter") {
// This flag requires an argument.
if (++it == args.end()) {
error("Filter requires an argument");
return std::unexpected { 1 };
}
auto arg = *it;
cli.filter = std::make_optional(arg);
continue;
}
if (arg == "-p" || arg == "--preset") {
// This flag requires an argument.
if (++it == args.end()) {
error("Preset requires an argument");
return std::unexpected { 1 };
}
auto arg = *it;
try {
auto preset_id = std::clamp(std::stoi(arg.data()), 0, 9);
cli.preset = std::make_optional(preset_id);
} catch (std::invalid_argument& e) {
error("Preset must be a positive number");
return std::unexpected { 1 };
} catch (std::out_of_range& e) {
error(fmt::format("Preset argument is out of range: {}", arg.data()));
return std::unexpected { 1 };
}
continue;
}
if (arg == "--themes-dir") {
// This flag requires an argument.
if (++it == args.end()) {
error("Themes directory requires an argument");
return std::unexpected { 1 };
}
auto arg = *it;
auto themes_dir = stdfs::path { arg };
if (not stdfs::is_directory(themes_dir)) {
error("Themes directory does not exist or is not a directory");
return std::unexpected { 1 };
}
cli.themes_dir = std::make_optional(themes_dir);
continue;
}
if (arg == "-u" || arg == "--update") {
// This flag requires an argument.
if (++it == args.end()) {
error("Update requires an argument");
return std::unexpected { 1 };
}
auto arg = *it;
try {
auto refresh_rate = std::max(std::stoi(arg.data()), 100);
cli.updates = refresh_rate;
} catch (std::invalid_argument& e) {
error("Update must be a positive number");
return std::unexpected { 1 };
} catch (std::out_of_range& e) {
error(fmt::format("Update argument is out of range: {}", arg.data()));
return std::unexpected { 1 };
}
continue;
}
error(fmt::format("Unknown argument '{}{}{}'", YELLOW, arg, RESET));
return std::unexpected { 1 };
}
return cli;
}
void usage() noexcept {
fmt::println("{0}Usage:{1} {2}btop{1} [OPTIONS]\n", BOLD_UNDERLINE, RESET, BOLD);
}
void help() noexcept {
fmt::print(
"{0}Options:{1}\n"
" {2}-c, --config{1} <file> Path to a config file\n"
" {2}-d, --debug{1} Start in debug mode with additional logs and metrics\n"
" {2}-f, --filter{1} <filter> Set an initial process filter\n"
" {2} --force-utf{1} Override automatic UTF locale detection\n"
" {2}-l, --low-color{1} Disable true color, 256 colors only\n"
" {2}-p, --preset{1} <id> Start with a preset (0-9)\n"
" {2}-t, --tty{1} Force tty mode with ANSI graph symbols and 16 colors only\n"
" {2} --themes-dir{1} <dir> Path to a custom themes directory\n"
" {2} --no-tty{1} Force disable tty mode\n"
" {2}-u, --update{1} <ms> Set an initial update rate in milliseconds\n"
" {2}-h, --help{1} Show this help message and exit\n"
" {2}-V, --version{1} Show a version message and exit (more with --version)\n",
BOLD_UNDERLINE, RESET, BOLD
);
}
void help_hint() noexcept {
fmt::println("For more information, try '{}--help{}'", BOLD, RESET);
}
} // namespace Cli
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <cstdint>
#include <expected>
#include <filesystem>
#include <optional>
#include <span>
#include <string_view>
namespace Cli {
namespace stdfs = std::filesystem;
// Configuration options set via the command line.
struct Cli {
// Alternate path to a configuration file
std::optional<stdfs::path> config_file;
// Enable debug mode with additional logs and metrics
bool debug {};
// Set an initial process filter.
std::optional<std::string> filter;
// Only use ANSI supported graph symbols and colors
std::optional<bool> force_tty;
// Use UTF-8 locale even if not detected
bool force_utf {};
// Disable true color and only use 256 color mode
bool low_color {};
// Start with one of the provided presets
std::optional<std::uint32_t> preset;
// Path to a custom themes directory
std::optional<stdfs::path> themes_dir;
// The initial refresh rate
std::optional<std::uint32_t> updates;
};
using Result = std::expected<Cli, std::int32_t>;
// Parse the command line arguments
[[nodiscard]] auto parse(std::span<const std::string_view> args) noexcept -> Result;
// Print a usage header
void usage() noexcept;
// Print a help message
void help() noexcept;
// Print a hint on how to show more help
void help_hint() noexcept;
} // namespace Cli
@@ -0,0 +1,831 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <array>
#include <atomic>
#include <filesystem>
#include <fstream>
#include <locale>
#include <optional>
#include <ranges>
#include <string_view>
#include <utility>
#include <fmt/core.h>
#include <sys/statvfs.h>
#include "btop_config.hpp"
#include "btop_shared.hpp"
#include "btop_tools.hpp"
using std::array;
using std::atomic;
using std::string_view;
namespace fs = std::filesystem;
namespace rng = std::ranges;
using namespace std::literals;
using namespace Tools;
//* Functions and variables for reading and writing the btop config file
namespace Config {
atomic<bool> locked (false);
atomic<bool> writelock (false);
bool write_new;
const vector<array<string, 2>> descriptions = {
{"color_theme", "#* Name of a btop++/bpytop/bashtop formatted \".theme\" file, \"Default\" and \"TTY\" for builtin themes.\n"
"#* Themes should be placed in \"../share/btop/themes\" relative to binary or \"$HOME/.config/btop/themes\""},
{"theme_background", "#* If the theme set background should be shown, set to False if you want terminal background transparency."},
{"truecolor", "#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false."},
{"force_tty", "#* Set to true to force tty mode regardless if a real tty has been detected or not.\n"
"#* Will force 16-color mode and TTY theme, set all graph symbols to \"tty\" and swap out other non tty friendly symbols."},
{"presets", "#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets.\n"
"#* Format: \"box_name:P:G,box_name:P:G\" P=(0 or 1) for alternate positions, G=graph symbol to use for box.\n"
"#* Use whitespace \" \" as separator between different presets.\n"
"#* Example: \"cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty\""},
{"vim_keys", "#* Set to True to enable \"h,j,k,l,g,G\" keys for directional control in lists.\n"
"#* Conflicting keys for h:\"help\" and k:\"kill\" is accessible while holding shift."},
{"rounded_corners", "#* Rounded corners on boxes, is ignored if TTY mode is ON."},
{"terminal_sync", "#* Use terminal synchronized output sequences to reduce flickering on supported terminals."},
{"graph_symbol", "#* Default symbols to use for graph creation, \"braille\", \"block\" or \"tty\".\n"
"#* \"braille\" offers the highest resolution but might not be included in all fonts.\n"
"#* \"block\" has half the resolution of braille but uses more common characters.\n"
"#* \"tty\" uses only 3 different symbols but will work with most fonts and should work in a real TTY.\n"
"#* 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\" 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."},
{"proc_sorting", "#* Processes sorting, \"pid\" \"program\" \"arguments\" \"threads\" \"user\" \"memory\" \"cpu lazy\" \"cpu direct\",\n"
"#* \"cpu lazy\" sorts top process over time (easier to follow), \"cpu direct\" updates top process directly."},
{"proc_reversed", "#* Reverse sorting order, True or False."},
{"proc_tree", "#* Show processes as a tree."},
{"proc_colors", "#* Use the cpu graph colors in the process list."},
{"proc_gradient", "#* Use a darkening gradient in the process list."},
{"proc_per_core", "#* If process cpu usage should be of the core it's running on or usage of the total available cpu power."},
{"proc_mem_bytes", "#* Show process memory as bytes instead of percent."},
{"proc_cpu_graphs", "#* Show cpu graph for each process."},
{"proc_info_smaps", "#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate)"},
{"proc_left", "#* Show proc box on left side of screen instead of right."},
{"proc_filter_kernel", "#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop)."},
{"proc_aggregate", "#* In tree-view, always accumulate child process resources in the parent process."},
{"keep_dead_proc_usage", "#* Should cpu and memory usage display be preserved for dead processes when paused."},
{"cpu_graph_upper", "#* Sets the CPU stat shown in upper half of the CPU graph, \"total\" is always available.\n"
"#* Select from a list of detected attributes from the options menu."},
{"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."},
{"cpu_bottom", "#* Show cpu box at bottom of screen instead of top."},
{"show_uptime", "#* Shows the system uptime in the CPU box."},
{"show_cpu_watts", "#* Shows the CPU package current power consumption in watts. Requires running `make setcap` or `make setuid` or running with sudo."},
{"check_temp", "#* Show cpu temperature."},
{"cpu_sensor", "#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors."},
{"show_coretemp", "#* Show temperatures for cpu cores also if check_temp is True and sensors has been found."},
{"cpu_core_map", "#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core.\n"
"#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine.\n"
"#* Format \"x:y\" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries.\n"
"#* Example: \"4:0 5:1 6:3\""},
{"temp_scale", "#* Which temperature scale to use, available values: \"celsius\", \"fahrenheit\", \"kelvin\" and \"rankine\"."},
{"base_10_sizes", "#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024."},
{"show_cpu_freq", "#* Show CPU frequency."},
#ifdef __linux__
{"freq_mode", "#* How to calculate CPU frequency, available values: \"first\", \"range\", \"lowest\", \"highest\" and \"average\"."},
#endif
{"clock_format", "#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.\n"
"#* Special formatting: /host = hostname | /user = username | /uptime = system uptime"},
{"background_update", "#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort."},
{"custom_cpu_name", "#* Custom cpu model name, empty string to disable."},
{"disks_filter", "#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace \" \".\n"
"#* Only disks matching the filter will be shown. Prepend exclude= to only show disks not matching the filter. Examples: disk_filter=\"/boot /home/user\", disks_filter=\"exclude=/boot /home/user\""},
{"mem_graphs", "#* Show graphs instead of meters for memory values."},
{"mem_below_net", "#* Show mem box below net box instead of above."},
{"zfs_arc_cached", "#* Count ZFS ARC in cached and available memory."},
{"show_swap", "#* If swap memory should be shown in memory box."},
{"swap_disk", "#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk."},
{"show_disks", "#* If mem box should be split to also show disks info."},
{"only_physical", "#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar."},
{"use_fstab", "#* Read disks list from /etc/fstab. This also disables only_physical."},
{"zfs_hide_datasets", "#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool)"},
{"disk_free_priv", "#* Set to true to show available disk space for privileged users."},
{"show_io_stat", "#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view."},
{"io_mode", "#* Toggles io mode for disks, showing big graphs for disk read/write speeds."},
{"io_graph_combined", "#* Set to True to show combined read/write io graphs in io mode."},
{"io_graph_speeds", "#* Set the top speed for the io graphs in MiB/s (100 by default), use format \"mountpoint:speed\" separate disks with whitespace \" \".\n"
"#* Example: \"/mnt/media:100 /:20 /boot:1\"."},
{"net_download", "#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False."},
{"net_upload", ""},
{"net_auto", "#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest."},
{"net_sync", "#* Sync the auto scaling for download and upload to whichever currently has the highest scale."},
{"net_iface", "#* Starts with the Network Interface specified here."},
{"base_10_bitrate", "#* \"True\" shows bitrates in base 10 (Kbps, Mbps). \"False\" shows bitrates in binary sizes (Kibps, Mibps, etc.). \"Auto\" uses base_10_sizes."},
{"show_battery", "#* Show battery stats in top right if battery is present."},
{"selected_battery", "#* Which battery to use if multiple are present. \"Auto\" for auto detection."},
{"show_battery_watts", "#* Show power stats of battery next to charge indicator."},
{"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."},
#ifdef GPU_SUPPORT
{"nvml_measure_pcie_speeds",
"#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards."},
{"rsmi_measure_pcie_speeds",
"#* Measure PCIe throughput on AMD cards, may impact performance on certain cards."},
{"gpu_mirror_graph", "#* Horizontally mirror the GPU graph."},
{"shown_gpus", "#* Set which GPU vendors to show. Available values are \"nvidia amd intel\""},
{"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
};
std::unordered_map<std::string_view, string> strings = {
{"color_theme", "Default"},
{"shown_boxes", "net proc"},
{"graph_symbol", "braille"},
{"presets", "net:0:default,proc:0:default"},
{"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", "Auto"},
{"cpu_graph_lower", "Auto"},
{"cpu_sensor", "Auto"},
{"selected_battery", "Auto"},
{"cpu_core_map", ""},
{"temp_scale", "celsius"},
#ifdef __linux__
{"freq_mode", "first"},
#endif
{"clock_format", "%X"},
{"custom_cpu_name", ""},
{"disks_filter", ""},
{"io_graph_speeds", ""},
{"net_iface", ""},
{"base_10_bitrate", "Auto"},
{"log_level", "WARNING"},
{"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"},
{"shown_gpus", "nvidia amd intel"}
#endif
};
std::unordered_map<std::string_view, string> stringsTmp;
std::unordered_map<std::string_view, bool> bools = {
{"theme_background", true},
{"truecolor", true},
{"rounded_corners", true},
{"proc_reversed", false},
{"proc_tree", false},
{"proc_colors", true},
{"proc_gradient", true},
{"proc_per_core", false},
{"proc_mem_bytes", true},
{"proc_cpu_graphs", true},
{"proc_info_smaps", false},
{"proc_left", false},
{"proc_filter_kernel", false},
{"cpu_invert_lower", true},
{"cpu_single_graph", false},
{"cpu_bottom", false},
{"show_uptime", true},
{"show_cpu_watts", true},
{"check_temp", true},
{"show_coretemp", true},
{"show_cpu_freq", true},
{"background_update", true},
{"mem_graphs", true},
{"mem_below_net", false},
{"zfs_arc_cached", true},
{"show_swap", true},
{"swap_disk", true},
{"show_disks", true},
{"only_physical", true},
{"use_fstab", true},
{"zfs_hide_datasets", false},
{"show_io_stat", true},
{"io_mode", false},
{"base_10_sizes", false},
{"io_graph_combined", false},
{"net_auto", true},
{"net_sync", true},
{"show_battery", true},
{"show_battery_watts", true},
{"vim_keys", false},
{"tty_mode", false},
{"disk_free_priv", false},
{"force_tty", false},
{"lowcolor", false},
{"show_detailed", false},
{"proc_filtering", false},
{"proc_aggregate", false},
{"pause_proc_list", false},
{"keep_dead_proc_usage", false},
#ifdef GPU_SUPPORT
{"nvml_measure_pcie_speeds", true},
{"rsmi_measure_pcie_speeds", true},
{"gpu_mirror_graph", true},
#endif
{"terminal_sync", true}
};
std::unordered_map<std::string_view, bool> boolsTmp;
std::unordered_map<std::string_view, int> ints = {
{"update_ms", 2000},
{"net_download", 100},
{"net_upload", 100},
{"detailed_pid", 0},
{"selected_pid", 0},
{"selected_depth", 0},
{"proc_start", 0},
{"proc_selected", 0},
{"proc_last_selected", 0}
};
std::unordered_map<std::string_view, int> intsTmp;
// Returns a valid config dir or an empty optional
// The config dir might be read only, a warning is printed, but a path is returned anyway
[[nodiscard]] std::optional<fs::path> get_config_dir() noexcept {
fs::path config_dir;
{
std::error_code error;
if (const auto xdg_config_home = std::getenv("XDG_CONFIG_HOME"); xdg_config_home != nullptr) {
if (fs::exists(xdg_config_home, error)) {
config_dir = fs::path(xdg_config_home) / "btop";
}
} else if (const auto home = std::getenv("HOME"); home != nullptr) {
error.clear();
if (fs::exists(home, error)) {
config_dir = fs::path(home) / ".config" / "btop";
}
if (error) {
fmt::print(stderr, "\033[0;31mWarning: \033[0m{} could not be accessed: {}\n", config_dir.string(), error.message());
config_dir = "";
}
}
}
// FIXME: This warnings can be noisy if the user deliberately has a non-writable config dir
// offer an alternative | disable messages by default | disable messages if config dir is not writable | disable messages with a flag
// FIXME: Make happy path not branch
if (not config_dir.empty()) {
std::error_code error;
if (fs::exists(config_dir, error)) {
if (fs::is_directory(config_dir, error)) {
struct statvfs stats {};
if ((fs::status(config_dir, error).permissions() & fs::perms::owner_write) == fs::perms::owner_write and
statvfs(config_dir.c_str(), &stats) == 0 and (stats.f_flag & ST_RDONLY) == 0) {
return config_dir;
} else {
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` is not writable\n", fs::absolute(config_dir).string());
// If the config is readable we can still use the provided config, but changes will not be persistent
if ((fs::status(config_dir, error).permissions() & fs::perms::owner_read) == fs::perms::owner_read) {
fmt::print(stderr, "\033[0;31mWarning: \033[0mLogging is disabled, config changes are not persistent\n");
return config_dir;
}
}
} else {
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` is not a directory\n", fs::absolute(config_dir).string());
}
} else {
// Doesn't exist
if (fs::create_directories(config_dir, error)) {
return config_dir;
} else {
fmt::print(stderr, "\033[0;31mWarning: \033[0m`{}` could not be created: {}\n", fs::absolute(config_dir).string(), error.message());
}
}
} else {
fmt::print(stderr, "\033[0;31mWarning: \033[0mCould not determine config path: Make sure `$XDG_CONFIG_HOME` or `$HOME` is set\n");
}
fmt::print(stderr, "\033[0;31mWarning: \033[0mLogging is disabled, config changes are not persistent\n");
return {};
}
bool _locked(const std::string_view name) {
atomic_wait(writelock, true);
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
write_new = true;
return locked.load();
}
fs::path conf_dir;
fs::path conf_file;
vector<string> available_batteries = {"Auto"};
vector<string> current_boxes;
vector<string> preset_list = {"cpu:0:default,mem:0:default,net:0:default,proc:0:default"};
int current_preset = -1;
bool presetsValid(const string& presets) {
vector<string> new_presets = {preset_list.at(0)};
for (int x = 0; const auto& preset : ssplit(presets)) {
if (++x > 9) {
validError = "Too many presets entered!";
return false;
}
for (int y = 0; const auto& box : ssplit(preset, ',')) {
if (++y > 4) {
validError = "Too many boxes entered for preset!";
return false;
}
const auto& vals = ssplit(box, ':');
if (vals.size() != 3) {
validError = "Malformatted preset in config value presets!";
return false;
}
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;
}
if (not is_in(vals.at(1), "0", "1")) {
validError = "Invalid position value in config value presets!";
return false;
}
if (not v_contains(valid_graph_symbols_def, vals.at(2))) {
validError = "Invalid graph name in config value presets!";
return false;
}
}
new_presets.push_back(preset);
}
preset_list = std::move(new_presets);
return true;
}
//* Apply selected preset
bool apply_preset(const string& preset) {
string boxes;
for (const auto& box : ssplit(preset, ',')) {
const auto& vals = ssplit(box, ':');
boxes += vals.at(0) + ' ';
}
if (not boxes.empty()) boxes.pop_back();
auto min_size = Term::get_min_size(boxes);
if (Term::width < min_size.at(0) or Term::height < min_size.at(1)) {
return false;
}
for (const auto& box : ssplit(preset, ',')) {
const auto& vals = ssplit(box, ':');
if (vals.at(0) == "cpu") {
set("cpu_bottom", (vals.at(1) != "0"));
} else if (vals.at(0) == "mem") {
set("mem_below_net", (vals.at(1) != "0"));
} else if (vals.at(0) == "proc") {
set("proc_left", (vals.at(1) != "0"));
}
if (vals.at(0).starts_with("gpu")) {
set("graph_symbol_gpu", vals.at(2));
} else {
set(strings.find("graph_symbol_" + vals.at(0))->first, vals.at(2));
}
}
if (set_boxes(boxes)) {
set("shown_boxes", boxes);
return true;
}
return false;
}
void lock() {
atomic_wait(writelock);
locked = true;
}
string validError;
bool intValid(const std::string_view name, const string& value) {
int i_value;
try {
i_value = stoi(value);
}
catch (const std::invalid_argument&) {
validError = "Invalid numerical value!";
return false;
}
catch (const std::out_of_range&) {
validError = "Value out of range!";
return false;
}
catch (const std::exception& e) {
validError = string{e.what()};
return false;
}
if (name == "update_ms" and i_value < 100)
validError = "Config value update_ms set too low (<100).";
else if (name == "update_ms" and i_value > ONE_DAY_MILLIS)
validError = fmt::format("Config value update_ms set too high (>{}).", ONE_DAY_MILLIS);
else
return true;
return false;
}
bool validBoxSizes(const string& boxes) {
auto min_size = Term::get_min_size(boxes);
return (Term::width >= min_size.at(0) and Term::height >= min_size.at(1));
}
bool stringValid(const std::string_view name, const string& value) {
if (name == "log_level" and not v_contains(Logger::log_levels, value))
validError = "Invalid log_level: " + value;
else if (name == "graph_symbol" and not v_contains(valid_graph_symbols, value))
validError = "Invalid graph symbol identifier: " + value;
else if (name.starts_with("graph_symbol_") and (value != "default" and not v_contains(valid_graph_symbols, value)))
validError = fmt::format("Invalid graph symbol identifier for {}: {}", name, value);
else if (name == "shown_boxes" and not Global::init_conf) {
if (value.empty())
validError = "No boxes selected!";
else if (not validBoxSizes(value))
validError = "Terminal too small to display entered boxes!";
else if (not set_boxes(value))
validError = "Invalid box name(s) in shown_boxes!";
else
return true;
}
#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;
else if (name == "cpu_core_map") {
const auto maps = ssplit(value);
bool all_good = true;
for (const auto& map : maps) {
const auto map_split = ssplit(map, ':');
if (map_split.size() != 2)
all_good = false;
else if (not isint(map_split.at(0)) or not isint(map_split.at(1)))
all_good = false;
if (not all_good) {
validError = "Invalid formatting of cpu_core_map!";
return false;
}
}
return true;
}
else if (name == "io_graph_speeds") {
const auto maps = ssplit(value);
bool all_good = true;
for (const auto& map : maps) {
const auto map_split = ssplit(map, ':');
if (map_split.size() != 2)
all_good = false;
else if (map_split.at(0).empty() or not isint(map_split.at(1)))
all_good = false;
if (not all_good) {
validError = "Invalid formatting of io_graph_speeds!";
return false;
}
}
return true;
}
else
return true;
return false;
}
string getAsString(const std::string_view name) {
if (auto it = bools.find(name); it != bools.end())
return it->second ? "True" : "False";
if (auto it = ints.find(name); it != ints.end())
return to_string(it->second);
if (auto it = strings.find(name); it != strings.end())
return it->second;
return "";
}
void flip(const std::string_view name) {
if (_locked(name)) {
if (boolsTmp.contains(name)) boolsTmp.at(name) = not boolsTmp.at(name);
else boolsTmp.insert_or_assign(name, (not bools.at(name)));
}
else bools.at(name) = not bools.at(name);
}
void unlock() {
if (not locked) return;
atomic_wait(Runner::active);
atomic_lock lck(writelock, true);
try {
if (Proc::shown) {
ints.at("selected_pid") = Proc::selected_pid;
strings.at("selected_name") = Proc::selected_name;
ints.at("proc_start") = Proc::start;
ints.at("proc_selected") = Proc::selected;
ints.at("selected_depth") = Proc::selected_depth;
}
for (auto& item : stringsTmp) {
strings.at(item.first) = item.second;
}
stringsTmp.clear();
for (auto& item : intsTmp) {
ints.at(item.first) = item.second;
}
intsTmp.clear();
for (auto& item : boolsTmp) {
bools.at(item.first) = item.second;
}
boolsTmp.clear();
}
catch (const std::exception& e) {
Global::exit_error_msg = "Exception during Config::unlock() : " + string{e.what()};
clean_quit(1);
}
locked = false;
}
bool set_boxes(const string& boxes) {
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")) {
int gpu_num = stoi(box.substr(3)) + 1;
if (gpu_num > Gpu::count) return false;
}
#endif
}
current_boxes = std::move(new_boxes);
return true;
}
bool toggle_box(const string& box) {
auto old_boxes = current_boxes;
auto box_pos = rng::find(current_boxes, box);
if (box_pos == current_boxes.end())
current_boxes.push_back(box);
else
current_boxes.erase(box_pos);
string new_boxes;
if (not current_boxes.empty()) {
for (const auto& b : current_boxes) new_boxes += b + ' ';
new_boxes.pop_back();
}
auto min_size = Term::get_min_size(new_boxes);
if (Term::width < min_size.at(0) or Term::height < min_size.at(1)) {
current_boxes = old_boxes;
return false;
}
Config::set("shown_boxes", new_boxes);
return true;
}
void load(const fs::path& conf_file, vector<string>& load_warnings) {
std::error_code error;
if (conf_file.empty())
return;
else if (not fs::exists(conf_file, error)) {
write_new = true;
return;
}
if (error) {
return;
}
std::ifstream cread(conf_file);
if (cread.good()) {
vector<string> valid_names;
valid_names.reserve(descriptions.size());
for (const auto &n : descriptions)
valid_names.push_back(n[0]);
if (string v_string; cread.peek() != '#' or (getline(cread, v_string, '\n') and not v_string.contains(Global::Version)))
write_new = true;
while (not cread.eof()) {
cread >> std::ws;
if (cread.peek() == '#') {
cread.ignore(SSmax, '\n');
continue;
}
string name, value;
getline(cread, name, '=');
if (name.ends_with(' ')) name = trim(name);
if (not v_contains(valid_names, name)) {
cread.ignore(SSmax, '\n');
continue;
}
cread >> std::ws;
if (bools.contains(name)) {
cread >> value;
if (not isbool(value))
load_warnings.push_back("Got an invalid bool value for config name: " + name);
else
bools.at(name) = stobool(value);
}
else if (ints.contains(name)) {
cread >> value;
if (not isint(value))
load_warnings.push_back("Got an invalid integer value for config name: " + name);
else if (not intValid(name, value)) {
load_warnings.push_back(validError);
}
else
ints.at(name) = stoi(value);
}
else if (strings.contains(name)) {
if (cread.peek() == '"') {
cread.ignore(1);
getline(cread, value, '"');
}
else cread >> value;
if (not stringValid(name, value))
load_warnings.push_back(validError);
else
strings.at(name) = value;
}
cread.ignore(SSmax, '\n');
}
if (not load_warnings.empty()) write_new = true;
}
}
void write() {
if (conf_file.empty() or not write_new) return;
Logger::debug("Writing new config file");
if (geteuid() != Global::real_uid and seteuid(Global::real_uid) != 0) return;
std::ofstream cwrite(conf_file, std::ios::trunc);
cwrite.imbue(std::locale::classic());
if (cwrite.good()) {
cwrite << "#? Config file for btop v. " << Global::Version << "\n";
for (const auto& [name, description] : descriptions) {
cwrite << "\n" << (description.empty() ? "" : description + "\n")
<< name << " = ";
if (strings.contains(name))
cwrite << "\"" << strings.at(name) << "\"";
else if (ints.contains(name))
cwrite << ints.at(name);
else if (bools.contains(name))
cwrite << (bools.at(name) ? "True" : "False");
cwrite << "\n";
}
}
}
static constexpr auto get_xdg_state_dir() -> std::optional<fs::path> {
std::optional<fs::path> xdg_state_home;
{
const auto* xdg_state_home_ptr = std::getenv("XDG_STATE_HOME");
if (xdg_state_home_ptr != nullptr) {
xdg_state_home = std::make_optional(fs::path(xdg_state_home_ptr));
} else {
const auto* home_ptr = std::getenv("HOME");
if (home_ptr != nullptr) {
xdg_state_home = std::make_optional(fs::path(home_ptr) / ".local" / "state");
}
}
}
if (xdg_state_home.has_value()) {
std::error_code err;
fs::create_directories(xdg_state_home.value(), err);
if (err) {
return std::nullopt;
}
return xdg_state_home;
}
return std::nullopt;
}
auto get_log_file() -> std::optional<fs::path> {
return get_xdg_state_dir().transform([](auto&& state_home) -> auto { return state_home / "btop.log"; });
}
}
@@ -0,0 +1,135 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#include <filesystem>
#include <optional>
#include <string>
#include <vector>
#include <unordered_map>
using std::string;
using std::vector;
//* Functions and variables for reading and writing the btop config file
namespace Config {
extern std::filesystem::path conf_dir;
extern std::filesystem::path conf_file;
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"
#ifdef GPU_SUPPORT
,"gpu0", "gpu1", "gpu2", "gpu3", "gpu4", "gpu5"
#endif
};
const vector<string> temp_scales = { "celsius", "fahrenheit", "kelvin", "rankine" };
#ifdef __linux__
const vector<string> freq_modes = { "first", "range", "lowest", "highest", "average" };
#endif
#ifdef GPU_SUPPORT
const vector<string> show_gpu_values = { "Auto", "On", "Off" };
#endif
const vector<string> base_10_bitrate_values = { "Auto", "True", "False" };
extern vector<string> current_boxes;
extern vector<string> preset_list;
extern vector<string> available_batteries;
extern int current_preset;
constexpr int ONE_DAY_MILLIS = 1000 * 60 * 60 * 24;
[[nodiscard]] std::optional<std::filesystem::path> get_config_dir() noexcept;
//* Check if string only contains space separated valid names for boxes and set current_boxes
bool set_boxes(const string& boxes);
bool validBoxSizes(const string& boxes);
//* Toggle box and update config string shown_boxes
bool toggle_box(const string& box);
//* Parse and setup config value presets
bool presetsValid(const string& presets);
//* Apply selected preset
bool apply_preset(const string& preset);
bool _locked(const std::string_view name);
//* Return bool for config key <name>
inline bool getB(const std::string_view name) { return bools.at(name); }
//* Return integer for config key <name>
inline const int& getI(const std::string_view name) { return ints.at(name); }
//* Return string for config key <name>
inline const string& getS(const std::string_view name) { return strings.at(name); }
string getAsString(const std::string_view name);
extern string validError;
bool intValid(const std::string_view name, const string& value);
bool stringValid(const std::string_view name, const string& value);
//* Set config key <name> to bool <value>
inline void set(const std::string_view name, bool value) {
if (_locked(name)) boolsTmp.insert_or_assign(name, value);
else bools.at(name) = value;
}
//* Set config key <name> to int <value>
inline void set(const std::string_view name, const int value) {
if (_locked(name)) intsTmp.insert_or_assign(name, value);
else ints.at(name) = value;
}
//* Set config key <name> to string <value>
inline void set(const std::string_view name, const string& value) {
if (_locked(name)) stringsTmp.insert_or_assign(name, value);
else strings.at(name) = value;
}
//* Flip config key bool <name>
void flip(const std::string_view name);
//* Lock config and cache changes until unlocked
void lock();
//* Unlock config and write any cached values to config
void unlock();
//* Load the config file from disk
void load(const std::filesystem::path& conf_file, vector<string>& load_warnings);
//* Write the config file to disk
void write();
auto get_log_file() -> std::optional<std::filesystem::path>;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,141 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#include <array>
#include <deque>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
using std::array;
using std::deque;
using std::string;
using std::vector;
namespace Symbols {
const string h_line = "";
const string v_line = "";
const string dotted_v_line = "";
const string left_up = "";
const string right_up = "";
const string left_down = "";
const string right_down = "";
const string round_left_up = "";
const string round_right_up = "";
const string round_left_down = "";
const string round_right_down = "";
const string title_left_down = "";
const string title_right_down = "";
const string title_left = "";
const string title_right = "";
const string div_right = "";
const string div_left = "";
const string div_up = "";
const string div_down = "";
const string up = "";
const string down = "";
const string left = "";
const string right = "";
const string enter = "";
}
namespace Draw {
//* Generate if needed and return the btop++ banner
string banner_gen(int y=0, int x=0, bool centered=false, bool redraw=false);
//* An editable text field
class TextEdit {
size_t pos{};
size_t upos{};
bool numeric = false;
public:
string text;
TextEdit();
explicit TextEdit(string text, bool numeric=false);
bool command(const std::string_view key);
string operator()(const size_t limit=0);
void clear();
};
//* Create a box and return as a string
string createBox(
const int x, const int y, const int width, const int height, string line_color = "", bool fill = false,
const std::string_view title = "", const std::string_view title2 = "", const int num = 0
);
bool update_clock(bool force = false);
//* Class holding a percentage meter
class Meter {
int width;
string color_gradient;
bool invert;
array<string, 101> cache;
public:
Meter();
Meter(const int width, string color_gradient, bool invert = false);
//* Return a string representation of the meter with given value
string operator()(int value);
};
//* Class holding a percentage graph
class Graph {
int width, height;
string color_gradient;
string out, symbol = "default";
bool invert, no_zero;
long long offset;
long long last = 0, max_value = 0;
bool current = true, tty_mode = 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);
public:
Graph();
Graph(int width, int height,
const string& color_gradient,
const deque<long long>& data,
const string& symbol="default",
bool invert=false, bool no_zero=false,
long long max_value=0, long long offset=0);
//* Add last value from back of <data> and return string representation of graph
string& operator()(const deque<long long>& data, bool data_same=false);
//* Return string representation of graph
string& operator()();
};
//* Calculate sizes of boxes, draw outlines and save to enabled boxes namespaces
void calcSizes();
}
namespace Proc {
extern Draw::TextEdit filter;
extern std::unordered_map<size_t, Draw::Graph> p_graphs;
extern std::unordered_map<size_t, int> p_counters;
}
@@ -0,0 +1,542 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <limits>
#include <ranges>
#include <vector>
#include <thread>
#include <mutex>
#include <fmt/format.h>
#include <signal.h>
#include <sys/select.h>
#include <utility>
#include "btop_input.hpp"
#include "btop_tools.hpp"
#include "btop_config.hpp"
#include "btop_shared.hpp"
#include "btop_menu.hpp"
#include "btop_draw.hpp"
using namespace Tools;
using namespace std::literals; // for operator""s
namespace rng = std::ranges;
namespace Input {
//* Map for translating key codes to readable values
const std::unordered_map<string, string> Key_escapes = {
{"\033", "escape"},
{"\x12", "ctrl_r"},
{"\n", "enter"},
{" ", "space"},
{"\x7f", "backspace"},
{"\x08", "backspace"},
{"[A", "up"},
{"OA", "up"},
{"[B", "down"},
{"OB", "down"},
{"[D", "left"},
{"OD", "left"},
{"[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"},
{"[Z", "shift_tab"},
{"OP", "f1"},
{"OQ", "f2"},
{"OR", "f3"},
{"OS", "f4"},
{"[15~", "f5"},
{"[17~", "f6"},
{"[18~", "f7"},
{"[19~", "f8"},
{"[20~", "f9"},
{"[21~", "f10"},
{"[23~", "f11"},
{"[24~", "f12"}
};
sigset_t signal_mask;
std::atomic<bool> polling (false);
array<int, 2> mouse_pos;
std::unordered_map<string, Mouse_loc> mouse_mappings;
deque<string> history(50, "");
string old_filter;
string input;
bool poll(const uint64_t timeout) {
atomic_lock lck(polling);
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
struct timespec wait;
struct timespec *waitptr = nullptr;
if(timeout != std::numeric_limits<uint64_t>::max()) {
wait.tv_sec = timeout / 1000;
wait.tv_nsec = (timeout % 1000) * 1000000;
waitptr = &wait;
}
if(pselect(STDIN_FILENO + 1, &fds, nullptr, nullptr, waitptr, &signal_mask) > 0) {
input.clear();
char buf[1024];
ssize_t count = 0;
while((count = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
input.append(std::string_view(buf, count));
}
return true;
}
return false;
}
string get() {
string key = input;
if (not key.empty()) {
//? Remove escape code prefix if present
if (key.length() > 1 and key.at(0) == Fx::e.at(0)) {
key.erase(0, 1);
}
//? Detect if input is an mouse event
if (key.starts_with("[<")) {
std::string_view key_view = key;
string mouse_event;
if (key_view.starts_with("[<0;") and key_view.find('M') != std::string_view::npos) {
mouse_event = "mouse_click";
key_view.remove_prefix(4);
}
// else if (key_view.starts_with("[<0;") and key_view.ends_with('m')) {
// mouse_event = "mouse_release";
// key_view.remove_prefix(4);
// }
else if (key_view.starts_with("[<64;")) {
mouse_event = "mouse_scroll_up";
key_view.remove_prefix(5);
}
else if (key_view.starts_with("[<65;")) {
mouse_event = "mouse_scroll_down";
key_view.remove_prefix(5);
}
else
key.clear();
if (Config::getB("proc_filtering")) {
if (mouse_event == "mouse_click") return mouse_event;
else return "";
}
//? Get column and line position of mouse and check for any actions mapped to current position
if (not key.empty()) {
try {
const auto delim = key_view.find(';');
mouse_pos[0] = stoi((string)key_view.substr(0, delim));
mouse_pos[1] = stoi((string)key_view.substr(delim + 1, key_view.find('M', delim)));
}
catch (const std::invalid_argument&) { mouse_event.clear(); }
catch (const std::out_of_range&) { mouse_event.clear(); }
key = mouse_event;
if (key == "mouse_click") {
const auto& [col, line] = mouse_pos;
for (const auto& [mapped_key, pos] : (Menu::active ? Menu::mouse_mappings : mouse_mappings)) {
if (col >= pos.col and col < pos.col + pos.width and line >= pos.line and line < pos.line + pos.height) {
key = mapped_key;
break;
}
}
}
}
}
else if (auto it = Key_escapes.find(key); it != Key_escapes.end())
key = it->second;
else if (ulen(key) > 1)
key.clear();
if (not key.empty()) {
history.push_back(key);
history.pop_front();
}
}
return key;
}
string wait() {
while(not poll(std::numeric_limits<uint64_t>::max())) {}
return get();
}
void interrupt() {
kill(getpid(), SIGUSR1);
}
void clear() {
// do not need it, actually
}
void process(const std::string_view key) {
if (key.empty()) return;
try {
auto filtering = Config::getB("proc_filtering");
auto vim_keys = Config::getB("vim_keys");
auto help_key = (vim_keys ? "H" : "h");
auto kill_key = (vim_keys ? "K" : "k");
//? Global input actions
if (not filtering) {
bool keep_going = false;
if (key == "q") {
clean_quit(0);
}
else if (is_in(key, "escape", "m")) {
Menu::show(Menu::Menus::Main);
return;
}
else if (is_in(key, "f1", "?", help_key)) {
Menu::show(Menu::Menus::Help);
return;
}
else if (is_in(key, "f2", "o")) {
Menu::show(Menu::Menus::Options);
return;
}
else if (key.size() == 1 and isint(key)) {
auto intKey = std::atoi(key.data());
// AltData: 1=net, 2=proc,其他屏蔽
if (intKey != 1 and intKey != 2)
return;
// 映射: 1->net(3), 2->proc(4)
static const array<string, 3> altBoxes = {"", "net", "proc"};
atomic_wait(Runner::active);
if (not Config::toggle_box(altBoxes.at(intKey))) {
Menu::show(Menu::Menus::SizeError);
return;
}
Config::current_preset = -1;
Draw::calcSizes();
Runner::run("all", false, true);
return;
}
else if (is_in(key, "p", "P") and Config::preset_list.size() > 1) {
const auto old_preset = Config::current_preset;
if (key == "p") {
if (++Config::current_preset >= (int)Config::preset_list.size()) Config::current_preset = 0;
}
else {
if (--Config::current_preset < 0) Config::current_preset = Config::preset_list.size() - 1;
}
atomic_wait(Runner::active);
if (not Config::apply_preset(Config::preset_list.at(Config::current_preset))) {
Menu::show(Menu::Menus::SizeError);
Config::current_preset = old_preset;
return;
}
Draw::calcSizes();
Runner::run("all", false, true);
return;
} else if (is_in(key, "ctrl_r")) {
kill(getpid(), SIGUSR2);
return;
} else
keep_going = true;
if (not keep_going) return;
}
//? Input actions for proc box
if (Proc::shown) {
bool keep_going = false;
bool no_update = true;
bool redraw = true;
if (filtering) {
if (key == "enter" or key == "down") {
Config::set("proc_filter", Proc::filter.text);
Config::set("proc_filtering", false);
old_filter.clear();
if(key == "down"){
Config::unlock();
Config::lock();
process("down");
return;
}
}
else if (key == "escape" or key == "mouse_click") {
Config::set("proc_filter", old_filter);
Config::set("proc_filtering", false);
old_filter.clear();
}
else if (Proc::filter.command(key)) {
if (Config::getS("proc_filter") != Proc::filter.text)
Config::set("proc_filter", Proc::filter.text);
}
else
return;
}
else if (key == "left" or (vim_keys and key == "h")) {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (--cur_i < 0)
cur_i = Proc::sort_vector.size() - 1;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "right" or (vim_keys and key == "l")) {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (std::cmp_greater(++cur_i, Proc::sort_vector.size() - 1))
cur_i = 0;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (is_in(key, "f", "/")) {
Config::flip("proc_filtering");
Proc::filter = Draw::TextEdit{Config::getS("proc_filter")};
old_filter = Proc::filter.text;
}
else if (key == "e") {
Config::flip("proc_tree");
no_update = false;
}
else if (is_in(key, "F")) {
Config::flip("pause_proc_list");
redraw = true;
}
else if (key == "r")
Config::flip("proc_reversed");
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);
else if (key.starts_with("mouse_")) {
redraw = false;
const auto& [col, line] = mouse_pos;
const int y = (Config::getB("show_detailed") ? Proc::y + 8 : Proc::y);
const int height = (Config::getB("show_detailed") ? Proc::height - 8 : Proc::height);
if (col >= Proc::x + 1 and col < Proc::x + Proc::width and line >= y + 1 and line < y + height - 1) {
if (key == "mouse_click") {
if (col < Proc::x + Proc::width - 2) {
const auto& current_selection = Config::getI("proc_selected");
if (current_selection == line - y - 1) {
redraw = true;
if (Config::getB("proc_tree")) {
const int x_pos = col - Proc::x;
const int offset = Config::getI("selected_depth") * 3;
if (x_pos > offset and x_pos < 4 + offset) {
process("space");
return;
}
}
// AltData: disable enter-triggered detail toggle
return;
}
else if (current_selection == 0 or line - y - 1 == 0)
redraw = true;
Config::set("proc_selected", line - y - 1);
}
else if (line == y + 1) {
if (Proc::selection("page_up") == -1) return;
}
else if (line == y + height - 2) {
if (Proc::selection("page_down") == -1) return;
}
else if (Proc::selection("mousey" + to_string(line - y - 2)) == -1)
return;
}
else
goto proc_mouse_scroll;
}
else if (key == "mouse_click" and Config::getI("proc_selected") > 0) {
Config::set("proc_selected", 0);
redraw = true;
}
else
keep_going = true;
}
else if (key == "enter") {
// AltData: disable enter-triggered detail toggle
return;
}
else if (is_in(key, "+", "-", "space", "u") and Config::getB("proc_tree") and Config::getI("proc_selected") > 0) {
atomic_wait(Runner::active);
auto& pid = Config::getI("selected_pid");
if (key == "+" or key == "space") Proc::expand = pid;
if (key == "-" or key == "space") Proc::collapse = pid;
if (key == "u") Proc::toggle_children = pid;
no_update = false;
}
else if (is_in(key, "t", kill_key) and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
atomic_wait(Runner::active);
if (Config::getB("show_detailed") and Config::getI("proc_selected") == 0 and Proc::detailed.status == "Dead") return;
Menu::show(Menu::Menus::SignalSend, (key == "t" ? SIGTERM : SIGKILL));
return;
}
else if (key == "s" and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
atomic_wait(Runner::active);
if (Config::getB("show_detailed") and Config::getI("proc_selected") == 0 and Proc::detailed.status == "Dead") return;
Menu::show(Menu::Menus::SignalChoose);
return;
}
else if (key == "N" and (Config::getB("show_detailed") or Config::getI("selected_pid") > 0)) {
atomic_wait(Runner::active);
if (Config::getB("show_detailed") and Config::getI("proc_selected") == 0 and Proc::detailed.status == "Dead") return;
Menu::show(Menu::Menus::Renice);
return;
}
else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end") or (vim_keys and is_in(key, "j", "k", "g", "G"))) {
proc_mouse_scroll:
redraw = false;
auto old_selected = Config::getI("proc_selected");
auto new_selected = Proc::selection(key);
if (new_selected == -1)
return;
else if (old_selected != new_selected and (old_selected == 0 or new_selected == 0))
redraw = true;
}
else keep_going = true;
if (not keep_going) {
Runner::run("proc", no_update, redraw);
return;
}
}
//? Input actions for cpu box
if (Cpu::shown) {
bool keep_going = false;
bool no_update = true;
bool redraw = true;
static uint64_t last_press = 0;
if (key == "+" and Config::getI("update_ms") <= 86399900) {
int add = (Config::getI("update_ms") <= 86399000 and last_press >= time_ms() - 200
and rng::all_of(Input::history, [](const auto& str){ return str == "+"; })
? 1000 : 100);
Config::set("update_ms", Config::getI("update_ms") + add);
last_press = time_ms();
redraw = true;
}
else if (key == "-" and Config::getI("update_ms") >= 200) {
int sub = (Config::getI("update_ms") >= 2000 and last_press >= time_ms() - 200
and rng::all_of(Input::history, [](const auto& str){ return str == "-"; })
? 1000 : 100);
Config::set("update_ms", Config::getI("update_ms") - sub);
last_press = time_ms();
redraw = true;
}
else keep_going = true;
if (not keep_going) {
Runner::run("cpu", no_update, redraw);
return;
}
}
//? Input actions for mem box
if (Mem::shown) {
bool keep_going = false;
bool no_update = true;
bool redraw = true;
if (key == "i") {
Config::flip("io_mode");
}
else if (key == "d") {
Config::flip("show_disks");
no_update = false;
Draw::calcSizes();
}
else keep_going = true;
if (not keep_going) {
Runner::run("mem", no_update, redraw);
return;
}
}
//? Input actions for net box
if (Net::shown) {
bool keep_going = false;
bool no_update = true;
bool redraw = true;
if (is_in(key, "b", "n")) {
atomic_wait(Runner::active);
int c_index = v_index(Net::interfaces, Net::selected_iface);
if (c_index != (int)Net::interfaces.size()) {
if (key == "b") {
if (--c_index < 0) c_index = Net::interfaces.size() - 1;
}
else if (key == "n") {
if (++c_index == (int)Net::interfaces.size()) c_index = 0;
}
Net::selected_iface = Net::interfaces.at(c_index);
Net::rescale = true;
}
}
else if (key == "y") {
Config::flip("net_sync");
Net::rescale = true;
}
else if (key == "a") {
Config::flip("net_auto");
Net::rescale = true;
}
else if (key == "z") {
atomic_wait(Runner::active);
auto& ndev = Net::current_net.at(Net::selected_iface);
if (ndev.stat.at("download").offset + ndev.stat.at("upload").offset > 0) {
ndev.stat.at("download").offset = 0;
ndev.stat.at("upload").offset = 0;
}
else {
ndev.stat.at("download").offset = ndev.stat.at("download").last + ndev.stat.at("download").rollover;
ndev.stat.at("upload").offset = ndev.stat.at("upload").last + ndev.stat.at("upload").rollover;
}
no_update = false;
}
else keep_going = true;
if (not keep_going) {
Runner::run("net", no_update, redraw);
return;
}
}
}
catch (const std::exception& e) {
throw std::runtime_error { fmt::format(R"(Input::process("{}"))", e.what()) };
}
}
}
@@ -0,0 +1,78 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#include <array>
#include <atomic>
#include <deque>
#include <string>
#include <string_view>
#include <unordered_map>
using std::array;
using std::atomic;
using std::deque;
using std::string;
/* The input functions rely on the following termios parameters being set:
Non-canonical mode (c_lflags & ~(ICANON))
VMIN and VTIME (c_cc) set to 0
These will automatically be set when running Term::init() from btop_tools.cpp
*/
//* Functions and variables for handling keyboard and mouse input
namespace Input {
struct Mouse_loc {
int line, col, height, width;
};
//? line, col, height, width
extern std::unordered_map<string, Mouse_loc> mouse_mappings;
//* Signal mask used during polling read
extern sigset_t signal_mask;
extern atomic<bool> polling;
//* Mouse column and line position
extern array<int, 2> mouse_pos;
//* Last entered key
extern deque<string> history;
//* Poll keyboard & mouse input for <timeout> ms and return input availability as a bool
bool poll(const uint64_t timeout=0);
//* Get a key or mouse action from input
string get();
//* Wait until input is available and return key
string wait();
//* Interrupt poll/wait
void interrupt();
//* Clears last entered key
void clear();
//* Process actions for input <key>
void process(const std::string_view key);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,99 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#include <atomic>
#include <bitset>
#include <string>
#include <string_view>
#include <vector>
#include "btop_input.hpp"
using std::atomic;
using std::bitset;
using std::string;
using std::vector;
namespace Menu {
extern atomic<bool> active;
extern string output;
extern int signalToSend;
extern bool redraw;
//? line, col, height, width
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
//? Boxtypes: 0 = OK button | 1 = YES and NO with YES selected | 2 = Same as 1 but with NO selected
//? Strings in content vector is not checked for box width overflow
class msgBox {
string box_contents, button_left, button_right;
int height{};
int width{};
int boxtype{};
int selected{};
int x{};
int y{};
public:
enum BoxTypes { OK, YES_NO, NO_YES };
enum msgReturn {
Invalid,
Ok_Yes,
No_Esc,
Select
};
msgBox();
msgBox(int width, int boxtype, const vector<string>& content, std::string_view title);
//? Draw and return box as a string
string operator()();
//? Process input and returns value from enum Ret
int input(const string& key);
//? Clears content vector and private strings
void clear();
int getX() const { return x; }
int getY() const { return y; }
};
extern bitset<8> menuMask;
//* Enum for functions in vector menuFuncs
enum Menus {
SizeError,
SignalChoose,
SignalSend,
SignalReturn,
Options,
Help,
Renice,
Main
};
//* Handles redirection of input for menu functions and handles return codes
void process(const std::string_view key = "");
//* Show a menu from enum Menu::Menus
void show(int menu, int signal=-1);
}
@@ -0,0 +1,292 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <sys/resource.h>
#include <filesystem>
#include <fstream>
#include <ranges>
#include <regex>
#include <string>
#include "btop_config.hpp"
#include "btop_shared.hpp"
#include "btop_tools.hpp"
namespace fs = std::filesystem;
namespace rng = std::ranges;
using namespace Tools;
namespace Cpu {
std::optional<std::string> container_engine;
string trim_name(string name) {
auto name_vec = ssplit(name);
if ((name.contains("Xeon") or v_contains(name_vec, "Duo"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(')'))
name = name_vec.at(cpu_pos + 1);
else
name.clear();
} else if (v_contains(name_vec, "Ryzen"s)) {
auto ryz_pos = v_index(name_vec, "Ryzen"s);
name = "Ryzen";
int tokens = 0;
for (auto i = ryz_pos + 1; i < name_vec.size() && tokens < 2; i++) {
const std::string& p = name_vec.at(i);
if (p != "AI" && p != "PRO" && p != "H" && p != "HX")
tokens++;
name += " " + p;
}
} else if (name.contains("Intel") 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) != "@")
name = name_vec.at(cpu_pos + 1);
else
name.clear();
} else
name.clear();
if (name.empty() and not name_vec.empty()) {
for (const auto &n : name_vec) {
if (n == "@") break;
name += n + ' ';
}
name.pop_back();
for (const auto& replace : {"Processor", "CPU", "(R)", "(TM)", "Intel", "AMD", "Apple", "Core"}) {
name = s_replace(name, replace, "");
name = s_replace(name, " ", " ");
}
name = trim(name);
}
return name;
}
}
#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 = 0;
}
#endif
namespace Proc {
bool set_priority(pid_t pid, int priority) {
if (setpriority(PRIO_PROCESS, pid, priority) == 0) {
return true;
}
return false;
}
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(proc_vec, rng::less{}, &proc_info::pid); break;
case 1: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::name); break;
case 2: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cmd); break;
case 3: rng::stable_sort(proc_vec, rng::less{}, &proc_info::threads); break;
case 4: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::user); break;
case 5: rng::stable_sort(proc_vec, rng::less{}, &proc_info::mem); break;
case 6: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cpu_c); break;
}
}
else {
switch (v_index(sort_vector, sorting)) {
case 0: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::pid); break;
case 1: rng::stable_sort(proc_vec, rng::less{}, &proc_info::name); break;
case 2: rng::stable_sort(proc_vec, rng::less{}, &proc_info::cmd); break;
case 3: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::threads); break;
case 4: rng::stable_sort(proc_vec, rng::less{}, &proc_info::user); break;
case 5: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::mem); break;
case 6: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cpu_p); break;
case 7: rng::stable_sort(proc_vec, rng::greater{}, &proc_info::cpu_c); break;
}
}
//* When sorting with "cpu lazy" push processes over threshold cpu usage to the front regardless of cumulative usage
if (not tree and not reverse and sorting == "cpu lazy") {
double max = 10.0, target = 30.0;
for (size_t i = 0, x = 0, offset = 0; i < proc_vec.size(); i++) {
if (i <= 5 and proc_vec.at(i).cpu_p > max)
max = proc_vec.at(i).cpu_p;
else if (i == 6)
target = (max > 30.0) ? max : 10.0;
if (i == offset and proc_vec.at(i).cpu_p > 30.0)
offset++;
else if (proc_vec.at(i).cpu_p > target) {
rotate(proc_vec.begin() + offset, proc_vec.begin() + i, proc_vec.begin() + i + 1);
if (++x > 10) break;
}
}
}
}
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, bool reverse, bool paused, int& c_index, const int index_max, bool collapsed) {
if (proc_vec.size() > 1 and not paused) {
if (reverse) {
switch (v_index(sort_vector, sorting)) {
case 3: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().threads < b.entry.get().threads; }); break;
case 5: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().mem < b.entry.get().mem; }); break;
case 6: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_p < b.entry.get().cpu_p; }); break;
case 7: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_c < b.entry.get().cpu_c; }); break;
}
}
else {
switch (v_index(sort_vector, sorting)) {
case 3: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().threads > b.entry.get().threads; }); break;
case 5: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().mem > b.entry.get().mem; }); break;
case 6: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_p > b.entry.get().cpu_p; }); break;
case 7: rng::stable_sort(proc_vec, [](const auto& a, const auto& b) { return a.entry.get().cpu_c > b.entry.get().cpu_c; }); break;
}
}
}
for (auto& r : proc_vec) {
r.entry.get().tree_index = (collapsed or r.entry.get().filtered ? index_max : c_index++);
if (not r.children.empty()) {
tree_sort(r.children, sorting, reverse, paused, c_index, (collapsed or r.entry.get().collapsed or r.entry.get().tree_index == (size_t)index_max));
}
}
}
auto matches_filter(const proc_info& proc, const std::string& filter) -> bool {
if (filter.starts_with("!")) {
if (filter.size() == 1) {
return true;
}
// An incomplete regex throws, see issue https://github.com/aristocratos/btop/issues/1133
try {
std::regex regex { filter.substr(1), std::regex::extended };
return std::regex_search(std::to_string(proc.pid), regex) || std::regex_search(proc.name, regex) ||
std::regex_match(proc.cmd, regex) || std::regex_search(proc.user, regex);
} catch (std::regex_error& /* unused */) {
return false;
}
}
return std::to_string(proc.pid).contains(filter) || s_contains_ic(proc.name, filter) ||
s_contains_ic(proc.cmd, filter) || s_contains_ic(proc.user, filter);
}
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs,
int cur_depth, bool collapsed, const string& filter, bool found, bool no_update, bool should_filter) {
bool filtering = false;
//? If filtering, include children of matching processes
if (not found and (should_filter or not filter.empty())) {
if (!matches_filter(cur_proc, filter)) {
filtering = true;
cur_proc.filtered = true;
filter_found++;
}
else {
found = true;
cur_depth = 0;
}
}
else if (cur_proc.filtered) cur_proc.filtered = false;
cur_proc.depth = cur_depth;
//? Set tree index position for process if not filtered out or currently in a collapsed sub-tree
out_procs.push_back({ cur_proc, {} });
if (not collapsed and not filtering) {
cur_proc.tree_index = out_procs.size() - 1;
//? Try to find name of the binary file and append to program name if not the same
if (cur_proc.short_cmd.empty() and not cur_proc.cmd.empty()) {
std::string_view cmd_view = cur_proc.cmd;
cmd_view = cmd_view.substr((size_t)0, std::min(cmd_view.find(' '), cmd_view.size()));
cmd_view = cmd_view.substr(std::min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
cur_proc.short_cmd = string{cmd_view};
}
}
else {
cur_proc.tree_index = in_procs.size();
}
//? Recursive iteration over all children
for (auto& p : rng::equal_range(in_procs, cur_proc.pid, rng::less{}, &proc_info::ppid)) {
if (collapsed and not filtering) {
cur_proc.filtered = true;
}
_tree_gen(p, in_procs, out_procs.back().children, cur_depth + 1, (collapsed or cur_proc.collapsed), filter, found, no_update, should_filter);
if (not no_update and not filtering and (collapsed or cur_proc.collapsed)) {
//auto& parent = cur_proc;
if (p.state != 'X') {
cur_proc.cpu_p += p.cpu_p;
cur_proc.cpu_c += p.cpu_c;
cur_proc.mem += p.mem;
cur_proc.threads += p.threads;
}
filter_found++;
p.filtered = true;
}
else if (Config::getB("proc_aggregate") and p.state != 'X') {
cur_proc.cpu_p += p.cpu_p;
cur_proc.cpu_c += p.cpu_c;
cur_proc.mem += p.mem;
cur_proc.threads += p.threads;
}
}
}
void _collect_prefixes(tree_proc &t, const bool is_last, const string &header) {
const bool is_filtered = t.entry.get().filtered;
if (is_filtered) t.entry.get().depth = 0;
if (!t.children.empty()) t.entry.get().prefix = header + (t.entry.get().collapsed ? "[+]─": "[-]─");
else t.entry.get().prefix = header + (is_last ? " └─": " ├─");
for (auto child = t.children.begin(); child != t.children.end(); ++child) {
_collect_prefixes(*child, child == (t.children.end() - 1),
is_filtered ? "": header + (is_last ? " ": ""));
}
}
}
auto detect_container() -> std::optional<std::string> {
std::error_code err;
if (fs::exists(fs::path("/run/.containerenv"), err)) {
return std::make_optional(std::string { "podman" });
}
if (fs::exists(fs::path("/.dockerenv"), err)) {
return std::make_optional(std::string { "docker" });
}
auto systemd_container = fs::path("/run/systemd/container");
if (fs::exists(systemd_container, err)) {
auto stream = std::ifstream { systemd_container };
auto buf = std::string {};
stream >> buf;
return std::make_optional(buf);
}
return std::nullopt;
}
@@ -0,0 +1,461 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#include <array>
#include <atomic>
#include <cstdint>
#include <deque>
#include <filesystem>
#include <optional>
#include <string>
#include <string_view>
#include <tuple>
#include <unordered_map>
#include <vector>
#include <unistd.h>
// From `man 3 getifaddrs`: <net/if.h> must be included before <ifaddrs.h>
// clang-format off
#include <net/if.h>
#include <ifaddrs.h>
// clang-format on
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
# include <kvm.h>
#endif
using std::array;
using std::atomic;
using std::deque;
using std::string;
using std::tuple;
using std::vector;
using namespace std::literals; // for operator""s
void term_resize(bool force=false);
void banner_gen();
extern void clean_quit(int sig);
namespace Global {
extern const vector<array<string, 2>> Banner_src;
extern const string Version;
extern atomic<bool> quitting;
extern string exit_error_msg;
extern atomic<bool> thread_exception;
extern string banner;
extern atomic<bool> resized;
extern string overlay;
extern string clock;
extern uid_t real_uid, set_uid;
extern atomic<bool> init_conf;
}
namespace Runner {
extern atomic<bool> active;
extern atomic<bool> reading;
extern atomic<bool> stopping;
extern atomic<bool> redraw;
extern atomic<bool> coreNum_reset;
extern pthread_t runner_id;
extern bool pause_output;
extern string debug_bg;
void run(const string& box="", bool no_update = false, bool force_redraw = false);
void stop();
}
namespace Tools {
//* Platform specific function for system_uptime (seconds since last restart)
double system_uptime();
}
namespace Shared {
//* Initialize platform specific needed variables and check for errors
void init();
extern long coreCount, page_size, clk_tck;
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
struct KvmDeleter {
void operator()(kvm_t* handle) {
kvm_close(handle);
}
};
using KvmPtr = std::unique_ptr<kvm_t, KvmDeleter>;
#endif
}
namespace Gpu {
#ifdef GPU_SUPPORT
extern vector<string> box;
extern int width, total_height, min_width, min_height;
extern vector<int> x_vec, y_vec;
extern vector<bool> redraw;
extern int shown;
extern int count;
extern vector<int> 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,
encoder_utilization = true,
decoder_utilization = 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;
long long encoder_utilization = 0;
long long decoder_utilization = 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;
extern bool shown, redraw, got_sensors, cpu_temp_only, has_battery, supports_watts;
extern string cpuName, cpuHz;
extern vector<string> available_fields;
extern vector<string> available_sensors;
extern tuple<int, float, long, string> current_bat;
extern std::optional<std::string> container_engine;
struct cpu_info {
std::unordered_map<string, deque<long long>> cpu_percent = {
{"total", {}},
{"user", {}},
{"nice", {}},
{"system", {}},
{"idle", {}},
{"iowait", {}},
{"irq", {}},
{"softirq", {}},
{"steal", {}},
{"guest", {}},
{"guest_nice", {}}
};
vector<deque<long long>> core_percent;
vector<deque<long long>> temp;
long long temp_max = 0;
array<double, 3> load_avg;
float usage_watts = 0;
std::optional<std::vector<std::int32_t>> active_cpus;
};
//* Collect cpu stats and temperatures
auto collect(bool no_update = false) -> cpu_info&;
//* Draw contents of cpu box using <cpu> as source
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() -> 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, float, long, string>;
string trim_name(string);
}
namespace Mem {
extern string box;
extern int x, y, width, height, min_width, min_height;
extern bool has_swap, shown, redraw;
const array mem_names { "used"s, "available"s, "cached"s, "free"s };
const array swap_names { "swap_used"s, "swap_free"s };
extern int disk_ios;
struct disk_info {
std::filesystem::path dev;
string name;
string fstype{}; // defaults to ""
std::filesystem::path stat{}; // defaults to ""
int64_t total{};
int64_t used{};
int64_t free{};
int used_percent{};
int free_percent{};
array<int64_t, 3> old_io = {0, 0, 0};
deque<long long> io_read = {};
deque<long long> io_write = {};
deque<long long> io_activity = {};
};
struct mem_info {
std::unordered_map<string, uint64_t> stats =
{{"used", 0}, {"available", 0}, {"cached", 0}, {"free", 0},
{"swap_total", 0}, {"swap_used", 0}, {"swap_free", 0}};
std::unordered_map<string, deque<long long>> percent =
{{"used", {}}, {"available", {}}, {"cached", {}}, {"free", {}},
{"swap_total", {}}, {"swap_used", {}}, {"swap_free", {}}};
std::unordered_map<string, disk_info> disks;
vector<string> disks_order;
};
//?* Get total system memory
uint64_t get_totalMem();
//* Collect mem & disks stats
auto collect(bool no_update = false) -> mem_info&;
//* Draw contents of mem box using <mem> as source
string draw(const mem_info& mem, bool force_redraw = false, bool data_same = false);
}
namespace Net {
extern string box;
extern int x, y, width, height, min_width, min_height;
extern bool shown, redraw;
extern string selected_iface;
extern vector<string> interfaces;
extern bool rescale;
extern std::unordered_map<string, uint64_t> graph_max;
struct net_stat {
uint64_t speed{};
uint64_t top{};
uint64_t total{};
uint64_t last{};
uint64_t offset{};
uint64_t rollover{};
};
struct net_info {
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{};
};
class IfAddrsPtr {
struct ifaddrs* ifaddr;
int status;
public:
IfAddrsPtr() { status = getifaddrs(&ifaddr); }
~IfAddrsPtr() noexcept { freeifaddrs(ifaddr); }
IfAddrsPtr(const IfAddrsPtr &) = delete;
IfAddrsPtr& operator=(IfAddrsPtr& other) = delete;
IfAddrsPtr(IfAddrsPtr &&) = delete;
IfAddrsPtr& operator=(IfAddrsPtr&& other) = delete;
[[nodiscard]] constexpr auto operator()() -> struct ifaddrs* { return ifaddr; }
[[nodiscard]] constexpr auto get() -> struct ifaddrs* { return ifaddr; }
[[nodiscard]] constexpr auto get_status() const noexcept -> int { return status; };
};
extern std::unordered_map<string, net_info> current_net;
//* Collect net upload/download stats
auto collect(bool no_update=false) -> net_info&;
//* Draw contents of net box using <net> as source
string draw(const net_info& net, bool force_redraw = false, bool data_same = false);
}
namespace Proc {
extern atomic<int> numpids;
extern string box;
extern int x, y, width, height, min_width, min_height;
extern bool shown, redraw;
extern int select_max;
extern atomic<int> detailed_pid;
extern int selected_pid, start, selected, collapse, expand, filter_found, selected_depth, toggle_children;
extern string selected_name;
//? Contains the valid sorting options for processes
const vector<string> sort_vector = {
"pid",
"name",
"command",
"threads",
"user",
"memory",
"cpu direct",
"cpu lazy",
};
//? Translation from process state char to explanative string
const std::unordered_map<char, string> proc_states = {
{'R', "Running"},
{'S', "Sleeping"},
{'D', "Waiting"},
{'Z', "Zombie"},
{'T', "Stopped"},
{'t', "Tracing"},
{'X', "Dead"},
{'x', "Dead"},
{'K', "Wakekill"},
{'W', "Unknown"},
{'P', "Parked"}
};
//* Container for process information
struct proc_info {
size_t pid{};
string name{}; // defaults to ""
string cmd{}; // defaults to ""
string short_cmd{}; // defaults to ""
size_t threads{};
int name_offset{};
string user{}; // defaults to ""
uint64_t mem{};
double cpu_p{}; // defaults to = 0.0
double cpu_c{}; // defaults to = 0.0
char state = '0';
int64_t p_nice{};
uint64_t ppid{};
uint64_t cpu_s{};
uint64_t cpu_t{};
uint64_t death_time{};
string prefix{}; // defaults to ""
size_t depth{};
size_t tree_index{};
bool collapsed{};
bool filtered{};
};
//* Container for process info box
struct detail_container {
size_t last_pid{};
bool skip_smaps{};
proc_info entry;
string elapsed, parent, status, io_read, io_write, memory;
long long first_mem = -1;
deque<long long> cpu_percent;
deque<long long> mem_bytes;
};
//? Contains all info for proc detailed box
extern detail_container detailed;
//* Collect and sort process information from /proc
auto collect(bool no_update = false) -> vector<proc_info>&;
//* Update current selection and view, returns -1 if no change otherwise the current selection
int selection(const std::string_view cmd_key);
//* Draw contents of proc box using <plist> as data source
string draw(const vector<proc_info>& plist, bool force_redraw = false, bool data_same = false);
struct tree_proc {
std::reference_wrapper<proc_info> entry;
vector<tree_proc> children;
};
//* Change priority (nice) of pid, returns true on success otherwise false
bool set_priority(pid_t pid, int priority);
//* Sort vector of proc_info's
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree = false);
//* Recursive sort of process tree
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, bool reverse, bool paused,
int& c_index, const int index_max, bool collapsed = false);
auto matches_filter(const proc_info& proc, const std::string& filter) -> bool;
//* Generate process tree list
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs,
int cur_depth, bool collapsed, const string& filter,
bool found = false, bool no_update = false, bool should_filter = false);
//* Build prefixes for tree view
void _collect_prefixes(tree_proc& t, bool is_last, const string &header = "");
}
/// Detect container engine.
auto detect_container() -> std::optional<std::string>;
@@ -0,0 +1,454 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <cmath>
#include <fstream>
#include <unistd.h>
#include "btop_tools.hpp"
#include "btop_config.hpp"
#include "btop_theme.hpp"
using std::round;
using std::stoi;
using std::to_string;
using std::vector;
using std::views::iota;
using namespace Tools;
namespace fs = std::filesystem;
string Term::fg, Term::bg;
string Fx::reset = reset_base;
namespace Theme {
fs::path theme_dir;
fs::path user_theme_dir;
fs::path custom_theme_dir;
vector<string> themes;
std::unordered_map<string, string> colors;
std::unordered_map<string, array<int, 3>> rgbs;
std::unordered_map<string, array<string, 101>> gradients;
const std::unordered_map<string, string> Default_theme = {
{ "main_bg", "#00" },
{ "main_fg", "#cc" },
{ "title", "#ee" },
{ "hi_fg", "#b54040" },
{ "selected_bg", "#6a2f2f" },
{ "selected_fg", "#ee" },
{ "inactive_fg", "#40" },
{ "graph_text", "#60" },
{ "meter_bg", "#40" },
{ "proc_misc", "#0de756" },
{ "cpu_box", "#556d59" },
{ "mem_box", "#6c6c4b" },
{ "net_box", "#5c588d" },
{ "proc_box", "#805252" },
{ "div_line", "#30" },
{ "temp_start", "#4897d4" },
{ "temp_mid", "#5474e8" },
{ "temp_end", "#ff40b6" },
{ "cpu_start", "#77ca9b" },
{ "cpu_mid", "#cbc06c" },
{ "cpu_end", "#dc4c4c" },
{ "free_start", "#384f21" },
{ "free_mid", "#b5e685" },
{ "free_end", "#dcff85" },
{ "cached_start", "#163350" },
{ "cached_mid", "#74e6fc" },
{ "cached_end", "#26c5ff" },
{ "available_start", "#4e3f0e" },
{ "available_mid", "#ffd77a" },
{ "available_end", "#ffb814" },
{ "used_start", "#592b26" },
{ "used_mid", "#d9626d" },
{ "used_end", "#ff4769" },
{ "download_start", "#291f75" },
{ "download_mid", "#4f43a3" },
{ "download_end", "#b0a9de" },
{ "upload_start", "#620665" },
{ "upload_mid", "#7d4180" },
{ "upload_end", "#dcafde" },
{ "process_start", "#80d0a3" },
{ "process_mid", "#dcd179" },
{ "process_end", "#d45454" },
{ "proc_pause_bg", "#b54040" }
};
const std::unordered_map<string, string> TTY_theme = {
{ "main_bg", "\x1b[0;40m" },
{ "main_fg", "\x1b[37m" },
{ "title", "\x1b[97m" },
{ "hi_fg", "\x1b[91m" },
{ "selected_bg", "\x1b[41m" },
{ "selected_fg", "\x1b[97m" },
{ "inactive_fg", "\x1b[90m" },
{ "graph_text", "\x1b[90m" },
{ "meter_bg", "\x1b[90m" },
{ "proc_misc", "\x1b[92m" },
{ "cpu_box", "\x1b[32m" },
{ "mem_box", "\x1b[33m" },
{ "net_box", "\x1b[35m" },
{ "proc_box", "\x1b[31m" },
{ "div_line", "\x1b[90m" },
{ "temp_start", "\x1b[94m" },
{ "temp_mid", "\x1b[96m" },
{ "temp_end", "\x1b[95m" },
{ "cpu_start", "\x1b[92m" },
{ "cpu_mid", "\x1b[93m" },
{ "cpu_end", "\x1b[91m" },
{ "free_start", "\x1b[32m" },
{ "free_mid", "" },
{ "free_end", "\x1b[92m" },
{ "cached_start", "\x1b[36m" },
{ "cached_mid", "" },
{ "cached_end", "\x1b[96m" },
{ "available_start", "\x1b[33m" },
{ "available_mid", "" },
{ "available_end", "\x1b[93m" },
{ "used_start", "\x1b[31m" },
{ "used_mid", "" },
{ "used_end", "\x1b[91m" },
{ "download_start", "\x1b[34m" },
{ "download_mid", "" },
{ "download_end", "\x1b[94m" },
{ "upload_start", "\x1b[35m" },
{ "upload_mid", "" },
{ "upload_end", "\x1b[95m" },
{ "process_start", "\x1b[32m" },
{ "process_mid", "\x1b[33m" },
{ "process_end", "\x1b[31m" },
{ "proc_pause_bg", "\x1b[41m" },
};
namespace {
//* Convert 24-bit colors to 256 colors
int truecolor_to_256(const int& r, const int& g, const int& b) {
//? Use upper 232-255 greyscale values if the downscaled red, green and blue are the same value
if (const int red = round((double)r / 11); red == round((double)g / 11) and red == round((double)b / 11)) {
return 232 + red;
}
//? Else use 6x6x6 color cube to calculate approximate colors
else {
return round((double)r / 51) * 36 + round((double)g / 51) * 6 + round((double)b / 51) + 16;
}
}
}
string hex_to_color(string hexa, bool t_to_256, const string& depth) {
if (hexa.size() > 1) {
hexa.erase(0, 1);
for (auto& c : hexa) {
if (not isxdigit(c)) {
Logger::error("Invalid hex value: " + hexa);
return "";
}
}
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
if (hexa.size() == 2) {
int h_int = stoi(hexa, nullptr, 16);
if (t_to_256) {
return pre + to_string(truecolor_to_256(h_int, h_int, h_int)) + "m";
} else {
string h_str = to_string(h_int);
return pre + h_str + ";" + h_str + ";" + h_str + "m";
}
}
else if (hexa.size() == 6) {
if (t_to_256) {
return pre + to_string(truecolor_to_256(
stoi(hexa.substr(0, 2), nullptr, 16),
stoi(hexa.substr(2, 2), nullptr, 16),
stoi(hexa.substr(4, 2), nullptr, 16))) + "m";
} else {
return pre +
to_string(stoi(hexa.substr(0, 2), nullptr, 16)) + ";" +
to_string(stoi(hexa.substr(2, 2), nullptr, 16)) + ";" +
to_string(stoi(hexa.substr(4, 2), nullptr, 16)) + "m";
}
}
else Logger::error("Invalid size of hex value: " + hexa);
}
else Logger::error("Hex value missing: " + hexa);
return "";
}
string dec_to_color(int r, int g, int b, bool t_to_256, const string& depth) {
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
r = std::clamp(r, 0, 255);
g = std::clamp(g, 0, 255);
b = std::clamp(b, 0, 255);
if (t_to_256) return pre + to_string(truecolor_to_256(r, g, b)) + "m";
else return pre + to_string(r) + ";" + to_string(g) + ";" + to_string(b) + "m";
}
namespace {
//* Convert hex color to a array of decimals
array<int, 3> hex_to_dec(string hexa) {
if (hexa.size() > 1) {
hexa.erase(0, 1);
for (auto& c : hexa) {
if (not isxdigit(c))
return array{-1, -1, -1};
}
if (hexa.size() == 2) {
int h_int = stoi(hexa, nullptr, 16);
return array{h_int, h_int, h_int};
}
else if (hexa.size() == 6) {
return array{
stoi(hexa.substr(0, 2), nullptr, 16),
stoi(hexa.substr(2, 2), nullptr, 16),
stoi(hexa.substr(4, 2), nullptr, 16)
};
}
}
return {-1 ,-1 ,-1};
}
//* Generate colors and rgb decimal vectors for the theme
void generateColors(const std::unordered_map<string, string>& source) {
vector<string> t_rgb;
string depth;
bool t_to_256 = Config::getB("lowcolor");
colors.clear(); rgbs.clear();
for (const auto& [name, color] : Default_theme) {
if (name == "main_bg" and not Config::getB("theme_background")) {
colors[name] = "\x1b[49m";
rgbs[name] = {-1, -1, -1};
continue;
}
depth = (name.ends_with("bg") and name != "meter_bg") ? "bg" : "fg";
if (source.contains(name)) {
if (name == "main_bg" and source.at(name).empty()) {
colors[name] = "\x1b[49m";
rgbs[name] = {-1, -1, -1};
continue;
}
else if (source.at(name).empty() and (name.ends_with("_mid") or name.ends_with("_end"))) {
colors[name] = "";
rgbs[name] = {-1, -1, -1};
continue;
}
else if (source.at(name).starts_with('#')) {
colors[name] = hex_to_color(source.at(name), t_to_256, depth);
rgbs[name] = hex_to_dec(source.at(name));
}
else if (not source.at(name).empty()) {
t_rgb = ssplit(source.at(name));
if (t_rgb.size() != 3) {
Logger::error("Invalid RGB decimal value: \"" + source.at(name) + "\"");
} else {
colors[name] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), t_to_256, depth);
rgbs[name] = array{stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2])};
}
}
}
if (not colors.contains(name) and not is_in(name, "meter_bg", "process_start", "process_mid", "process_end", "graph_text")) {
Logger::debug("Missing color value for \"" + name + "\". Using value from default.");
colors[name] = hex_to_color(color, t_to_256, depth);
rgbs[name] = hex_to_dec(color);
}
}
//? Set fallback values for optional colors not defined in theme file
if (not colors.contains("meter_bg")) {
colors["meter_bg"] = colors.at("inactive_fg");
rgbs["meter_bg"] = rgbs.at("inactive_fg");
}
if (not colors.contains("process_start")) {
colors["process_start"] = colors.at("cpu_start");
colors["process_mid"] = colors.at("cpu_mid");
colors["process_end"] = colors.at("cpu_end");
rgbs["process_start"] = rgbs.at("cpu_start");
rgbs["process_mid"] = rgbs.at("cpu_mid");
rgbs["process_end"] = rgbs.at("cpu_end");
}
if (not colors.contains("graph_text")) {
colors["graph_text"] = colors.at("inactive_fg");
rgbs["graph_text"] = rgbs.at("inactive_fg");
}
}
//* Generate color gradients from two or three colors, 101 values indexed 0-100
void generateGradients() {
gradients.clear();
bool t_to_256 = Config::getB("lowcolor");
//? Insert values for processes greyscale gradient and processes color gradient
rgbs.insert({
{ "proc_start", rgbs["main_fg"] },
{ "proc_mid", {-1, -1, -1} },
{ "proc_end", rgbs["inactive_fg"] },
{ "proc_color_start", rgbs["inactive_fg"] },
{ "proc_color_mid", {-1, -1, -1} },
{ "proc_color_end", rgbs["process_start"] },
});
for (const auto& [name, source_arr] : rgbs) {
if (not name.ends_with("_start")) continue;
const string color_name { rtrim(name, "_start") };
//? input_colors[start,mid,end][red,green,blue]
const array<array<int, 3>, 3> input_colors = {
source_arr,
rgbs[color_name + "_mid"],
rgbs[color_name + "_end"]
};
//? output_colors[red,green,blue][0-100]
array<array<int, 3>, 101> output_colors;
output_colors[0][0] = -1;
//? Only start iteration if gradient has an end color defined
if (input_colors[2][0] >= 0) {
//? Split iteration in two passes of 50 + 51 instead of one pass of 101 if gradient has start, mid and end values defined
int current_range = (input_colors[1][0] >= 0) ? 50 : 100;
for (int rgb : iota(0, 3)) {
int start = 0, offset = 0;
int end = (current_range == 50) ? 1 : 2;
for (int i : iota(0, 101)) {
output_colors[i][rgb] = input_colors[start][rgb] + (i - offset) * (input_colors[end][rgb] - input_colors[start][rgb]) / current_range;
//? Switch source arrays from start->mid to mid->end at 50 passes if mid is defined
if (i == current_range) { ++start; ++end; offset = 50; }
}
}
}
//? Generate color escape codes for the generated rgb decimals
array<string, 101> color_gradient;
if (output_colors[0][0] != -1) {
for (int y = 0; const auto& [red, green, blue] : output_colors)
color_gradient[y++] = dec_to_color(red, green, blue, t_to_256);
}
else {
//? If only start was defined fill array with start color
color_gradient.fill(colors[name]);
}
gradients[color_name] = std::move(color_gradient);
}
}
//* Set colors and generate gradients for the TTY theme
void generateTTYColors() {
rgbs.clear();
gradients.clear();
colors = TTY_theme;
if (not Config::getB("theme_background"))
colors["main_bg"] = "\x1b[49m";
for (const auto& c : colors) {
if (not c.first.ends_with("_start")) continue;
const string base_name { rtrim(c.first, "_start") };
string section = "_start";
int split = colors.at(base_name + "_mid").empty() ? 50 : 33;
for (int i : iota(0, 101)) {
gradients[base_name][i] = colors.at(base_name + section);
if (i == split) {
section = (split == 33) ? "_mid" : "_end";
split *= 2;
}
}
}
}
//* Load a .theme file from disk
auto loadFile(const string& filename) {
const fs::path filepath = filename;
if (not fs::exists(filepath))
return Default_theme;
std::ifstream themefile(filepath);
if (themefile.good()) {
std::unordered_map<string, string> theme_out;
Logger::debug("Loading theme file: " + filename);
while (not themefile.bad()) {
if (themefile.peek() == '#') {
themefile.ignore(SSmax, '\n');
continue;
}
themefile.ignore(SSmax, '[');
if (themefile.eof()) break;
string name, value;
getline(themefile, name, ']');
if (not Default_theme.contains(name)) {
themefile.ignore(SSmax, '\n');
continue;
}
themefile.ignore(SSmax, '=');
themefile >> std::ws;
if (themefile.eof()) break;
if (themefile.peek() == '"') {
themefile.ignore(1);
getline(themefile, value, '"');
themefile.ignore(SSmax, '\n');
}
else getline(themefile, value, '\n');
theme_out[name] = value;
}
return theme_out;
}
return Default_theme;
}
}
void updateThemes() {
themes.clear();
themes.push_back("Default");
themes.push_back("TTY");
//? Priority: custom_theme_dir -> user_theme_dir -> theme_dir
for (const auto& path : { custom_theme_dir, user_theme_dir, theme_dir } ) {
if (path.empty()) continue;
for (auto& file : fs::directory_iterator(path)) {
if (file.path().extension() == ".theme" and access(file.path().c_str(), R_OK) != -1 and not v_contains(themes, file.path().c_str())) {
themes.push_back(file.path().c_str());
}
}
}
}
void setTheme() {
const auto& theme = Config::getS("color_theme");
fs::path theme_path;
for (const fs::path p : themes) {
if (p == theme or p.stem() == theme or p.filename() == theme) {
theme_path = p;
break;
}
}
if (theme == "TTY" or Config::getB("tty_mode"))
generateTTYColors();
else {
generateColors((theme == "Default" or theme_path.empty() ? Default_theme : loadFile(theme_path)));
generateGradients();
}
Term::fg = colors.at("main_fg");
Term::bg = colors.at("main_bg");
Fx::reset = Fx::reset_base + Term::fg + Term::bg;
}
}
@@ -0,0 +1,70 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#include <array>
#include <filesystem>
#include <string>
#include <vector>
#include <unordered_map>
using std::array;
using std::string;
using std::vector;
namespace Theme {
extern std::filesystem::path theme_dir;
extern std::filesystem::path user_theme_dir;
extern std::filesystem::path custom_theme_dir;
//* Contains "Default" and "TTY" at indices 0 and 1, otherwise full paths to theme files
extern vector<string> themes;
//* Generate escape sequence for 24-bit or 256 color and return as a string
//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale
//* t_to_256: [true|false] convert 24bit value to 256 color value
//* depth: ["fg"|"bg"] for either a foreground color or a background color
string hex_to_color(string hexa, bool t_to_256=false, const string& depth="fg");
//* Generate escape sequence for 24-bit or 256 color and return as a string
//* Args r: [0-255], g: [0-255], b: [0-255]
//* t_to_256: [true|false] convert 24bit value to 256 color value
//* depth: ["fg"|"bg"] for either a foreground color or a background color
string dec_to_color(int r, int g, int b, bool t_to_256=false, const string& depth="fg");
//* Update list of paths for available themes
void updateThemes();
//* Set current theme from current "color_theme" value in config
void setTheme();
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); }
//* Return array of escape codes for color gradient <name>
inline const array<string, 101>& g(const string& name) { return gradients.at(name); }
//* Return array of red, green and blue in decimal for color <name>
inline const std::array<int, 3>& dec(const string& name) { return rgbs.at(name); }
}
@@ -0,0 +1,714 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <cmath>
#include <codecvt>
#include <ctime>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <optional>
#include <ranges>
#include <sstream>
#include <string_view>
#include <utility>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include "widechar_width.hpp"
#include "btop_shared.hpp"
#include "btop_tools.hpp"
#include "btop_config.hpp"
using std::cout;
using std::floor;
using std::flush;
using std::max;
using std::string_view;
using std::to_string;
using namespace std::literals; // to use operator""s
namespace fs = std::filesystem;
namespace rng = std::ranges;
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
//* Collection of escape codes and functions for terminal manipulation
namespace Term {
atomic<bool> initialized{};
atomic<int> width{};
atomic<int> height{};
string current_tty;
namespace {
struct termios initial_settings;
//* Toggle terminal input echo
bool echo(bool on=true) {
struct termios settings;
if (tcgetattr(STDIN_FILENO, &settings)) return false;
if (on) settings.c_lflag |= ECHO;
else settings.c_lflag &= ~(ECHO);
return 0 == tcsetattr(STDIN_FILENO, TCSANOW, &settings);
}
//* Toggle need for return key when reading input
bool linebuffered(bool on=true) {
struct termios settings;
if (tcgetattr(STDIN_FILENO, &settings)) return false;
if (on) settings.c_lflag |= ICANON;
else {
settings.c_lflag &= ~(ICANON);
settings.c_cc[VMIN] = 0;
settings.c_cc[VTIME] = 0;
}
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false;
if (on) setlinebuf(stdin);
else setbuf(stdin, nullptr);
return true;
}
}
bool refresh(bool only_check) {
// Query dimensions of '/dev/tty' of the 'STDOUT_FILENO' isn't available.
// 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 | O_CLOEXEC);
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 = wsize.ws_col;
height = wsize.ws_row;
}
return true;
}
return false;
}
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;
#ifdef GPU_SUPPORT
int gpu = 0;
if (Gpu::count > 0)
for (char i = '0'; i <= '5'; i++)
gpu += (boxes.contains("gpu"s + i) ? 1 : 0);
#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
for (int i = 0; i < gpu; i++)
height += Gpu::gpu_b_height_offsets[i] + 4;
#endif
return { width, height };
}
bool init() {
if (not initialized) {
initialized = (bool)isatty(STDIN_FILENO);
if (initialized) {
tcgetattr(STDIN_FILENO, &initial_settings);
current_tty = (ttyname(STDIN_FILENO) != nullptr ? static_cast<string>(ttyname(STDIN_FILENO)) : "unknown");
//? 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);
echo(false);
linebuffered(false);
refresh();
cout << alt_screen << hide_cursor << mouse_on << flush;
Global::resized = false;
}
}
return initialized;
}
void restore() {
if (initialized) {
tcsetattr(STDIN_FILENO, TCSANOW, &initial_settings);
cout << mouse_off << clear << Fx::reset << normal_screen << show_cursor << flush;
initialized = false;
}
}
}
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
// ! Disabled due to issue when compiling with musl, reverted back to using regex
// namespace Fx {
// string uncolor(const string& s) {
// string out = s;
// for (size_t offset = 0, start_pos = 0, end_pos = 0;;) {
// start_pos = (offset == 0) ? out.find('\x1b') : offset;
// if (start_pos == string::npos)
// break;
// offset = start_pos + 1;
// end_pos = out.find('m', offset);
// if (end_pos == string::npos)
// break;
// else if (auto next_pos = out.find('\x1b', offset); not isdigit(out[end_pos - 1]) or end_pos > next_pos) {
// offset = next_pos;
// continue;
// }
// out.erase(start_pos, (end_pos - start_pos)+1);
// offset = 0;
// }
// out.shrink_to_fit();
// return out;
// }
// }
namespace Tools {
size_t wide_ulen(const std::string_view str) {
unsigned int chars = 0;
try {
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
auto w_str = conv.from_bytes((str.size() > 10000 ? str.substr(0, 10000).data() : str.data()));
for (auto c : w_str) {
chars += utf8::wcwidth(c);
}
}
catch (...) {
return ulen(str);
}
return chars;
}
size_t wide_ulen(const std::wstring_view w_str) {
unsigned int chars = 0;
for (auto c : w_str) {
chars += utf8::wcwidth(c);
}
return chars;
}
string uresize(string str, const size_t len, bool wide) {
if (len < 1 or str.empty())
return "";
if (wide) {
try {
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
auto w_str = conv.from_bytes((str.size() > 10000 ? str.substr(0, 10000).c_str() : str.c_str()));
while (wide_ulen(w_str) > len)
w_str.pop_back();
string n_str = conv.to_bytes(w_str);
return n_str;
}
catch (...) {
return uresize(str, len, false);
}
}
else {
for (size_t x = 0, i = 0; i < str.size(); i++) {
if ((static_cast<unsigned char>(str.at(i)) & 0xC0) != 0x80) x++;
if (x >= len + 1) {
str.resize(i);
break;
}
}
}
str.shrink_to_fit();
return str;
}
string luresize(string str, const size_t len, bool wide) {
if (len < 1 or str.empty())
return "";
for (size_t x = 0, last_pos = 0, i = str.size() - 1; i > 0 ; i--) {
if (wide and static_cast<unsigned char>(str.at(i)) > 0xef) {
x += 2;
last_pos = max((size_t)0, i - 1);
}
else if ((static_cast<unsigned char>(str.at(i)) & 0xC0) != 0x80) {
x++;
last_pos = i;
}
if (x >= len) {
str = str.substr(last_pos);
str.shrink_to_fit();
break;
}
}
return str;
}
string s_replace(const string& str, const string& from, const string& to) {
string out = str;
for (size_t start_pos = out.find(from); start_pos != std::string::npos; start_pos = out.find(from)) {
out.replace(start_pos, from.length(), to);
}
return out;
}
string_view ltrim(string_view str, const string_view t_str) {
while (str.starts_with(t_str))
str.remove_prefix(t_str.size());
return str;
}
string_view rtrim(string_view str, const string_view t_str) {
while (str.ends_with(t_str))
str.remove_suffix(t_str.size());
return str;
}
string ljust(string str, const size_t x, bool utf, bool wide, bool limit) {
if (utf) {
if (limit and ulen(str, wide) > x)
return uresize(str, x, wide);
return str + string(max((int)(x - ulen(str, wide)), 0), ' ');
}
else {
if (limit and str.size() > x) {
str.resize(x);
return str;
}
return str + string(max((int)(x - str.size()), 0), ' ');
}
}
string rjust(string str, const size_t x, bool utf, bool wide, bool limit) {
if (utf) {
if (limit and ulen(str, wide) > x)
return uresize(str, x, wide);
return string(max((int)(x - ulen(str)), 0), ' ') + str;
}
else {
if (limit and str.size() > x) {
str.resize(x);
return str;
};
return string(max((int)(x - str.size()), 0), ' ') + str;
}
}
string cjust(string str, const size_t x, bool utf, bool wide, bool limit) {
if (utf) {
if (limit and ulen(str, wide) > x)
return uresize(str, x, wide);
return string(max((int)ceil((double)(x - ulen(str)) / 2), 0), ' ') + str + string(max((int)floor((double)(x - ulen(str)) / 2), 0), ' ');
}
else {
if (limit and str.size() > x) {
str.resize(x);
return str;
}
return string(max((int)ceil((double)(x - str.size()) / 2), 0), ' ') + str + string(max((int)floor((double)(x - str.size()) / 2), 0), ' ');
}
}
string trans(const string& str) {
std::string_view oldstr{str};
string newstr;
newstr.reserve(str.size());
for (size_t pos; (pos = oldstr.find(' ')) != string::npos;) {
newstr.append(oldstr.substr(0, pos));
size_t x = 0;
while (pos + x < oldstr.size() and oldstr.at(pos + x) == ' ') x++;
newstr.append(Mv::r(x));
oldstr.remove_prefix(pos + x);
}
return (newstr.empty()) ? str : newstr + string{oldstr};
}
string sec_to_dhms(size_t seconds, bool no_days, bool no_seconds) {
size_t days = seconds / 86400; seconds %= 86400;
size_t hours = seconds / 3600; seconds %= 3600;
size_t minutes = seconds / 60; seconds %= 60;
string out = (not no_days and days > 0 ? to_string(days) + "d " : "")
+ (hours < 10 ? "0" : "") + to_string(hours) + ':'
+ (minutes < 10 ? "0" : "") + to_string(minutes)
+ (not no_seconds ? ":" + string(std::cmp_less(seconds, 10) ? "0" : "") + to_string(seconds) : "");
return out;
}
string floating_humanizer(uint64_t value, bool shorten, size_t start, bool bit, bool per_second) {
string out;
const size_t mult = (bit) ? 8 : 1;
bool mega = Config::getB("base_10_sizes");
// Bitrates
if(bit && per_second) {
const auto& base_10_bitrate = Config::getS("base_10_bitrate");
if(base_10_bitrate == "True") {
mega = true;
} else if(base_10_bitrate == "False") {
mega = false;
}
// Default or "Auto": Uses base_10_sizes for bitrates
}
// taking advantage of type deduction for array creation (since C++17)
// combined with string literals (operator""s)
static const array mebiUnits_bit {
"bit"s, "Kib"s, "Mib"s,
"Gib"s, "Tib"s, "Pib"s,
"Eib"s, "Zib"s, "Yib"s,
"Bib"s, "GEb"s
};
static const array mebiUnits_byte {
"Byte"s, "KiB"s, "MiB"s,
"GiB"s, "TiB"s, "PiB"s,
"EiB"s, "ZiB"s, "YiB"s,
"BiB"s, "GEB"s
};
static const array megaUnits_bit {
"bit"s, "Kb"s, "Mb"s,
"Gb"s, "Tb"s, "Pb"s,
"Eb"s, "Zb"s, "Yb"s,
"Bb"s, "Gb"s
};
static const array megaUnits_byte {
"Byte"s, "KB"s, "MB"s,
"GB"s, "TB"s, "PB"s,
"EB"s, "ZB"s, "YB"s,
"BB"s, "GB"s
};
const auto& units = (bit) ? ( mega ? megaUnits_bit : mebiUnits_bit) : ( mega ? megaUnits_byte : mebiUnits_byte);
value *= 100 * mult;
if (mega) {
while (value >= 100000) {
value /= 1000;
if (value < 100) {
out = fmt::format("{}", value);
break;
}
start++;
}
}
else {
while (value >= 102400) {
value >>= 10;
if (value < 100) {
out = fmt::format("{}", value);
break;
}
start++;
}
}
if (out.empty()) {
out = fmt::format("{}", value);
if (not mega and out.size() == 4 and start > 0) {
out.pop_back();
out.insert(2, ".");
}
else if (out.size() == 3 and start > 0) {
out.insert(1, ".");
}
else if (out.size() >= 2) {
out.resize(out.size() - 2);
}
if (out.empty()) {
out = "0";
}
}
if (shorten) {
auto f_pos = out.find(".");
if (f_pos == 1 and out.size() > 3) {
out = fmt::format("{:.1f}", stod(out));
}
else if (f_pos != string::npos) {
out = fmt::format("{:.0f}", stod(out));
}
if (out.size() > 3) {
out = fmt::format("{:d}.0", out[0] - '0');
start++;
}
out.push_back(units[start][0]);
}
else out += " " + units[start];
if (per_second) out += (bit) ? "ps" : "/s";
return out;
}
std::string operator*(const string& str, int64_t n) {
if (n < 1 or str.empty()) {
return "";
}
else if (n == 1) {
return str;
}
string new_str;
new_str.reserve(str.size() * n);
for (; n > 0; n--)
new_str.append(str);
return new_str;
}
string strf_time(const string& strf) {
auto in_time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm bt {};
std::stringstream ss;
ss << std::put_time(localtime_r(&in_time_t, &bt), strf.c_str());
return ss.str();
}
void atomic_wait(const atomic<bool>& atom, bool old) noexcept {
while (atom.load(std::memory_order_relaxed) == old ) busy_wait();
}
void atomic_wait_for(const atomic<bool>& atom, bool old, const uint64_t wait_ms) noexcept {
const uint64_t start_time = time_ms();
while (atom.load(std::memory_order_relaxed) == old and (time_ms() - start_time < wait_ms)) sleep_ms(1);
}
atomic_lock::atomic_lock(atomic<bool>& atom, bool wait) : atom(atom) {
if (wait) while (not this->atom.compare_exchange_strong(this->not_true, true));
else this->atom.store(true);
}
atomic_lock::~atomic_lock() noexcept {
this->atom.store(false);
}
string readfile(const std::filesystem::path& path, const string& fallback) {
if (not fs::exists(path)) return fallback;
string out;
try {
std::ifstream file(path);
for (string readstr; getline(file, readstr); out += readstr);
}
catch (const std::exception& e) {
Logger::error("readfile() : Exception when reading " + string{path} + " : " + e.what());
return fallback;
}
return (out.empty() ? fallback : out);
}
auto celsius_to(const long long& celsius, const string& scale) -> tuple<long long, string> {
if (scale == "celsius")
return {celsius, "°C"};
else if (scale == "fahrenheit")
return {(long long)round((double)celsius * 1.8 + 32), "°F"};
else if (scale == "kelvin")
return {(long long)round((double)celsius + 273.15), "K "};
else if (scale == "rankine")
return {(long long)round((double)celsius * 1.8 + 491.67), "°R"};
return {0, ""};
}
string hostname() {
char host[HOST_NAME_MAX];
gethostname(host, HOST_NAME_MAX);
host[HOST_NAME_MAX - 1] = '\0';
return string{host};
}
string username() {
auto user = getenv("LOGNAME");
if (user == nullptr or strlen(user) == 0) user = getenv("USER");
return (user != nullptr ? user : "");
}
DebugTimer::DebugTimer(string name, bool start, bool delayed_report)
: name(std::move(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 {
using namespace Tools;
std::atomic<bool> busy (false);
bool first = true;
const string tdf = "%Y/%m/%d (%T) | ";
size_t loglevel;
std::optional<std::filesystem::path> logfile;
//* Wrapper for lowering privileges if using SUID bit and currently isn't using real userid
class lose_priv {
int status = -1;
public:
lose_priv() {
if (geteuid() != Global::real_uid) {
this->status = seteuid(Global::real_uid);
}
}
~lose_priv() noexcept {
if (status == 0) {
status = seteuid(Global::set_uid);
}
}
lose_priv(const lose_priv& other) = delete;
lose_priv& operator=(const lose_priv& other) = delete;
lose_priv(lose_priv&& other) = delete;
lose_priv& operator=(lose_priv&& other) = delete;
};
void set(const string& level) {
loglevel = v_index(log_levels, level);
}
void log_write(const Level level, const std::string_view msg) {
if (loglevel < level or !logfile.has_value()) {
return;
}
auto& log_file = logfile.value();
atomic_lock lck(busy, true);
lose_priv neutered{};
std::error_code ec;
try {
// NOTE: `exist()` could throw but since we return with an empty logfile we don't care
if (fs::exists(log_file) and fs::file_size(log_file, ec) > 1024 << 10 and not ec) {
auto old_log = log_file;
old_log += ".1";
if (fs::exists(old_log))
fs::remove(old_log, ec);
if (not ec)
fs::rename(log_file, old_log, ec);
}
if (not ec) {
std::ofstream lwrite(log_file, std::ios::app);
if (first) {
first = false;
lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";
}
lwrite << strf_time(tdf) << log_levels.at(level) << ": " << msg << "\n";
}
else log_file.clear();
}
catch (const std::exception& e) {
log_file.clear();
throw std::runtime_error("Exception in Logger::log_write() : " + string{e.what()});
}
}
}
@@ -0,0 +1,488 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#if !defined(NDEBUG)
# define BTOP_DEBUG
#endif
#include <algorithm> // for std::ranges::count_if
#include <array>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <filesystem>
#include <limits.h>
#include <pthread.h>
#include <ranges>
#include <regex>
#include <string>
#include <string_view>
#include <thread>
#include <tuple>
#include <unordered_map>
#include <vector>
#ifdef BTOP_DEBUG
#include <source_location>
#endif
#ifndef HOST_NAME_MAX
#ifdef __APPLE__
#define HOST_NAME_MAX 255
#else
#define HOST_NAME_MAX 64
#endif
#endif
#include "fmt/core.h"
#include "fmt/format.h"
using std::array;
using std::atomic;
using std::string;
using std::to_string;
using std::string_view;
using std::tuple;
using std::vector;
using namespace fmt::literals;
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
//* Collection of escape codes for text style and formatting
namespace Fx {
const string e = "\x1b["; //* Escape sequence start
const string b = e + "1m"; //* Bold on/off
const string ub = e + "22m"; //* Bold off
const string d = e + "2m"; //* Dark on
const string ud = e + "22m"; //* Dark off
const string i = e + "3m"; //* Italic on
const string ui = e + "23m"; //* Italic off
const string ul = e + "4m"; //* Underline on
const string uul = e + "24m"; //* Underline off
const string bl = e + "5m"; //* Blink on
const string ubl = e + "25m"; //* Blink off
const string s = e + "9m"; //* Strike/crossed-out on
const string us = e + "29m"; //* Strike/crossed-out on/off
//* Reset foreground/background color and text effects
const string reset_base = e + "0m";
//* Reset text effects and restore theme foregrund and background color
extern string reset;
//* Regex for matching color, style and cursor move escape sequences
const std::regex escape_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m|f|s|u|C|D|A|B){1}");
//* Regex for matching only color and style escape sequences
const std::regex color_regex("\033\\[\\d+;?\\d?;?\\d*;?\\d*;?\\d*(m){1}");
//* Return a string with all colors and text styling removed
inline string uncolor(const string& s) { return std::regex_replace(s, color_regex, ""); }
// string uncolor(const string& s);
}
//* Collection of escape codes and functions for cursor manipulation
namespace Mv {
//* Move cursor to <line>, <column>
inline string to(int line, int col) { return Fx::e + to_string(line) + ';' + to_string(col) + 'f'; }
//* Move cursor right <x> columns
inline string r(int x) { return Fx::e + to_string(x) + 'C'; }
//* Move cursor left <x> columns
inline string l(int x) { return Fx::e + to_string(x) + 'D'; }
//* Move cursor up x lines
inline string u(int x) { return Fx::e + to_string(x) + 'A'; }
//* Move cursor down x lines
inline string d(int x) { return Fx::e + to_string(x) + 'B'; }
//* Save cursor position
const string save = Fx::e + "s";
//* Restore saved cursor position
const string restore = Fx::e + "u";
}
//* Collection of escape codes and functions for terminal manipulation
namespace Term {
extern atomic<bool> initialized;
extern atomic<int> width;
extern atomic<int> height;
extern string fg, bg, current_tty;
const string hide_cursor = Fx::e + "?25l";
const string show_cursor = Fx::e + "?25h";
const string alt_screen = Fx::e + "?1049h";
const string normal_screen = Fx::e + "?1049l";
const string clear = Fx::e + "2J" + Fx::e + "0;0f";
const string clear_end = Fx::e + "0J";
const string clear_begin = Fx::e + "1J";
const string mouse_on = Fx::e + "?1002h" + Fx::e + "?1015h" + Fx::e + "?1006h"; //? Enable reporting of mouse position on click and release
const string mouse_off = Fx::e + "?1002l" + Fx::e + "?1015l" + Fx::e + "?1006l";
const string mouse_direct_on = Fx::e + "?1003h"; //? Enable reporting of mouse position at any movement
const string mouse_direct_off = Fx::e + "?1003l";
const string sync_start = Fx::e + "?2026h"; //? Start of terminal synchronized output
const string sync_end = Fx::e + "?2026l"; //? End of terminal synchronized output
//* Returns true if terminal has been resized and updates width and height
bool refresh(bool only_check=false);
//* Returns an array with the lowest possible width, height with current box config
auto get_min_size(const string& boxes) -> array<int, 2>;
//* Check for a valid tty, save terminal options and set new options
bool init();
//* Restore terminal options
void restore();
}
//* Simple logging implementation
namespace Logger {
const vector<string> log_levels = {
"DISABLED",
"ERROR",
"WARNING",
"INFO",
"DEBUG",
};
extern std::optional<std::filesystem::path> logfile;
enum Level : std::uint8_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 std::string_view msg);
inline void error(const std::string_view msg) {
log_write(ERROR, msg);
}
inline void warning(const std::string_view msg) {
log_write(WARNING, msg);
}
inline void info(const std::string_view msg) {
log_write(INFO, msg);
}
inline void debug(const std::string_view 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 override { return '\''; }
virtual std::string do_grouping() const override { return "\03"; }
};
size_t wide_ulen(const std::string_view str);
size_t wide_ulen(const std::wstring_view w_str);
//* Return number of UTF8 characters in a string (wide=true for column size needed on terminal)
inline size_t ulen(const std::string_view str, bool wide = false) {
return (wide ? wide_ulen(str) : std::ranges::count_if(str, [](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; }));
}
//* Resize a string consisting of UTF8 characters (only reduces size)
string uresize(const string str, const size_t len, bool wide = false);
//* Resize a string consisting of UTF8 characters from left (only reduces size)
string luresize(const string str, const size_t len, bool wide = false);
//* Replace <from> in <str> with <to> and return new string
string s_replace(const string& str, const string& from, const string& to);
//* Replace ascii control characters with <replacement> in <str> and return new string
inline string replace_ascii_control(string str, const char replacement = ' ') {
std::ranges::for_each(str, [&replacement](char& c) { if (static_cast<unsigned char>(c) < 0x20) c = replacement; });
return str;
}
//* Capitalize <str>
inline string capitalize(string str) {
str.at(0) = toupper(str.at(0));
return str;
}
//* Return <str> with only uppercase characters
inline auto str_to_upper(string str) {
std::ranges::for_each(str, [](auto& c) { c = ::toupper(c); } );
return str;
}
//* Return <str> with only lowercase characters
inline auto str_to_lower(string str) {
std::ranges::for_each(str, [](char& c) { c = ::tolower(c); } );
return str;
}
//* Check if vector <vec> contains value <find_val>
template <typename T, typename T2>
inline bool v_contains(const vector<T>& vec, const T2& find_val) {
return std::ranges::find(vec, find_val) != vec.end();
}
//* Check if string <str> contains string <find_val>, while ignoring case
inline bool s_contains_ic(const std::string_view str, const std::string_view find_val) {
auto it = std::search(
str.begin(), str.end(),
find_val.begin(), find_val.end(),
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
);
return it != str.end();
}
//* Return index of <find_val> from vector <vec>, returns size of <vec> if <find_val> is not present
template <typename T>
inline size_t v_index(const vector<T>& vec, const T& find_val) {
return std::ranges::distance(vec.begin(), std::ranges::find(vec, find_val));
}
//* Compare <first> with all following values
template<typename First, typename ... T>
inline bool is_in(const First& first, const T& ... t) {
return ((first == t) or ...);
}
//* Return current time since epoch in seconds
inline uint64_t time_s() {
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
//* Return current time since epoch in milliseconds
inline uint64_t time_ms() {
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
//* Return current time since epoch in microseconds
inline uint64_t time_micros() {
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
//* Check if a string is a valid bool value
inline bool isbool(const std::string_view str) {
return is_in(str, "true", "false", "True", "False");
}
//* Convert string to bool, returning any value not equal to "true" or "True" as false
inline bool stobool(const std::string_view str) {
return is_in(str, "true", "True");
}
//* Check if a string is a valid integer value (only positive)
constexpr bool isint(const std::string_view str) {
return std::ranges::all_of(str, ::isdigit);
}
//* Left-trim <t_str> from <str> and return new string
string_view ltrim(string_view str, string_view t_str = " ");
//* Right-trim <t_str> from <str> and return new string
string_view rtrim(string_view str, string_view t_str = " ");
//* Left/right-trim <t_str> from <str> and return new string
inline string_view trim(string_view str, string_view t_str = " ") {
return ltrim(rtrim(str, t_str), t_str);
}
//* Split <string> at all occurrences of <delim> and return as vector of strings
inline std::vector<std::string> ssplit(std::string_view str, char delim = ' ') {
std::vector<std::string> result;
std::string token;
for (char c : str) {
if (c == delim) {
if (!token.empty()) { result.push_back(token); token.clear(); }
} else { token += c; }
}
if (!token.empty()) result.push_back(token);
return result;
}
//* Put current thread to sleep for <ms> milliseconds
inline void sleep_ms(const size_t& ms) {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}
//* Put current thread to sleep for <micros> microseconds
inline void sleep_micros(const size_t& micros) {
std::this_thread::sleep_for(std::chrono::microseconds(micros));
}
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
string ljust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
//* Right justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
string rjust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
//* Center justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
string cjust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
//* Replace whitespaces " " with escape code for move right
string trans(const string& str);
//* Convert seconds to format "<days>d <hours>:<minutes>:<seconds>" and return string
string sec_to_dhms(size_t seconds, bool no_days = false, bool no_seconds = false);
//* Scales up in steps of 1024 to highest positive value unit and returns string with unit suffixed
//* bit=True or defaults to bytes
//* start=int to set 1024 multiplier starting unit
//* short=True always returns 0 decimals and shortens unit to 1 character
string floating_humanizer(uint64_t value, bool shorten = false, size_t start = 0, bool bit = false, bool per_second = false);
//* 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 (auto it = map.find(key); it != map.end()) {
return it->second;
} 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 (auto it = map.find(key); it != map.end()) {
return it->second;
} 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[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[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);
string hostname();
string username();
static inline void busy_wait (void) {
#if defined __i386__ || defined __x86_64__
__builtin_ia32_pause();
#elif defined __ia64__
__asm volatile("hint @pause" : : : "memory");
#elif defined __sparc__ && (defined __arch64__ || defined __sparc_v9__)
__asm volatile("membar #LoadLoad" : : : "memory");
#else
__asm volatile("" : : : "memory");
#endif
}
void atomic_wait(const atomic<bool>& atom, bool old = true) noexcept;
void atomic_wait_for(const atomic<bool>& atom, bool old = true, const uint64_t wait_ms = 0) noexcept;
//* Sets atomic<bool> to true on construct, sets to false on destruct
class atomic_lock {
atomic<bool>& atom;
bool not_true{};
public:
explicit atomic_lock(atomic<bool>& atom, bool wait = false);
~atomic_lock() noexcept;
atomic_lock(const atomic_lock& other) = delete;
atomic_lock& operator=(const atomic_lock& other) = delete;
atomic_lock(atomic_lock&& other) = delete;
atomic_lock& operator=(atomic_lock&& other) = delete;
};
//* Read a complete file and return as a string
string readfile(const std::filesystem::path& path, const string& fallback = "");
//* 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>;
}
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{};
string name{};
bool delayed_report{};
Logger::Level log_level = Logger::DEBUG;
public:
DebugTimer() = default;
explicit DebugTimer(string name, bool start = true, bool delayed_report = true);
~DebugTimer();
DebugTimer(const DebugTimer& other) = delete;
DebugTimer& operator=(const DebugTimer& other) = delete;
DebugTimer(DebugTimer&& other) = delete;
DebugTimer& operator=(DebugTimer&& other) = delete;
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();
};
}
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <string_view>
constexpr std::string_view GIT_COMMIT = "@GIT_COMMIT@";
constexpr std::string_view COMPILER = "@COMPILER@";
constexpr std::string_view COMPILER_VERSION = "@COMPILER_VERSION@";
constexpr std::string_view CONFIGURE_COMMAND = "@CONFIGURE_COMMAND@";
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,19 @@
enable_language(C)
add_library(igt OBJECT
igt_perf.c
intel_device_info.c
intel_gpu_top.c
intel_name_lookup_shim.c
)
if(BTOP_LTO)
# We have checked LTO support already and it's supported :)
set_target_properties(igt PROPERTIES INTERPROCEDURAL_OPTIMIZATION ON)
endif()
# Disable all warnings
target_compile_options(igt PRIVATE -w)
# Link igt into btop
target_link_libraries(libbtop $<TARGET_OBJECTS:igt>)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,786 @@
/*
* Copyright 2013 Intel Corporation
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _I915_PCIIDS_H
#define _I915_PCIIDS_H
/*
* A pci_device_id struct {
* __u32 vendor, device;
* __u32 subvendor, subdevice;
* __u32 class, class_mask;
* kernel_ulong_t driver_data;
* };
* Don't use C99 here because "class" is reserved and we want to
* give userspace flexibility.
*/
#define INTEL_VGA_DEVICE(id, info) { \
0x8086, id, \
~0, ~0, \
0x030000, 0xff0000, \
(unsigned long) info }
#define INTEL_QUANTA_VGA_DEVICE(info) { \
0x8086, 0x16a, \
0x152d, 0x8990, \
0x030000, 0xff0000, \
(unsigned long) info }
#define INTEL_I810_IDS(MACRO__, ...) \
MACRO__(0x7121, ## __VA_ARGS__), /* I810 */ \
MACRO__(0x7123, ## __VA_ARGS__), /* I810_DC100 */ \
MACRO__(0x7125, ## __VA_ARGS__) /* I810_E */
#define INTEL_I815_IDS(MACRO__, ...) \
MACRO__(0x1132, ## __VA_ARGS__) /* I815*/
#define INTEL_I830_IDS(MACRO__, ...) \
MACRO__(0x3577, ## __VA_ARGS__)
#define INTEL_I845G_IDS(MACRO__, ...) \
MACRO__(0x2562, ## __VA_ARGS__)
#define INTEL_I85X_IDS(MACRO__, ...) \
MACRO__(0x3582, ## __VA_ARGS__), /* I855_GM */ \
MACRO__(0x358e, ## __VA_ARGS__)
#define INTEL_I865G_IDS(MACRO__, ...) \
MACRO__(0x2572, ## __VA_ARGS__) /* I865_G */
#define INTEL_I915G_IDS(MACRO__, ...) \
MACRO__(0x2582, ## __VA_ARGS__), /* I915_G */ \
MACRO__(0x258a, ## __VA_ARGS__) /* E7221_G */
#define INTEL_I915GM_IDS(MACRO__, ...) \
MACRO__(0x2592, ## __VA_ARGS__) /* I915_GM */
#define INTEL_I945G_IDS(MACRO__, ...) \
MACRO__(0x2772, ## __VA_ARGS__) /* I945_G */
#define INTEL_I945GM_IDS(MACRO__, ...) \
MACRO__(0x27a2, ## __VA_ARGS__), /* I945_GM */ \
MACRO__(0x27ae, ## __VA_ARGS__) /* I945_GME */
#define INTEL_I965G_IDS(MACRO__, ...) \
MACRO__(0x2972, ## __VA_ARGS__), /* I946_GZ */ \
MACRO__(0x2982, ## __VA_ARGS__), /* G35_G */ \
MACRO__(0x2992, ## __VA_ARGS__), /* I965_Q */ \
MACRO__(0x29a2, ## __VA_ARGS__) /* I965_G */
#define INTEL_G33_IDS(MACRO__, ...) \
MACRO__(0x29b2, ## __VA_ARGS__), /* Q35_G */ \
MACRO__(0x29c2, ## __VA_ARGS__), /* G33_G */ \
MACRO__(0x29d2, ## __VA_ARGS__) /* Q33_G */
#define INTEL_I965GM_IDS(MACRO__, ...) \
MACRO__(0x2a02, ## __VA_ARGS__), /* I965_GM */ \
MACRO__(0x2a12, ## __VA_ARGS__) /* I965_GME */
#define INTEL_GM45_IDS(MACRO__, ...) \
MACRO__(0x2a42, ## __VA_ARGS__) /* GM45_G */
#define INTEL_G45_IDS(MACRO__, ...) \
MACRO__(0x2e02, ## __VA_ARGS__), /* IGD_E_G */ \
MACRO__(0x2e12, ## __VA_ARGS__), /* Q45_G */ \
MACRO__(0x2e22, ## __VA_ARGS__), /* G45_G */ \
MACRO__(0x2e32, ## __VA_ARGS__), /* G41_G */ \
MACRO__(0x2e42, ## __VA_ARGS__), /* B43_G */ \
MACRO__(0x2e92, ## __VA_ARGS__) /* B43_G.1 */
#define INTEL_PNV_G_IDS(MACRO__, ...) \
MACRO__(0xa001, ## __VA_ARGS__)
#define INTEL_PNV_M_IDS(MACRO__, ...) \
MACRO__(0xa011, ## __VA_ARGS__)
#define INTEL_PNV_IDS(MACRO__, ...) \
INTEL_PNV_G_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_PNV_M_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_ILK_D_IDS(MACRO__, ...) \
MACRO__(0x0042, ## __VA_ARGS__)
#define INTEL_ILK_M_IDS(MACRO__, ...) \
MACRO__(0x0046, ## __VA_ARGS__)
#define INTEL_ILK_IDS(MACRO__, ...) \
INTEL_ILK_D_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_ILK_M_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_SNB_D_GT1_IDS(MACRO__, ...) \
MACRO__(0x0102, ## __VA_ARGS__), \
MACRO__(0x010A, ## __VA_ARGS__)
#define INTEL_SNB_D_GT2_IDS(MACRO__, ...) \
MACRO__(0x0112, ## __VA_ARGS__), \
MACRO__(0x0122, ## __VA_ARGS__)
#define INTEL_SNB_D_IDS(MACRO__, ...) \
INTEL_SNB_D_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_SNB_D_GT2_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_SNB_M_GT1_IDS(MACRO__, ...) \
MACRO__(0x0106, ## __VA_ARGS__)
#define INTEL_SNB_M_GT2_IDS(MACRO__, ...) \
MACRO__(0x0116, ## __VA_ARGS__), \
MACRO__(0x0126, ## __VA_ARGS__)
#define INTEL_SNB_M_IDS(MACRO__, ...) \
INTEL_SNB_M_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_SNB_M_GT2_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_SNB_IDS(MACRO__, ...) \
INTEL_SNB_D_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_SNB_M_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_IVB_M_GT1_IDS(MACRO__, ...) \
MACRO__(0x0156, ## __VA_ARGS__) /* GT1 mobile */
#define INTEL_IVB_M_GT2_IDS(MACRO__, ...) \
MACRO__(0x0166, ## __VA_ARGS__) /* GT2 mobile */
#define INTEL_IVB_M_IDS(MACRO__, ...) \
INTEL_IVB_M_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_IVB_M_GT2_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_IVB_D_GT1_IDS(MACRO__, ...) \
MACRO__(0x0152, ## __VA_ARGS__), /* GT1 desktop */ \
MACRO__(0x015a, ## __VA_ARGS__) /* GT1 server */
#define INTEL_IVB_D_GT2_IDS(MACRO__, ...) \
MACRO__(0x0162, ## __VA_ARGS__), /* GT2 desktop */ \
MACRO__(0x016a, ## __VA_ARGS__) /* GT2 server */
#define INTEL_IVB_D_IDS(MACRO__, ...) \
INTEL_IVB_D_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_IVB_D_GT2_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_IVB_IDS(MACRO__, ...) \
INTEL_IVB_M_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_IVB_D_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_IVB_Q_IDS(MACRO__, ...) \
INTEL_QUANTA_VGA_DEVICE(__VA_ARGS__) /* Quanta transcode */
#define INTEL_HSW_ULT_GT1_IDS(MACRO__, ...) \
MACRO__(0x0A02, ## __VA_ARGS__), /* ULT GT1 desktop */ \
MACRO__(0x0A06, ## __VA_ARGS__), /* ULT GT1 mobile */ \
MACRO__(0x0A0A, ## __VA_ARGS__), /* ULT GT1 server */ \
MACRO__(0x0A0B, ## __VA_ARGS__) /* ULT GT1 reserved */
#define INTEL_HSW_ULX_GT1_IDS(MACRO__, ...) \
MACRO__(0x0A0E, ## __VA_ARGS__) /* ULX GT1 mobile */
#define INTEL_HSW_GT1_IDS(MACRO__, ...) \
INTEL_HSW_ULT_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_HSW_ULX_GT1_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x0402, ## __VA_ARGS__), /* GT1 desktop */ \
MACRO__(0x0406, ## __VA_ARGS__), /* GT1 mobile */ \
MACRO__(0x040A, ## __VA_ARGS__), /* GT1 server */ \
MACRO__(0x040B, ## __VA_ARGS__), /* GT1 reserved */ \
MACRO__(0x040E, ## __VA_ARGS__), /* GT1 reserved */ \
MACRO__(0x0C02, ## __VA_ARGS__), /* SDV GT1 desktop */ \
MACRO__(0x0C06, ## __VA_ARGS__), /* SDV GT1 mobile */ \
MACRO__(0x0C0A, ## __VA_ARGS__), /* SDV GT1 server */ \
MACRO__(0x0C0B, ## __VA_ARGS__), /* SDV GT1 reserved */ \
MACRO__(0x0C0E, ## __VA_ARGS__), /* SDV GT1 reserved */ \
MACRO__(0x0D02, ## __VA_ARGS__), /* CRW GT1 desktop */ \
MACRO__(0x0D06, ## __VA_ARGS__), /* CRW GT1 mobile */ \
MACRO__(0x0D0A, ## __VA_ARGS__), /* CRW GT1 server */ \
MACRO__(0x0D0B, ## __VA_ARGS__), /* CRW GT1 reserved */ \
MACRO__(0x0D0E, ## __VA_ARGS__) /* CRW GT1 reserved */
#define INTEL_HSW_ULT_GT2_IDS(MACRO__, ...) \
MACRO__(0x0A12, ## __VA_ARGS__), /* ULT GT2 desktop */ \
MACRO__(0x0A16, ## __VA_ARGS__), /* ULT GT2 mobile */ \
MACRO__(0x0A1A, ## __VA_ARGS__), /* ULT GT2 server */ \
MACRO__(0x0A1B, ## __VA_ARGS__) /* ULT GT2 reserved */ \
#define INTEL_HSW_ULX_GT2_IDS(MACRO__, ...) \
MACRO__(0x0A1E, ## __VA_ARGS__) /* ULX GT2 mobile */ \
#define INTEL_HSW_GT2_IDS(MACRO__, ...) \
INTEL_HSW_ULT_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_HSW_ULX_GT2_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x0412, ## __VA_ARGS__), /* GT2 desktop */ \
MACRO__(0x0416, ## __VA_ARGS__), /* GT2 mobile */ \
MACRO__(0x041A, ## __VA_ARGS__), /* GT2 server */ \
MACRO__(0x041B, ## __VA_ARGS__), /* GT2 reserved */ \
MACRO__(0x041E, ## __VA_ARGS__), /* GT2 reserved */ \
MACRO__(0x0C12, ## __VA_ARGS__), /* SDV GT2 desktop */ \
MACRO__(0x0C16, ## __VA_ARGS__), /* SDV GT2 mobile */ \
MACRO__(0x0C1A, ## __VA_ARGS__), /* SDV GT2 server */ \
MACRO__(0x0C1B, ## __VA_ARGS__), /* SDV GT2 reserved */ \
MACRO__(0x0C1E, ## __VA_ARGS__), /* SDV GT2 reserved */ \
MACRO__(0x0D12, ## __VA_ARGS__), /* CRW GT2 desktop */ \
MACRO__(0x0D16, ## __VA_ARGS__), /* CRW GT2 mobile */ \
MACRO__(0x0D1A, ## __VA_ARGS__), /* CRW GT2 server */ \
MACRO__(0x0D1B, ## __VA_ARGS__), /* CRW GT2 reserved */ \
MACRO__(0x0D1E, ## __VA_ARGS__) /* CRW GT2 reserved */
#define INTEL_HSW_ULT_GT3_IDS(MACRO__, ...) \
MACRO__(0x0A22, ## __VA_ARGS__), /* ULT GT3 desktop */ \
MACRO__(0x0A26, ## __VA_ARGS__), /* ULT GT3 mobile */ \
MACRO__(0x0A2A, ## __VA_ARGS__), /* ULT GT3 server */ \
MACRO__(0x0A2B, ## __VA_ARGS__), /* ULT GT3 reserved */ \
MACRO__(0x0A2E, ## __VA_ARGS__) /* ULT GT3 reserved */
#define INTEL_HSW_GT3_IDS(MACRO__, ...) \
INTEL_HSW_ULT_GT3_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x0422, ## __VA_ARGS__), /* GT3 desktop */ \
MACRO__(0x0426, ## __VA_ARGS__), /* GT3 mobile */ \
MACRO__(0x042A, ## __VA_ARGS__), /* GT3 server */ \
MACRO__(0x042B, ## __VA_ARGS__), /* GT3 reserved */ \
MACRO__(0x042E, ## __VA_ARGS__), /* GT3 reserved */ \
MACRO__(0x0C22, ## __VA_ARGS__), /* SDV GT3 desktop */ \
MACRO__(0x0C26, ## __VA_ARGS__), /* SDV GT3 mobile */ \
MACRO__(0x0C2A, ## __VA_ARGS__), /* SDV GT3 server */ \
MACRO__(0x0C2B, ## __VA_ARGS__), /* SDV GT3 reserved */ \
MACRO__(0x0C2E, ## __VA_ARGS__), /* SDV GT3 reserved */ \
MACRO__(0x0D22, ## __VA_ARGS__), /* CRW GT3 desktop */ \
MACRO__(0x0D26, ## __VA_ARGS__), /* CRW GT3 mobile */ \
MACRO__(0x0D2A, ## __VA_ARGS__), /* CRW GT3 server */ \
MACRO__(0x0D2B, ## __VA_ARGS__), /* CRW GT3 reserved */ \
MACRO__(0x0D2E, ## __VA_ARGS__) /* CRW GT3 reserved */
#define INTEL_HSW_IDS(MACRO__, ...) \
INTEL_HSW_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_HSW_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_HSW_GT3_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_VLV_IDS(MACRO__, ...) \
MACRO__(0x0f30, ## __VA_ARGS__), \
MACRO__(0x0f31, ## __VA_ARGS__), \
MACRO__(0x0f32, ## __VA_ARGS__), \
MACRO__(0x0f33, ## __VA_ARGS__)
#define INTEL_BDW_ULT_GT1_IDS(MACRO__, ...) \
MACRO__(0x1606, ## __VA_ARGS__), /* GT1 ULT */ \
MACRO__(0x160B, ## __VA_ARGS__) /* GT1 Iris */
#define INTEL_BDW_ULX_GT1_IDS(MACRO__, ...) \
MACRO__(0x160E, ## __VA_ARGS__) /* GT1 ULX */
#define INTEL_BDW_GT1_IDS(MACRO__, ...) \
INTEL_BDW_ULT_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_BDW_ULX_GT1_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x1602, ## __VA_ARGS__), /* GT1 ULT */ \
MACRO__(0x160A, ## __VA_ARGS__), /* GT1 Server */ \
MACRO__(0x160D, ## __VA_ARGS__) /* GT1 Workstation */
#define INTEL_BDW_ULT_GT2_IDS(MACRO__, ...) \
MACRO__(0x1616, ## __VA_ARGS__), /* GT2 ULT */ \
MACRO__(0x161B, ## __VA_ARGS__) /* GT2 ULT */
#define INTEL_BDW_ULX_GT2_IDS(MACRO__, ...) \
MACRO__(0x161E, ## __VA_ARGS__) /* GT2 ULX */
#define INTEL_BDW_GT2_IDS(MACRO__, ...) \
INTEL_BDW_ULT_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_BDW_ULX_GT2_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x1612, ## __VA_ARGS__), /* GT2 Halo */ \
MACRO__(0x161A, ## __VA_ARGS__), /* GT2 Server */ \
MACRO__(0x161D, ## __VA_ARGS__) /* GT2 Workstation */
#define INTEL_BDW_ULT_GT3_IDS(MACRO__, ...) \
MACRO__(0x1626, ## __VA_ARGS__), /* ULT */ \
MACRO__(0x162B, ## __VA_ARGS__) /* Iris */ \
#define INTEL_BDW_ULX_GT3_IDS(MACRO__, ...) \
MACRO__(0x162E, ## __VA_ARGS__) /* ULX */
#define INTEL_BDW_GT3_IDS(MACRO__, ...) \
INTEL_BDW_ULT_GT3_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_BDW_ULX_GT3_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x1622, ## __VA_ARGS__), /* ULT */ \
MACRO__(0x162A, ## __VA_ARGS__), /* Server */ \
MACRO__(0x162D, ## __VA_ARGS__) /* Workstation */
#define INTEL_BDW_ULT_RSVD_IDS(MACRO__, ...) \
MACRO__(0x1636, ## __VA_ARGS__), /* ULT */ \
MACRO__(0x163B, ## __VA_ARGS__) /* Iris */
#define INTEL_BDW_ULX_RSVD_IDS(MACRO__, ...) \
MACRO__(0x163E, ## __VA_ARGS__) /* ULX */
#define INTEL_BDW_RSVD_IDS(MACRO__, ...) \
INTEL_BDW_ULT_RSVD_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_BDW_ULX_RSVD_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x1632, ## __VA_ARGS__), /* ULT */ \
MACRO__(0x163A, ## __VA_ARGS__), /* Server */ \
MACRO__(0x163D, ## __VA_ARGS__) /* Workstation */
#define INTEL_BDW_IDS(MACRO__, ...) \
INTEL_BDW_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_BDW_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_BDW_GT3_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_BDW_RSVD_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_CHV_IDS(MACRO__, ...) \
MACRO__(0x22b0, ## __VA_ARGS__), \
MACRO__(0x22b1, ## __VA_ARGS__), \
MACRO__(0x22b2, ## __VA_ARGS__), \
MACRO__(0x22b3, ## __VA_ARGS__)
#define INTEL_SKL_ULT_GT1_IDS(MACRO__, ...) \
MACRO__(0x1906, ## __VA_ARGS__), /* ULT GT1 */ \
MACRO__(0x1913, ## __VA_ARGS__) /* ULT GT1.5 */
#define INTEL_SKL_ULX_GT1_IDS(MACRO__, ...) \
MACRO__(0x190E, ## __VA_ARGS__), /* ULX GT1 */ \
MACRO__(0x1915, ## __VA_ARGS__) /* ULX GT1.5 */
#define INTEL_SKL_GT1_IDS(MACRO__, ...) \
INTEL_SKL_ULT_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_SKL_ULX_GT1_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x1902, ## __VA_ARGS__), /* DT GT1 */ \
MACRO__(0x190A, ## __VA_ARGS__), /* SRV GT1 */ \
MACRO__(0x190B, ## __VA_ARGS__), /* Halo GT1 */ \
MACRO__(0x1917, ## __VA_ARGS__) /* DT GT1.5 */
#define INTEL_SKL_ULT_GT2_IDS(MACRO__, ...) \
MACRO__(0x1916, ## __VA_ARGS__), /* ULT GT2 */ \
MACRO__(0x1921, ## __VA_ARGS__) /* ULT GT2F */
#define INTEL_SKL_ULX_GT2_IDS(MACRO__, ...) \
MACRO__(0x191E, ## __VA_ARGS__) /* ULX GT2 */
#define INTEL_SKL_GT2_IDS(MACRO__, ...) \
INTEL_SKL_ULT_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_SKL_ULX_GT2_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x1912, ## __VA_ARGS__), /* DT GT2 */ \
MACRO__(0x191A, ## __VA_ARGS__), /* SRV GT2 */ \
MACRO__(0x191B, ## __VA_ARGS__), /* Halo GT2 */ \
MACRO__(0x191D, ## __VA_ARGS__) /* WKS GT2 */
#define INTEL_SKL_ULT_GT3_IDS(MACRO__, ...) \
MACRO__(0x1923, ## __VA_ARGS__), /* ULT GT3 */ \
MACRO__(0x1926, ## __VA_ARGS__), /* ULT GT3e */ \
MACRO__(0x1927, ## __VA_ARGS__) /* ULT GT3e */
#define INTEL_SKL_GT3_IDS(MACRO__, ...) \
INTEL_SKL_ULT_GT3_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x192A, ## __VA_ARGS__), /* SRV GT3 */ \
MACRO__(0x192B, ## __VA_ARGS__), /* Halo GT3e */ \
MACRO__(0x192D, ## __VA_ARGS__) /* SRV GT3e */
#define INTEL_SKL_GT4_IDS(MACRO__, ...) \
MACRO__(0x1932, ## __VA_ARGS__), /* DT GT4 */ \
MACRO__(0x193A, ## __VA_ARGS__), /* SRV GT4e */ \
MACRO__(0x193B, ## __VA_ARGS__), /* Halo GT4e */ \
MACRO__(0x193D, ## __VA_ARGS__) /* WKS GT4e */
#define INTEL_SKL_IDS(MACRO__, ...) \
INTEL_SKL_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_SKL_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_SKL_GT3_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_SKL_GT4_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_BXT_IDS(MACRO__, ...) \
MACRO__(0x0A84, ## __VA_ARGS__), \
MACRO__(0x1A84, ## __VA_ARGS__), \
MACRO__(0x1A85, ## __VA_ARGS__), \
MACRO__(0x5A84, ## __VA_ARGS__), /* APL HD Graphics 505 */ \
MACRO__(0x5A85, ## __VA_ARGS__) /* APL HD Graphics 500 */
#define INTEL_GLK_IDS(MACRO__, ...) \
MACRO__(0x3184, ## __VA_ARGS__), \
MACRO__(0x3185, ## __VA_ARGS__)
#define INTEL_KBL_ULT_GT1_IDS(MACRO__, ...) \
MACRO__(0x5906, ## __VA_ARGS__), /* ULT GT1 */ \
MACRO__(0x5913, ## __VA_ARGS__) /* ULT GT1.5 */
#define INTEL_KBL_ULX_GT1_IDS(MACRO__, ...) \
MACRO__(0x590E, ## __VA_ARGS__), /* ULX GT1 */ \
MACRO__(0x5915, ## __VA_ARGS__) /* ULX GT1.5 */
#define INTEL_KBL_GT1_IDS(MACRO__, ...) \
INTEL_KBL_ULT_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_KBL_ULX_GT1_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x5902, ## __VA_ARGS__), /* DT GT1 */ \
MACRO__(0x5908, ## __VA_ARGS__), /* Halo GT1 */ \
MACRO__(0x590A, ## __VA_ARGS__), /* SRV GT1 */ \
MACRO__(0x590B, ## __VA_ARGS__) /* Halo GT1 */
#define INTEL_KBL_ULT_GT2_IDS(MACRO__, ...) \
MACRO__(0x5916, ## __VA_ARGS__), /* ULT GT2 */ \
MACRO__(0x5921, ## __VA_ARGS__) /* ULT GT2F */
#define INTEL_KBL_ULX_GT2_IDS(MACRO__, ...) \
MACRO__(0x591E, ## __VA_ARGS__) /* ULX GT2 */
#define INTEL_KBL_GT2_IDS(MACRO__, ...) \
INTEL_KBL_ULT_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_KBL_ULX_GT2_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x5912, ## __VA_ARGS__), /* DT GT2 */ \
MACRO__(0x5917, ## __VA_ARGS__), /* Mobile GT2 */ \
MACRO__(0x591A, ## __VA_ARGS__), /* SRV GT2 */ \
MACRO__(0x591B, ## __VA_ARGS__), /* Halo GT2 */ \
MACRO__(0x591D, ## __VA_ARGS__) /* WKS GT2 */
#define INTEL_KBL_ULT_GT3_IDS(MACRO__, ...) \
MACRO__(0x5926, ## __VA_ARGS__) /* ULT GT3 */
#define INTEL_KBL_GT3_IDS(MACRO__, ...) \
INTEL_KBL_ULT_GT3_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x5923, ## __VA_ARGS__), /* ULT GT3 */ \
MACRO__(0x5927, ## __VA_ARGS__) /* ULT GT3 */
#define INTEL_KBL_GT4_IDS(MACRO__, ...) \
MACRO__(0x593B, ## __VA_ARGS__) /* Halo GT4 */
/* AML/KBL Y GT2 */
#define INTEL_AML_KBL_GT2_IDS(MACRO__, ...) \
MACRO__(0x591C, ## __VA_ARGS__), /* ULX GT2 */ \
MACRO__(0x87C0, ## __VA_ARGS__) /* ULX GT2 */
/* AML/CFL Y GT2 */
#define INTEL_AML_CFL_GT2_IDS(MACRO__, ...) \
MACRO__(0x87CA, ## __VA_ARGS__)
/* CML GT1 */
#define INTEL_CML_GT1_IDS(MACRO__, ...) \
MACRO__(0x9BA2, ## __VA_ARGS__), \
MACRO__(0x9BA4, ## __VA_ARGS__), \
MACRO__(0x9BA5, ## __VA_ARGS__), \
MACRO__(0x9BA8, ## __VA_ARGS__)
#define INTEL_CML_U_GT1_IDS(MACRO__, ...) \
MACRO__(0x9B21, ## __VA_ARGS__), \
MACRO__(0x9BAA, ## __VA_ARGS__), \
MACRO__(0x9BAC, ## __VA_ARGS__)
/* CML GT2 */
#define INTEL_CML_GT2_IDS(MACRO__, ...) \
MACRO__(0x9BC2, ## __VA_ARGS__), \
MACRO__(0x9BC4, ## __VA_ARGS__), \
MACRO__(0x9BC5, ## __VA_ARGS__), \
MACRO__(0x9BC6, ## __VA_ARGS__), \
MACRO__(0x9BC8, ## __VA_ARGS__), \
MACRO__(0x9BE6, ## __VA_ARGS__), \
MACRO__(0x9BF6, ## __VA_ARGS__)
#define INTEL_CML_U_GT2_IDS(MACRO__, ...) \
MACRO__(0x9B41, ## __VA_ARGS__), \
MACRO__(0x9BCA, ## __VA_ARGS__), \
MACRO__(0x9BCC, ## __VA_ARGS__)
#define INTEL_CML_IDS(MACRO__, ...) \
INTEL_CML_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_CML_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_CML_U_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_CML_U_GT2_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_KBL_IDS(MACRO__, ...) \
INTEL_KBL_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_KBL_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_KBL_GT3_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_KBL_GT4_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_AML_KBL_GT2_IDS(MACRO__, ## __VA_ARGS__)
/* CFL S */
#define INTEL_CFL_S_GT1_IDS(MACRO__, ...) \
MACRO__(0x3E90, ## __VA_ARGS__), /* SRV GT1 */ \
MACRO__(0x3E93, ## __VA_ARGS__), /* SRV GT1 */ \
MACRO__(0x3E99, ## __VA_ARGS__) /* SRV GT1 */
#define INTEL_CFL_S_GT2_IDS(MACRO__, ...) \
MACRO__(0x3E91, ## __VA_ARGS__), /* SRV GT2 */ \
MACRO__(0x3E92, ## __VA_ARGS__), /* SRV GT2 */ \
MACRO__(0x3E96, ## __VA_ARGS__), /* SRV GT2 */ \
MACRO__(0x3E98, ## __VA_ARGS__), /* SRV GT2 */ \
MACRO__(0x3E9A, ## __VA_ARGS__) /* SRV GT2 */
/* CFL H */
#define INTEL_CFL_H_GT1_IDS(MACRO__, ...) \
MACRO__(0x3E9C, ## __VA_ARGS__)
#define INTEL_CFL_H_GT2_IDS(MACRO__, ...) \
MACRO__(0x3E94, ## __VA_ARGS__), /* Halo GT2 */ \
MACRO__(0x3E9B, ## __VA_ARGS__) /* Halo GT2 */
/* CFL U GT2 */
#define INTEL_CFL_U_GT2_IDS(MACRO__, ...) \
MACRO__(0x3EA9, ## __VA_ARGS__)
/* CFL U GT3 */
#define INTEL_CFL_U_GT3_IDS(MACRO__, ...) \
MACRO__(0x3EA5, ## __VA_ARGS__), /* ULT GT3 */ \
MACRO__(0x3EA6, ## __VA_ARGS__), /* ULT GT3 */ \
MACRO__(0x3EA7, ## __VA_ARGS__), /* ULT GT3 */ \
MACRO__(0x3EA8, ## __VA_ARGS__) /* ULT GT3 */
#define INTEL_CFL_IDS(MACRO__, ...) \
INTEL_CFL_S_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_CFL_S_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_CFL_H_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_CFL_H_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_CFL_U_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_CFL_U_GT3_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_AML_CFL_GT2_IDS(MACRO__, ## __VA_ARGS__)
/* WHL/CFL U GT1 */
#define INTEL_WHL_U_GT1_IDS(MACRO__, ...) \
MACRO__(0x3EA1, ## __VA_ARGS__), \
MACRO__(0x3EA4, ## __VA_ARGS__)
/* WHL/CFL U GT2 */
#define INTEL_WHL_U_GT2_IDS(MACRO__, ...) \
MACRO__(0x3EA0, ## __VA_ARGS__), \
MACRO__(0x3EA3, ## __VA_ARGS__)
/* WHL/CFL U GT3 */
#define INTEL_WHL_U_GT3_IDS(MACRO__, ...) \
MACRO__(0x3EA2, ## __VA_ARGS__)
#define INTEL_WHL_IDS(MACRO__, ...) \
INTEL_WHL_U_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_WHL_U_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_WHL_U_GT3_IDS(MACRO__, ## __VA_ARGS__)
/* CNL */
#define INTEL_CNL_PORT_F_IDS(MACRO__, ...) \
MACRO__(0x5A44, ## __VA_ARGS__), \
MACRO__(0x5A4C, ## __VA_ARGS__), \
MACRO__(0x5A54, ## __VA_ARGS__), \
MACRO__(0x5A5C, ## __VA_ARGS__)
#define INTEL_CNL_IDS(MACRO__, ...) \
INTEL_CNL_PORT_F_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x5A40, ## __VA_ARGS__), \
MACRO__(0x5A41, ## __VA_ARGS__), \
MACRO__(0x5A42, ## __VA_ARGS__), \
MACRO__(0x5A49, ## __VA_ARGS__), \
MACRO__(0x5A4A, ## __VA_ARGS__), \
MACRO__(0x5A50, ## __VA_ARGS__), \
MACRO__(0x5A51, ## __VA_ARGS__), \
MACRO__(0x5A52, ## __VA_ARGS__), \
MACRO__(0x5A59, ## __VA_ARGS__), \
MACRO__(0x5A5A, ## __VA_ARGS__)
/* ICL */
#define INTEL_ICL_PORT_F_IDS(MACRO__, ...) \
MACRO__(0x8A50, ## __VA_ARGS__), \
MACRO__(0x8A52, ## __VA_ARGS__), \
MACRO__(0x8A53, ## __VA_ARGS__), \
MACRO__(0x8A54, ## __VA_ARGS__), \
MACRO__(0x8A56, ## __VA_ARGS__), \
MACRO__(0x8A57, ## __VA_ARGS__), \
MACRO__(0x8A58, ## __VA_ARGS__), \
MACRO__(0x8A59, ## __VA_ARGS__), \
MACRO__(0x8A5A, ## __VA_ARGS__), \
MACRO__(0x8A5B, ## __VA_ARGS__), \
MACRO__(0x8A5C, ## __VA_ARGS__), \
MACRO__(0x8A70, ## __VA_ARGS__), \
MACRO__(0x8A71, ## __VA_ARGS__)
#define INTEL_ICL_IDS(MACRO__, ...) \
INTEL_ICL_PORT_F_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0x8A51, ## __VA_ARGS__), \
MACRO__(0x8A5D, ## __VA_ARGS__)
/* EHL */
#define INTEL_EHL_IDS(MACRO__, ...) \
MACRO__(0x4541, ## __VA_ARGS__), \
MACRO__(0x4551, ## __VA_ARGS__), \
MACRO__(0x4555, ## __VA_ARGS__), \
MACRO__(0x4557, ## __VA_ARGS__), \
MACRO__(0x4570, ## __VA_ARGS__), \
MACRO__(0x4571, ## __VA_ARGS__)
/* JSL */
#define INTEL_JSL_IDS(MACRO__, ...) \
MACRO__(0x4E51, ## __VA_ARGS__), \
MACRO__(0x4E55, ## __VA_ARGS__), \
MACRO__(0x4E57, ## __VA_ARGS__), \
MACRO__(0x4E61, ## __VA_ARGS__), \
MACRO__(0x4E71, ## __VA_ARGS__)
/* TGL */
#define INTEL_TGL_GT1_IDS(MACRO__, ...) \
MACRO__(0x9A60, ## __VA_ARGS__), \
MACRO__(0x9A68, ## __VA_ARGS__), \
MACRO__(0x9A70, ## __VA_ARGS__)
#define INTEL_TGL_GT2_IDS(MACRO__, ...) \
MACRO__(0x9A40, ## __VA_ARGS__), \
MACRO__(0x9A49, ## __VA_ARGS__), \
MACRO__(0x9A59, ## __VA_ARGS__), \
MACRO__(0x9A78, ## __VA_ARGS__), \
MACRO__(0x9AC0, ## __VA_ARGS__), \
MACRO__(0x9AC9, ## __VA_ARGS__), \
MACRO__(0x9AD9, ## __VA_ARGS__), \
MACRO__(0x9AF8, ## __VA_ARGS__)
#define INTEL_TGL_IDS(MACRO__, ...) \
INTEL_TGL_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_TGL_GT2_IDS(MACRO__, ## __VA_ARGS__)
/* RKL */
#define INTEL_RKL_IDS(MACRO__, ...) \
MACRO__(0x4C80, ## __VA_ARGS__), \
MACRO__(0x4C8A, ## __VA_ARGS__), \
MACRO__(0x4C8B, ## __VA_ARGS__), \
MACRO__(0x4C8C, ## __VA_ARGS__), \
MACRO__(0x4C90, ## __VA_ARGS__), \
MACRO__(0x4C9A, ## __VA_ARGS__)
/* DG1 */
#define INTEL_DG1_IDS(MACRO__, ...) \
MACRO__(0x4905, ## __VA_ARGS__), \
MACRO__(0x4906, ## __VA_ARGS__), \
MACRO__(0x4907, ## __VA_ARGS__), \
MACRO__(0x4908, ## __VA_ARGS__), \
MACRO__(0x4909, ## __VA_ARGS__)
/* ADL-S */
#define INTEL_ADLS_IDS(MACRO__, ...) \
MACRO__(0x4680, ## __VA_ARGS__), \
MACRO__(0x4682, ## __VA_ARGS__), \
MACRO__(0x4688, ## __VA_ARGS__), \
MACRO__(0x468A, ## __VA_ARGS__), \
MACRO__(0x468B, ## __VA_ARGS__), \
MACRO__(0x4690, ## __VA_ARGS__), \
MACRO__(0x4692, ## __VA_ARGS__), \
MACRO__(0x4693, ## __VA_ARGS__)
/* ADL-P */
#define INTEL_ADLP_IDS(MACRO__, ...) \
MACRO__(0x46A0, ## __VA_ARGS__), \
MACRO__(0x46A1, ## __VA_ARGS__), \
MACRO__(0x46A2, ## __VA_ARGS__), \
MACRO__(0x46A3, ## __VA_ARGS__), \
MACRO__(0x46A6, ## __VA_ARGS__), \
MACRO__(0x46A8, ## __VA_ARGS__), \
MACRO__(0x46AA, ## __VA_ARGS__), \
MACRO__(0x462A, ## __VA_ARGS__), \
MACRO__(0x4626, ## __VA_ARGS__), \
MACRO__(0x4628, ## __VA_ARGS__), \
MACRO__(0x46B0, ## __VA_ARGS__), \
MACRO__(0x46B1, ## __VA_ARGS__), \
MACRO__(0x46B2, ## __VA_ARGS__), \
MACRO__(0x46B3, ## __VA_ARGS__), \
MACRO__(0x46C0, ## __VA_ARGS__), \
MACRO__(0x46C1, ## __VA_ARGS__), \
MACRO__(0x46C2, ## __VA_ARGS__), \
MACRO__(0x46C3, ## __VA_ARGS__)
/* ADL-N */
#define INTEL_ADLN_IDS(MACRO__, ...) \
MACRO__(0x46D0, ## __VA_ARGS__), \
MACRO__(0x46D1, ## __VA_ARGS__), \
MACRO__(0x46D2, ## __VA_ARGS__), \
MACRO__(0x46D3, ## __VA_ARGS__), \
MACRO__(0x46D4, ## __VA_ARGS__)
/* RPL-S */
#define INTEL_RPLS_IDS(MACRO__, ...) \
MACRO__(0xA780, ## __VA_ARGS__), \
MACRO__(0xA781, ## __VA_ARGS__), \
MACRO__(0xA782, ## __VA_ARGS__), \
MACRO__(0xA783, ## __VA_ARGS__), \
MACRO__(0xA788, ## __VA_ARGS__), \
MACRO__(0xA789, ## __VA_ARGS__), \
MACRO__(0xA78A, ## __VA_ARGS__), \
MACRO__(0xA78B, ## __VA_ARGS__)
/* RPL-U */
#define INTEL_RPLU_IDS(MACRO__, ...) \
MACRO__(0xA721, ## __VA_ARGS__), \
MACRO__(0xA7A1, ## __VA_ARGS__), \
MACRO__(0xA7A9, ## __VA_ARGS__), \
MACRO__(0xA7AC, ## __VA_ARGS__), \
MACRO__(0xA7AD, ## __VA_ARGS__)
/* RPL-P */
#define INTEL_RPLP_IDS(MACRO__, ...) \
MACRO__(0xA720, ## __VA_ARGS__), \
MACRO__(0xA7A0, ## __VA_ARGS__), \
MACRO__(0xA7A8, ## __VA_ARGS__), \
MACRO__(0xA7AA, ## __VA_ARGS__), \
MACRO__(0xA7AB, ## __VA_ARGS__)
/* DG2 */
#define INTEL_DG2_G10_IDS(MACRO__, ...) \
MACRO__(0x5690, ## __VA_ARGS__), \
MACRO__(0x5691, ## __VA_ARGS__), \
MACRO__(0x5692, ## __VA_ARGS__), \
MACRO__(0x56A0, ## __VA_ARGS__), \
MACRO__(0x56A1, ## __VA_ARGS__), \
MACRO__(0x56A2, ## __VA_ARGS__), \
MACRO__(0x56BE, ## __VA_ARGS__), \
MACRO__(0x56BF, ## __VA_ARGS__)
#define INTEL_DG2_G11_IDS(MACRO__, ...) \
MACRO__(0x5693, ## __VA_ARGS__), \
MACRO__(0x5694, ## __VA_ARGS__), \
MACRO__(0x5695, ## __VA_ARGS__), \
MACRO__(0x56A5, ## __VA_ARGS__), \
MACRO__(0x56A6, ## __VA_ARGS__), \
MACRO__(0x56B0, ## __VA_ARGS__), \
MACRO__(0x56B1, ## __VA_ARGS__), \
MACRO__(0x56BA, ## __VA_ARGS__), \
MACRO__(0x56BB, ## __VA_ARGS__), \
MACRO__(0x56BC, ## __VA_ARGS__), \
MACRO__(0x56BD, ## __VA_ARGS__)
#define INTEL_DG2_G12_IDS(MACRO__, ...) \
MACRO__(0x5696, ## __VA_ARGS__), \
MACRO__(0x5697, ## __VA_ARGS__), \
MACRO__(0x56A3, ## __VA_ARGS__), \
MACRO__(0x56A4, ## __VA_ARGS__), \
MACRO__(0x56B2, ## __VA_ARGS__), \
MACRO__(0x56B3, ## __VA_ARGS__)
#define INTEL_DG2_IDS(MACRO__, ...) \
INTEL_DG2_G10_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_DG2_G11_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_DG2_G12_IDS(MACRO__, ## __VA_ARGS__)
#define INTEL_ATS_M150_IDS(MACRO__, ...) \
MACRO__(0x56C0, ## __VA_ARGS__), \
MACRO__(0x56C2, ## __VA_ARGS__)
#define INTEL_ATS_M75_IDS(MACRO__, ...) \
MACRO__(0x56C1, ## __VA_ARGS__)
#define INTEL_ATS_M_IDS(MACRO__, ...) \
INTEL_ATS_M150_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_ATS_M75_IDS(MACRO__, ## __VA_ARGS__)
/* MTL */
#define INTEL_MTL_IDS(MACRO__, ...) \
MACRO__(0x7D40, ## __VA_ARGS__), \
MACRO__(0x7D41, ## __VA_ARGS__), \
MACRO__(0x7D45, ## __VA_ARGS__), \
MACRO__(0x7D51, ## __VA_ARGS__), \
MACRO__(0x7D55, ## __VA_ARGS__), \
MACRO__(0x7D60, ## __VA_ARGS__), \
MACRO__(0x7D67, ## __VA_ARGS__), \
MACRO__(0x7DD1, ## __VA_ARGS__), \
MACRO__(0x7DD5, ## __VA_ARGS__)
#endif /* _I915_PCIIDS_H */
@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2022 Intel Corporation
*/
#ifndef _I915_PCIIDS_LOCAL_H_
#define _I915_PCIIDS_LOCAL_H_
#include "i915_pciids.h"
/* MTL perf */
#ifndef INTEL_MTL_M_IDS
#define INTEL_MTL_M_IDS(MACRO__, ...) \
MACRO__(0x7D60, ## __VA_ARGS__), \
MACRO__(0x7D67, ## __VA_ARGS__)
#endif
#ifndef INTEL_MTL_P_GT2_IDS
#define INTEL_MTL_P_GT2_IDS(MACRO__, ...) \
MACRO__(0x7D45, ## __VA_ARGS__)
#endif
#ifndef INTEL_MTL_P_GT3_IDS
#define INTEL_MTL_P_GT3_IDS(MACRO__, ...) \
MACRO__(0x7D55, ## __VA_ARGS__), \
MACRO__(0x7DD5, ## __VA_ARGS__)
#endif
#ifndef INTEL_MTL_P_IDS
#define INTEL_MTL_P_IDS(MACRO__, ...) \
INTEL_MTL_P_GT2_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_MTL_P_GT3_IDS(MACRO__, ## __VA_ARGS__)
#endif
#ifndef INTEL_ARL_GT1_IDS
#define INTEL_ARL_GT1_IDS(MACRO__, ...) \
MACRO__(0x7D41, ## __VA_ARGS__), \
MACRO__(0x7D67, ## __VA_ARGS__)
#endif
#ifndef INTEL_ARL_GT2_IDS
#define INTEL_ARL_GT2_IDS(MACRO__, ...) \
MACRO__(0x7D51, ## __VA_ARGS__), \
MACRO__(0x7DD1, ## __VA_ARGS__)
#endif
#ifndef INTEL_ARL_IDS
#define INTEL_ARL_IDS(MACRO__, ...) \
INTEL_ARL_GT1_IDS(MACRO__, ## __VA_ARGS__), \
INTEL_ARL_GT2_IDS(MACRO__, ## __VA_ARGS__)
#endif
/* PVC */
#ifndef INTEL_PVC_IDS
#define INTEL_PVC_IDS(MACRO__, ...) \
MACRO__(0x0BD0, ## __VA_ARGS__), \
MACRO__(0x0BD1, ## __VA_ARGS__), \
MACRO__(0x0BD2, ## __VA_ARGS__), \
MACRO__(0x0BD5, ## __VA_ARGS__), \
MACRO__(0x0BD6, ## __VA_ARGS__), \
MACRO__(0x0BD7, ## __VA_ARGS__), \
MACRO__(0x0BD8, ## __VA_ARGS__), \
MACRO__(0x0BD9, ## __VA_ARGS__), \
MACRO__(0x0BDA, ## __VA_ARGS__), \
MACRO__(0x0BDB, ## __VA_ARGS__)
#endif
#endif /* _I915_PCIIDS_LOCAL_H */
@@ -0,0 +1,210 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#include <sys/sysmacros.h>
#include <linux/limits.h>
#endif
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include "igt_perf.h"
static char *bus_address(int i915, char *path, int pathlen)
{
struct stat st;
int len = -1;
int dir;
char *s;
if (fstat(i915, &st) || !S_ISCHR(st.st_mode))
return NULL;
snprintf(path, pathlen, "/sys/dev/char/%d:%d",
major(st.st_rdev), minor(st.st_rdev));
dir = open(path, O_RDONLY);
if (dir != -1) {
len = readlinkat(dir, "device", path, pathlen - 1);
close(dir);
}
if (len < 0)
return NULL;
path[len] = '\0';
/* strip off the relative path */
s = strrchr(path, '/');
if (s)
memmove(path, s + 1, len - (s - path) + 1);
return path;
}
const char *i915_perf_device(int i915, char *buf, int buflen)
{
char *s;
#define prefix "i915_"
#define plen strlen(prefix)
if (!buf || buflen < plen)
return "i915";
memcpy(buf, prefix, plen);
if (!bus_address(i915, buf + plen, buflen - plen) ||
strcmp(buf + plen, "0000:00:02.0") == 0) /* legacy name for igfx */
buf[plen - 1] = '\0';
/* Convert all colons in the address to '_', thanks perf! */
for (s = buf; *s; s++)
if (*s == ':')
*s = '_';
return buf;
}
const char *xe_perf_device(int xe, char *buf, int buflen)
{
char *s;
char pref[] = "xe_";
int len = strlen(pref);
if (!buf || buflen < len)
return "xe";
memcpy(buf, pref, len);
if (!bus_address(xe, buf + len, buflen - len))
buf[len - 1] = '\0';
/* Convert all colons in the address to '_', thanks perf! */
for (s = buf; *s; s++)
if (*s == ':')
*s = '_';
return buf;
}
uint64_t xe_perf_type_id(int xe)
{
char buf[80];
return igt_perf_type_id(xe_perf_device(xe, buf, sizeof(buf)));
}
uint64_t i915_perf_type_id(int i915)
{
char buf[80];
return igt_perf_type_id(i915_perf_device(i915, buf, sizeof(buf)));
}
uint64_t igt_perf_type_id(const char *device)
{
char buf[64];
ssize_t ret;
int fd;
snprintf(buf, sizeof(buf),
"/sys/bus/event_source/devices/%s/type", device);
fd = open(buf, O_RDONLY);
if (fd < 0)
return 0;
ret = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (ret < 1)
return 0;
buf[ret] = '\0';
return strtoull(buf, NULL, 0);
}
int igt_perf_events_dir(int i915)
{
char buf[80];
char path[PATH_MAX];
i915_perf_device(i915, buf, sizeof(buf));
snprintf(path, sizeof(path), "/sys/bus/event_source/devices/%s/events", buf);
return open(path, O_RDONLY);
}
static int
_perf_open(uint64_t type, uint64_t config, int group, uint64_t format)
{
struct perf_event_attr attr = { };
int nr_cpus = get_nprocs_conf();
int cpu = 0, ret;
attr.type = type;
if (attr.type == 0)
return -ENOENT;
if (group >= 0)
format &= ~PERF_FORMAT_GROUP;
attr.read_format = format;
attr.config = config;
attr.use_clockid = 1;
attr.clockid = CLOCK_MONOTONIC;
do {
ret = perf_event_open(&attr, -1, cpu++, group, 0);
} while ((ret < 0 && errno == EINVAL) && (cpu < nr_cpus));
return ret;
}
int perf_igfx_open(uint64_t config)
{
return _perf_open(igt_perf_type_id("i915"), config, -1,
PERF_FORMAT_TOTAL_TIME_ENABLED);
}
int perf_igfx_open_group(uint64_t config, int group)
{
return _perf_open(igt_perf_type_id("i915"), config, group,
PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_GROUP);
}
int perf_xe_open(int xe, uint64_t config)
{
return _perf_open(xe_perf_type_id(xe), config, -1,
PERF_FORMAT_TOTAL_TIME_ENABLED);
}
int perf_i915_open(int i915, uint64_t config)
{
return _perf_open(i915_perf_type_id(i915), config, -1,
PERF_FORMAT_TOTAL_TIME_ENABLED);
}
int perf_i915_open_group(int i915, uint64_t config, int group)
{
return _perf_open(i915_perf_type_id(i915), config, group,
PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_GROUP);
}
int igt_perf_open(uint64_t type, uint64_t config)
{
return _perf_open(type, config, -1,
PERF_FORMAT_TOTAL_TIME_ENABLED);
}
int igt_perf_open_group(uint64_t type, uint64_t config, int group)
{
return _perf_open(type, config, group,
PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_GROUP);
}
@@ -0,0 +1,75 @@
/*
* Copyright © 2017 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#ifndef I915_PERF_H
#define I915_PERF_H
#include <stdint.h>
#ifdef __linux__
#include <linux/perf_event.h>
#endif
//#include "igt_gt.h"
static inline int
perf_event_open(struct perf_event_attr *attr,
pid_t pid,
int cpu,
int group_fd,
unsigned long flags)
{
#ifndef __NR_perf_event_open
#if defined(__i386__)
#define __NR_perf_event_open 336
#elif defined(__x86_64__)
#define __NR_perf_event_open 298
#else
#define __NR_perf_event_open 0
#endif
#endif
attr->size = sizeof(*attr);
return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
}
uint64_t igt_perf_type_id(const char *device);
int igt_perf_events_dir(int i915);
int igt_perf_open(uint64_t type, uint64_t config);
int igt_perf_open_group(uint64_t type, uint64_t config, int group);
const char *i915_perf_device(int i915, char *buf, int buflen);
uint64_t i915_perf_type_id(int i915);
const char *xe_perf_device(int xe, char *buf, int buflen);
uint64_t xe_perf_type_id(int);
int perf_igfx_open(uint64_t config);
int perf_igfx_open_group(uint64_t config, int group);
int perf_i915_open(int i915, uint64_t config);
int perf_i915_open_group(int i915, uint64_t config, int group);
int perf_xe_open(int xe, uint64_t config);
#endif /* I915_PERF_H */
@@ -0,0 +1,233 @@
/*
* Copyright © 2007 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Authors:
* Eric Anholt <eric@anholt.net>
*
*/
#ifndef _INTEL_CHIPSET_H
#define _INTEL_CHIPSET_H
#include <stdbool.h>
#include <stdint.h>
#define BIT(x) (1ul <<(x))
struct intel_device_info {
unsigned graphics_ver;
unsigned graphics_rel;
unsigned display_ver;
unsigned gt; /* 0 if unknown */
bool has_4tile : 1;
bool has_flatccs : 1;
bool has_oam : 1;
bool is_mobile : 1;
bool is_whitney : 1;
bool is_almador : 1;
bool is_brookdale : 1;
bool is_montara : 1;
bool is_springdale : 1;
bool is_grantsdale : 1;
bool is_alviso : 1;
bool is_lakeport : 1;
bool is_calistoga : 1;
bool is_bearlake : 1;
bool is_pineview : 1;
bool is_broadwater : 1;
bool is_crestline : 1;
bool is_eaglelake : 1;
bool is_cantiga : 1;
bool is_ironlake : 1;
bool is_arrandale : 1;
bool is_sandybridge : 1;
bool is_ivybridge : 1;
bool is_valleyview : 1;
bool is_haswell : 1;
bool is_broadwell : 1;
bool is_cherryview : 1;
bool is_skylake : 1;
bool is_broxton : 1;
bool is_kabylake : 1;
bool is_geminilake : 1;
bool is_coffeelake : 1;
bool is_cometlake : 1;
bool is_cannonlake : 1;
bool is_icelake : 1;
bool is_elkhartlake : 1;
bool is_jasperlake : 1;
bool is_tigerlake : 1;
bool is_rocketlake : 1;
bool is_dg1 : 1;
bool is_dg2 : 1;
bool is_alderlake_s : 1;
bool is_raptorlake_s : 1;
bool is_alderlake_p : 1;
bool is_alderlake_n : 1;
bool is_meteorlake : 1;
bool is_pontevecchio : 1;
bool is_lunarlake : 1;
bool is_battlemage : 1;
const char *codename;
};
const struct intel_device_info *intel_get_device_info(uint16_t devid) __attribute__((pure));
extern enum pch_type intel_pch;
enum pch_type {
PCH_NONE,
PCH_IBX,
PCH_CPT,
PCH_LPT,
};
void intel_check_pch(void);
#define HAS_IBX (intel_pch == PCH_IBX)
#define HAS_CPT (intel_pch == PCH_CPT)
#define HAS_LPT (intel_pch == PCH_LPT)
#define IP_VER(ver, rel) ((ver) << 8 | (rel))
/* Exclude chipset #defines, they just add noise */
#ifndef __GTK_DOC_IGNORE__
#define PCI_CHIP_I810 0x7121
#define PCI_CHIP_I810_DC100 0x7123
#define PCI_CHIP_I810_E 0x7125
#define PCI_CHIP_I815 0x1132
#define PCI_CHIP_I830_M 0x3577
#define PCI_CHIP_845_G 0x2562
#define PCI_CHIP_I854_G 0x358e
#define PCI_CHIP_I855_GM 0x3582
#define PCI_CHIP_I865_G 0x2572
#define PCI_CHIP_I915_G 0x2582
#define PCI_CHIP_E7221_G 0x258A
#define PCI_CHIP_I915_GM 0x2592
#define PCI_CHIP_I945_G 0x2772
#define PCI_CHIP_I945_GM 0x27A2
#define PCI_CHIP_I945_GME 0x27AE
#define PCI_CHIP_I965_G 0x29A2
#define PCI_CHIP_I965_Q 0x2992
#define PCI_CHIP_I965_G_1 0x2982
#define PCI_CHIP_I946_GZ 0x2972
#define PCI_CHIP_I965_GM 0x2A02
#define PCI_CHIP_I965_GME 0x2A12
#define PCI_CHIP_GM45_GM 0x2A42
#define PCI_CHIP_Q45_G 0x2E12
#define PCI_CHIP_G45_G 0x2E22
#define PCI_CHIP_G41_G 0x2E32
#endif /* __GTK_DOC_IGNORE__ */
#define IS_915G(devid) (intel_get_device_info(devid)->is_grantsdale)
#define IS_915GM(devid) (intel_get_device_info(devid)->is_alviso)
#define IS_915(devid) (IS_915G(devid) || IS_915GM(devid))
#define IS_945G(devid) (intel_get_device_info(devid)->is_lakeport)
#define IS_945GM(devid) (intel_get_device_info(devid)->is_calistoga)
#define IS_945(devid) (IS_945G(devid) || \
IS_945GM(devid) || \
IS_G33(devid))
#define IS_PINEVIEW(devid) (intel_get_device_info(devid)->is_pineview)
#define IS_G33(devid) (intel_get_device_info(devid)->is_bearlake || \
intel_get_device_info(devid)->is_pineview)
#define IS_BROADWATER(devid) (intel_get_device_info(devid)->is_broadwater)
#define IS_CRESTLINE(devid) (intel_get_device_info(devid)->is_crestline)
#define IS_GM45(devid) (intel_get_device_info(devid)->is_cantiga)
#define IS_G45(devid) (intel_get_device_info(devid)->is_eaglelake)
#define IS_G4X(devid) (IS_G45(devid) || IS_GM45(devid))
#define IS_IRONLAKE(devid) (intel_get_device_info(devid)->is_ironlake)
#define IS_ARRANDALE(devid) (intel_get_device_info(devid)->is_arrandale)
#define IS_SANDYBRIDGE(devid) (intel_get_device_info(devid)->is_sandybridge)
#define IS_IVYBRIDGE(devid) (intel_get_device_info(devid)->is_ivybridge)
#define IS_VALLEYVIEW(devid) (intel_get_device_info(devid)->is_valleyview)
#define IS_HASWELL(devid) (intel_get_device_info(devid)->is_haswell)
#define IS_BROADWELL(devid) (intel_get_device_info(devid)->is_broadwell)
#define IS_CHERRYVIEW(devid) (intel_get_device_info(devid)->is_cherryview)
#define IS_SKYLAKE(devid) (intel_get_device_info(devid)->is_skylake)
#define IS_BROXTON(devid) (intel_get_device_info(devid)->is_broxton)
#define IS_KABYLAKE(devid) (intel_get_device_info(devid)->is_kabylake)
#define IS_GEMINILAKE(devid) (intel_get_device_info(devid)->is_geminilake)
#define IS_COFFEELAKE(devid) (intel_get_device_info(devid)->is_coffeelake)
#define IS_COMETLAKE(devid) (intel_get_device_info(devid)->is_cometlake)
#define IS_CANNONLAKE(devid) (intel_get_device_info(devid)->is_cannonlake)
#define IS_ICELAKE(devid) (intel_get_device_info(devid)->is_icelake)
#define IS_TIGERLAKE(devid) (intel_get_device_info(devid)->is_tigerlake)
#define IS_ROCKETLAKE(devid) (intel_get_device_info(devid)->is_rocketlake)
#define IS_DG1(devid) (intel_get_device_info(devid)->is_dg1)
#define IS_DG2(devid) (intel_get_device_info(devid)->is_dg2)
#define IS_ALDERLAKE_S(devid) (intel_get_device_info(devid)->is_alderlake_s)
#define IS_RAPTORLAKE_S(devid) (intel_get_device_info(devid)->is_raptorlake_s)
#define IS_ALDERLAKE_P(devid) (intel_get_device_info(devid)->is_alderlake_p)
#define IS_ALDERLAKE_N(devid) (intel_get_device_info(devid)->is_alderlake_n)
#define IS_METEORLAKE(devid) (intel_get_device_info(devid)->is_meteorlake)
#define IS_PONTEVECCHIO(devid) (intel_get_device_info(devid)->is_pontevecchio)
#define IS_LUNARLAKE(devid) (intel_get_device_info(devid)->is_lunarlake)
#define IS_BATTLEMAGE(devid) (intel_get_device_info(devid)->is_battlemage)
#define IS_GEN(devid, x) (intel_get_device_info(devid)->graphics_ver == x)
#define AT_LEAST_GEN(devid, x) (intel_get_device_info(devid)->graphics_ver >= x)
#define AT_LEAST_DISPLAY(devid, x) (intel_get_device_info(devid)->display_ver >= x)
#define IS_GEN2(devid) IS_GEN(devid, 2)
#define IS_GEN3(devid) IS_GEN(devid, 3)
#define IS_GEN4(devid) IS_GEN(devid, 4)
#define IS_GEN5(devid) IS_GEN(devid, 5)
#define IS_GEN6(devid) IS_GEN(devid, 6)
#define IS_GEN7(devid) IS_GEN(devid, 7)
#define IS_GEN8(devid) IS_GEN(devid, 8)
#define IS_GEN9(devid) IS_GEN(devid, 9)
#define IS_GEN10(devid) IS_GEN(devid, 10)
#define IS_GEN11(devid) IS_GEN(devid, 11)
#define IS_GEN12(devid) IS_GEN(devid, 12)
#define IS_MOBILE(devid) (intel_get_device_info(devid)->is_mobile)
#define IS_965(devid) AT_LEAST_GEN(devid, 4)
#define HAS_BSD_RING(devid) AT_LEAST_GEN(devid, 5)
#define HAS_BLT_RING(devid) AT_LEAST_GEN(devid, 6)
#define HAS_PCH_SPLIT(devid) (AT_LEAST_GEN(devid, 5) && \
!(IS_VALLEYVIEW(devid) || \
IS_CHERRYVIEW(devid) || \
IS_BROXTON(devid)))
#define HAS_4TILE(devid) (intel_get_device_info(devid)->has_4tile)
#define HAS_FLATCCS(devid) (intel_get_device_info(devid)->has_flatccs)
#define HAS_OAM(devid) (intel_get_device_info(devid)->has_oam)
#endif /* _INTEL_CHIPSET_H */
@@ -0,0 +1,653 @@
#include "intel_chipset.h"
#include "i915_pciids.h"
#include "i915_pciids_local.h"
#include "xe_pciids.h"
#include <strings.h> /* ffs() */
// from pciaccess.h
#define PCI_MATCH_ANY (~0U)
// from pciaccess.h
struct pci_id_match {
/**
* \name Device / vendor matching controls
*
* Control the search based on the device, vendor, subdevice, or subvendor
* IDs. Setting any of these fields to \c PCI_MATCH_ANY will cause the
* field to not be used in the comparison.
*/
/*@{*/
uint32_t vendor_id;
uint32_t device_id;
uint32_t subvendor_id;
uint32_t subdevice_id;
/*@}*/
/**
* \name Device class matching controls
*
*/
/*@{*/
uint32_t device_class;
uint32_t device_class_mask;
/*@}*/
intptr_t match_data;
};
static const struct intel_device_info intel_generic_info = {
.graphics_ver = 0,
.display_ver = 0,
};
static const struct intel_device_info intel_i810_info = {
.graphics_ver = 1,
.display_ver = 1,
.is_whitney = true,
.codename = "solano" /* 815 == "whitney" ? or vice versa? */
};
static const struct intel_device_info intel_i815_info = {
.graphics_ver = 1,
.display_ver = 1,
.is_whitney = true,
.codename = "whitney"
};
static const struct intel_device_info intel_i830_info = {
.graphics_ver = 2,
.display_ver = 2,
.is_almador = true,
.codename = "almador"
};
static const struct intel_device_info intel_i845_info = {
.graphics_ver = 2,
.display_ver = 2,
.is_brookdale = true,
.codename = "brookdale"
};
static const struct intel_device_info intel_i855_info = {
.graphics_ver = 2,
.display_ver = 2,
.is_mobile = true,
.is_montara = true,
.codename = "montara"
};
static const struct intel_device_info intel_i865_info = {
.graphics_ver = 2,
.display_ver = 2,
.is_springdale = true,
.codename = "spingdale"
};
static const struct intel_device_info intel_i915_info = {
.graphics_ver = 3,
.display_ver = 3,
.is_grantsdale = true,
.codename = "grantsdale"
};
static const struct intel_device_info intel_i915m_info = {
.graphics_ver = 3,
.display_ver = 3,
.is_mobile = true,
.is_alviso = true,
.codename = "alviso"
};
static const struct intel_device_info intel_i945_info = {
.graphics_ver = 3,
.display_ver = 3,
.is_lakeport = true,
.codename = "lakeport"
};
static const struct intel_device_info intel_i945m_info = {
.graphics_ver = 3,
.display_ver = 3,
.is_mobile = true,
.is_calistoga = true,
.codename = "calistoga"
};
static const struct intel_device_info intel_g33_info = {
.graphics_ver = 3,
.display_ver = 3,
.is_bearlake = true,
.codename = "bearlake"
};
static const struct intel_device_info intel_pineview_g_info = {
.graphics_ver = 3,
.display_ver = 3,
.is_pineview = true,
.codename = "pineview"
};
static const struct intel_device_info intel_pineview_m_info = {
.graphics_ver = 3,
.display_ver = 3,
.is_mobile = true,
.is_pineview = true,
.codename = "pineview"
};
static const struct intel_device_info intel_i965_info = {
.graphics_ver = 4,
.display_ver = 4,
.is_broadwater = true,
.codename = "broadwater"
};
static const struct intel_device_info intel_i965m_info = {
.graphics_ver = 4,
.display_ver = 4,
.is_mobile = true,
.is_crestline = true,
.codename = "crestline"
};
static const struct intel_device_info intel_g45_info = {
.graphics_ver = 4,
.display_ver = 4,
.is_eaglelake = true,
.codename = "eaglelake"
};
static const struct intel_device_info intel_gm45_info = {
.graphics_ver = 4,
.display_ver = 4,
.is_mobile = true,
.is_cantiga = true,
.codename = "cantiga"
};
static const struct intel_device_info intel_ironlake_info = {
.graphics_ver = 5,
.display_ver = 5,
.is_ironlake = true,
.codename = "ironlake" /* clarkdale? */
};
static const struct intel_device_info intel_ironlake_m_info = {
.graphics_ver = 5,
.display_ver = 5,
.is_mobile = true,
.is_arrandale = true,
.codename = "arrandale"
};
static const struct intel_device_info intel_sandybridge_info = {
.graphics_ver = 6,
.display_ver = 6,
.is_sandybridge = true,
.codename = "sandybridge"
};
static const struct intel_device_info intel_sandybridge_m_info = {
.graphics_ver = 6,
.display_ver = 6,
.is_mobile = true,
.is_sandybridge = true,
.codename = "sandybridge"
};
static const struct intel_device_info intel_ivybridge_info = {
.graphics_ver = 7,
.display_ver = 7,
.is_ivybridge = true,
.codename = "ivybridge"
};
static const struct intel_device_info intel_ivybridge_m_info = {
.graphics_ver = 7,
.display_ver = 7,
.is_mobile = true,
.is_ivybridge = true,
.codename = "ivybridge"
};
static const struct intel_device_info intel_valleyview_info = {
.graphics_ver = 7,
.display_ver = 7,
.is_valleyview = true,
.codename = "valleyview"
};
#define HASWELL_FIELDS \
.graphics_ver = 7, \
.display_ver = 7, \
.is_haswell = true, \
.codename = "haswell"
static const struct intel_device_info intel_haswell_gt1_info = {
HASWELL_FIELDS,
.gt = 1,
};
static const struct intel_device_info intel_haswell_gt2_info = {
HASWELL_FIELDS,
.gt = 2,
};
static const struct intel_device_info intel_haswell_gt3_info = {
HASWELL_FIELDS,
.gt = 3,
};
#define BROADWELL_FIELDS \
.graphics_ver = 8, \
.display_ver = 8, \
.is_broadwell = true, \
.codename = "broadwell"
static const struct intel_device_info intel_broadwell_gt1_info = {
BROADWELL_FIELDS,
.gt = 1,
};
static const struct intel_device_info intel_broadwell_gt2_info = {
BROADWELL_FIELDS,
.gt = 2,
};
static const struct intel_device_info intel_broadwell_gt3_info = {
BROADWELL_FIELDS,
.gt = 3,
};
static const struct intel_device_info intel_broadwell_unknown_info = {
BROADWELL_FIELDS,
};
static const struct intel_device_info intel_cherryview_info = {
.graphics_ver = 8,
.display_ver = 8,
.is_cherryview = true,
.codename = "cherryview"
};
#define SKYLAKE_FIELDS \
.graphics_ver = 9, \
.display_ver = 9, \
.codename = "skylake", \
.is_skylake = true
static const struct intel_device_info intel_skylake_gt1_info = {
SKYLAKE_FIELDS,
.gt = 1,
};
static const struct intel_device_info intel_skylake_gt2_info = {
SKYLAKE_FIELDS,
.gt = 2,
};
static const struct intel_device_info intel_skylake_gt3_info = {
SKYLAKE_FIELDS,
.gt = 3,
};
static const struct intel_device_info intel_skylake_gt4_info = {
SKYLAKE_FIELDS,
.gt = 4,
};
static const struct intel_device_info intel_broxton_info = {
.graphics_ver = 9,
.display_ver = 9,
.is_broxton = true,
.codename = "broxton"
};
#define KABYLAKE_FIELDS \
.graphics_ver = 9, \
.display_ver = 9, \
.is_kabylake = true, \
.codename = "kabylake"
static const struct intel_device_info intel_kabylake_gt1_info = {
KABYLAKE_FIELDS,
.gt = 1,
};
static const struct intel_device_info intel_kabylake_gt2_info = {
KABYLAKE_FIELDS,
.gt = 2,
};
static const struct intel_device_info intel_kabylake_gt3_info = {
KABYLAKE_FIELDS,
.gt = 3,
};
static const struct intel_device_info intel_kabylake_gt4_info = {
KABYLAKE_FIELDS,
.gt = 4,
};
static const struct intel_device_info intel_geminilake_info = {
.graphics_ver = 9,
.display_ver = 9,
.is_geminilake = true,
.codename = "geminilake"
};
#define COFFEELAKE_FIELDS \
.graphics_ver = 9, \
.display_ver = 9, \
.is_coffeelake = true, \
.codename = "coffeelake"
static const struct intel_device_info intel_coffeelake_gt1_info = {
COFFEELAKE_FIELDS,
.gt = 1,
};
static const struct intel_device_info intel_coffeelake_gt2_info = {
COFFEELAKE_FIELDS,
.gt = 2,
};
static const struct intel_device_info intel_coffeelake_gt3_info = {
COFFEELAKE_FIELDS,
.gt = 3,
};
#define COMETLAKE_FIELDS \
.graphics_ver = 9, \
.display_ver = 9, \
.is_cometlake = true, \
.codename = "cometlake"
static const struct intel_device_info intel_cometlake_gt1_info = {
COMETLAKE_FIELDS,
.gt = 1,
};
static const struct intel_device_info intel_cometlake_gt2_info = {
COMETLAKE_FIELDS,
.gt = 2,
};
static const struct intel_device_info intel_cannonlake_info = {
.graphics_ver = 10,
.display_ver = 10,
.is_cannonlake = true,
.codename = "cannonlake"
};
static const struct intel_device_info intel_icelake_info = {
.graphics_ver = 11,
.display_ver = 11,
.is_icelake = true,
.codename = "icelake"
};
static const struct intel_device_info intel_elkhartlake_info = {
.graphics_ver = 11,
.display_ver = 11,
.is_elkhartlake = true,
.codename = "elkhartlake"
};
static const struct intel_device_info intel_jasperlake_info = {
.graphics_ver = 11,
.display_ver = 11,
.is_jasperlake = true,
.codename = "jasperlake"
};
static const struct intel_device_info intel_tigerlake_gt1_info = {
.graphics_ver = 12,
.display_ver = 12,
.is_tigerlake = true,
.codename = "tigerlake",
.gt = 1,
};
static const struct intel_device_info intel_tigerlake_gt2_info = {
.graphics_ver = 12,
.display_ver = 12,
.is_tigerlake = true,
.codename = "tigerlake",
.gt = 2,
};
static const struct intel_device_info intel_rocketlake_info = {
.graphics_ver = 12,
.display_ver = 12,
.is_rocketlake = true,
.codename = "rocketlake"
};
static const struct intel_device_info intel_dg1_info = {
.graphics_ver = 12,
.graphics_rel = 10,
.display_ver = 12,
.is_dg1 = true,
.codename = "dg1"
};
static const struct intel_device_info intel_dg2_info = {
.graphics_ver = 12,
.graphics_rel = 55,
.display_ver = 13,
.has_4tile = true,
.is_dg2 = true,
.codename = "dg2",
.has_flatccs = true,
};
static const struct intel_device_info intel_alderlake_s_info = {
.graphics_ver = 12,
.display_ver = 12,
.is_alderlake_s = true,
.codename = "alderlake_s"
};
static const struct intel_device_info intel_raptorlake_s_info = {
.graphics_ver = 12,
.display_ver = 12,
.is_raptorlake_s = true,
.codename = "raptorlake_s"
};
static const struct intel_device_info intel_alderlake_p_info = {
.graphics_ver = 12,
.display_ver = 13,
.is_alderlake_p = true,
.codename = "alderlake_p"
};
static const struct intel_device_info intel_alderlake_n_info = {
.graphics_ver = 12,
.display_ver = 13,
.is_alderlake_n = true,
.codename = "alderlake_n"
};
static const struct intel_device_info intel_ats_m_info = {
.graphics_ver = 12,
.graphics_rel = 55,
.display_ver = 0, /* no display support */
.is_dg2 = true,
.has_4tile = true,
.codename = "ats_m",
.has_flatccs = true,
};
static const struct intel_device_info intel_meteorlake_info = {
.graphics_ver = 12,
.graphics_rel = 70,
.display_ver = 14,
.has_4tile = true,
.has_oam = true,
.is_meteorlake = true,
.codename = "meteorlake",
};
static const struct intel_device_info intel_pontevecchio_info = {
.graphics_ver = 12,
.graphics_rel = 60,
.is_pontevecchio = true,
.codename = "pontevecchio",
};
static const struct intel_device_info intel_lunarlake_info = {
.graphics_ver = 20,
.graphics_rel = 4,
.display_ver = 20,
.has_4tile = true,
.has_flatccs = true,
.has_oam = true,
.is_lunarlake = true,
.codename = "lunarlake",
};
static const struct intel_device_info intel_battlemage_info = {
.graphics_ver = 20,
.graphics_rel = 1,
.display_ver = 14,
.has_4tile = true,
.has_flatccs = true,
.is_battlemage = true,
.codename = "battlemage",
};
static const struct pci_id_match intel_device_match[] = {
INTEL_I810_IDS(INTEL_VGA_DEVICE, &intel_i810_info),
INTEL_I815_IDS(INTEL_VGA_DEVICE, &intel_i815_info),
INTEL_I830_IDS(INTEL_VGA_DEVICE, &intel_i830_info),
INTEL_I845G_IDS(INTEL_VGA_DEVICE, &intel_i845_info),
INTEL_I85X_IDS(INTEL_VGA_DEVICE, &intel_i855_info),
INTEL_I865G_IDS(INTEL_VGA_DEVICE, &intel_i865_info),
INTEL_I915G_IDS(INTEL_VGA_DEVICE, &intel_i915_info),
INTEL_I915GM_IDS(INTEL_VGA_DEVICE, &intel_i915m_info),
INTEL_I945G_IDS(INTEL_VGA_DEVICE, &intel_i945_info),
INTEL_I945GM_IDS(INTEL_VGA_DEVICE, &intel_i945m_info),
INTEL_G33_IDS(INTEL_VGA_DEVICE, &intel_g33_info),
INTEL_PNV_G_IDS(INTEL_VGA_DEVICE, &intel_pineview_g_info),
INTEL_PNV_M_IDS(INTEL_VGA_DEVICE, &intel_pineview_m_info),
INTEL_I965G_IDS(INTEL_VGA_DEVICE, &intel_i965_info),
INTEL_I965GM_IDS(INTEL_VGA_DEVICE, &intel_i965m_info),
INTEL_G45_IDS(INTEL_VGA_DEVICE, &intel_g45_info),
INTEL_GM45_IDS(INTEL_VGA_DEVICE, &intel_gm45_info),
INTEL_ILK_D_IDS(INTEL_VGA_DEVICE, &intel_ironlake_info),
INTEL_ILK_M_IDS(INTEL_VGA_DEVICE, &intel_ironlake_m_info),
INTEL_SNB_D_IDS(INTEL_VGA_DEVICE, &intel_sandybridge_info),
INTEL_SNB_M_IDS(INTEL_VGA_DEVICE, &intel_sandybridge_m_info),
INTEL_IVB_D_IDS(INTEL_VGA_DEVICE, &intel_ivybridge_info),
INTEL_IVB_M_IDS(INTEL_VGA_DEVICE, &intel_ivybridge_m_info),
INTEL_HSW_GT1_IDS(INTEL_VGA_DEVICE, &intel_haswell_gt1_info),
INTEL_HSW_GT2_IDS(INTEL_VGA_DEVICE, &intel_haswell_gt2_info),
INTEL_HSW_GT3_IDS(INTEL_VGA_DEVICE, &intel_haswell_gt3_info),
INTEL_VLV_IDS(INTEL_VGA_DEVICE, &intel_valleyview_info),
INTEL_BDW_GT1_IDS(INTEL_VGA_DEVICE, &intel_broadwell_gt1_info),
INTEL_BDW_GT2_IDS(INTEL_VGA_DEVICE, &intel_broadwell_gt2_info),
INTEL_BDW_GT3_IDS(INTEL_VGA_DEVICE, &intel_broadwell_gt3_info),
INTEL_BDW_RSVD_IDS(INTEL_VGA_DEVICE, &intel_broadwell_unknown_info),
INTEL_CHV_IDS(INTEL_VGA_DEVICE, &intel_cherryview_info),
INTEL_SKL_GT1_IDS(INTEL_VGA_DEVICE, &intel_skylake_gt1_info),
INTEL_SKL_GT2_IDS(INTEL_VGA_DEVICE, &intel_skylake_gt2_info),
INTEL_SKL_GT3_IDS(INTEL_VGA_DEVICE, &intel_skylake_gt3_info),
INTEL_SKL_GT4_IDS(INTEL_VGA_DEVICE, &intel_skylake_gt4_info),
INTEL_BXT_IDS(INTEL_VGA_DEVICE, &intel_broxton_info),
INTEL_KBL_GT1_IDS(INTEL_VGA_DEVICE, &intel_kabylake_gt1_info),
INTEL_KBL_GT2_IDS(INTEL_VGA_DEVICE, &intel_kabylake_gt2_info),
INTEL_KBL_GT3_IDS(INTEL_VGA_DEVICE, &intel_kabylake_gt3_info),
INTEL_KBL_GT4_IDS(INTEL_VGA_DEVICE, &intel_kabylake_gt4_info),
INTEL_AML_KBL_GT2_IDS(INTEL_VGA_DEVICE, &intel_kabylake_gt2_info),
INTEL_GLK_IDS(INTEL_VGA_DEVICE, &intel_geminilake_info),
INTEL_CFL_S_GT1_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt1_info),
INTEL_CFL_S_GT2_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt2_info),
INTEL_CFL_H_GT1_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt1_info),
INTEL_CFL_H_GT2_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt2_info),
INTEL_CFL_U_GT2_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt2_info),
INTEL_CFL_U_GT3_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt3_info),
INTEL_WHL_U_GT1_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt1_info),
INTEL_WHL_U_GT2_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt2_info),
INTEL_WHL_U_GT3_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt3_info),
INTEL_AML_CFL_GT2_IDS(INTEL_VGA_DEVICE, &intel_coffeelake_gt2_info),
INTEL_CML_GT1_IDS(INTEL_VGA_DEVICE, &intel_cometlake_gt1_info),
INTEL_CML_GT2_IDS(INTEL_VGA_DEVICE, &intel_cometlake_gt2_info),
INTEL_CML_U_GT1_IDS(INTEL_VGA_DEVICE, &intel_cometlake_gt1_info),
INTEL_CML_U_GT2_IDS(INTEL_VGA_DEVICE, &intel_cometlake_gt2_info),
INTEL_CNL_IDS(INTEL_VGA_DEVICE, &intel_cannonlake_info),
INTEL_ICL_IDS(INTEL_VGA_DEVICE, &intel_icelake_info),
INTEL_EHL_IDS(INTEL_VGA_DEVICE, &intel_elkhartlake_info),
INTEL_JSL_IDS(INTEL_VGA_DEVICE, &intel_jasperlake_info),
INTEL_TGL_GT1_IDS(INTEL_VGA_DEVICE, &intel_tigerlake_gt1_info),
INTEL_TGL_GT2_IDS(INTEL_VGA_DEVICE, &intel_tigerlake_gt2_info),
INTEL_RKL_IDS(INTEL_VGA_DEVICE, &intel_rocketlake_info),
INTEL_DG1_IDS(INTEL_VGA_DEVICE, &intel_dg1_info),
INTEL_DG2_IDS(INTEL_VGA_DEVICE, &intel_dg2_info),
INTEL_ADLS_IDS(INTEL_VGA_DEVICE, &intel_alderlake_s_info),
INTEL_RPLS_IDS(INTEL_VGA_DEVICE, &intel_raptorlake_s_info),
INTEL_ADLP_IDS(INTEL_VGA_DEVICE, &intel_alderlake_p_info),
INTEL_RPLU_IDS(INTEL_VGA_DEVICE, &intel_alderlake_p_info),
INTEL_RPLP_IDS(INTEL_VGA_DEVICE, &intel_alderlake_p_info),
INTEL_ADLN_IDS(INTEL_VGA_DEVICE, &intel_alderlake_n_info),
INTEL_ATS_M_IDS(INTEL_VGA_DEVICE, &intel_ats_m_info),
INTEL_MTL_IDS(INTEL_VGA_DEVICE, &intel_meteorlake_info),
INTEL_PVC_IDS(INTEL_VGA_DEVICE, &intel_pontevecchio_info),
XE_LNL_IDS(INTEL_VGA_DEVICE, &intel_lunarlake_info),
XE_BMG_IDS(INTEL_VGA_DEVICE, &intel_battlemage_info),
INTEL_VGA_DEVICE(PCI_MATCH_ANY, &intel_generic_info),
};
/**
* intel_get_device_info:
* @devid: pci device id
*
* Looks up the Intel GFX device info for the given device id.
*
* Returns:
* The associated intel_get_device_info
*/
const struct intel_device_info *intel_get_device_info(uint16_t devid)
{
static const struct intel_device_info *cache = &intel_generic_info;
static uint16_t cached_devid;
int i;
if (cached_devid == devid)
goto out;
/* XXX Presort table and bsearch! */
for (i = 0; intel_device_match[i].device_id != PCI_MATCH_ANY; i++) {
if (devid == intel_device_match[i].device_id)
break;
}
cached_devid = devid;
cache = (void *)intel_device_match[i].match_data;
out:
return cache;
}
@@ -0,0 +1,697 @@
/*
* Copyright © 2007-2023 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <poll.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <termios.h>
#include <time.h>
#include <sys/sysmacros.h>
#include <stdarg.h>
#include "intel_gpu_top.h"
#include "i915_drm.h"
#include "igt_perf.h"
#ifdef __clang__ //? Workaround for missing asprintf when compiling with clang, taken from https://stackoverflow.com/a/4899487
int asprintf(char **ret, const char *format, ...)
{
va_list ap;
*ret = NULL; /* Ensure value can be passed to free() */
va_start(ap, format);
int count = vsnprintf(NULL, 0, format, ap);
va_end(ap);
if (count >= 0)
{
char* buffer = malloc(count + 1);
if (buffer == NULL)
return -1;
va_start(ap, format);
count = vsnprintf(buffer, count + 1, format, ap);
va_end(ap);
if (count < 0)
{
free(buffer);
return count;
}
*ret = buffer;
}
return count;
}
#endif
__attribute__((format(scanf,3,4)))
static int igt_sysfs_scanf(int dir, const char *attr, const char *fmt, ...)
{
FILE *file;
int fd;
int ret = -1;
fd = openat(dir, attr, O_RDONLY);
if (fd < 0)
return -1;
file = fdopen(fd, "r");
if (file) {
va_list ap;
va_start(ap, fmt);
ret = vfscanf(file, fmt, ap);
va_end(ap);
fclose(file);
} else {
close(fd);
}
return ret;
}
static int pmu_parse(struct pmu_counter *pmu, const char *path, const char *str)
{
locale_t locale, oldlocale;
bool result = true;
char buf[128] = {};
int dir;
dir = open(path, O_RDONLY);
if (dir < 0)
return -errno;
/* Replace user environment with plain C to match kernel format */
locale = newlocale(LC_ALL, "C", 0);
oldlocale = uselocale(locale);
result &= igt_sysfs_scanf(dir, "type", "%"PRIu64, &pmu->type) == 1;
snprintf(buf, sizeof(buf) - 1, "events/%s", str);
result &= igt_sysfs_scanf(dir, buf, "event=%"PRIx64, &pmu->config) == 1;
snprintf(buf, sizeof(buf) - 1, "events/%s.scale", str);
result &= igt_sysfs_scanf(dir, buf, "%lf", &pmu->scale) == 1;
snprintf(buf, sizeof(buf) - 1, "events/%s.unit", str);
result &= igt_sysfs_scanf(dir, buf, "%127s", buf) == 1;
pmu->units = strdup(buf);
uselocale(oldlocale);
freelocale(locale);
close(dir);
if (!result)
return -EINVAL;
if (isnan(pmu->scale) || !pmu->scale)
return -ERANGE;
return 0;
}
static int rapl_parse(struct pmu_counter *pmu, const char *str)
{
const char *expected_units = "Joules";
int err;
err = pmu_parse(pmu, "/sys/devices/power", str);
if (err < 0)
return err;
if (!pmu->units || strcmp(pmu->units, expected_units)) {
fprintf(stderr,
"Unexpected units for RAPL %s: found '%s', expected '%s'\n",
str, pmu->units, expected_units);
}
return 0;
}
static void
rapl_open(struct pmu_counter *pmu,
const char *domain,
struct engines *engines)
{
int fd;
if (rapl_parse(pmu, domain) < 0)
return;
fd = igt_perf_open_group(pmu->type, pmu->config, engines->rapl_fd);
if (fd < 0)
return;
if (engines->rapl_fd == -1)
engines->rapl_fd = fd;
pmu->idx = engines->num_rapl++;
pmu->present = true;
}
static void gpu_power_open(struct pmu_counter *pmu,
struct engines *engines)
{
rapl_open(pmu, "energy-gpu", engines);
}
static void pkg_power_open(struct pmu_counter *pmu,
struct engines *engines)
{
rapl_open(pmu, "energy-pkg", engines);
}
static uint64_t
get_pmu_config(int dirfd, const char *name, const char *counter)
{
char buf[128], *p;
int fd, ret;
ret = snprintf(buf, sizeof(buf), "%s-%s", name, counter);
if (ret < 0 || ret == sizeof(buf))
return -1;
fd = openat(dirfd, buf, O_RDONLY);
if (fd < 0)
return -1;
ret = read(fd, buf, sizeof(buf));
close(fd);
if (ret <= 0)
return -1;
p = index(buf, '0');
if (!p)
return -1;
return strtoul(p, NULL, 0);
}
#define engine_ptr(engines, n) (&engines->engine + (n))
static const char *class_display_name(unsigned int class)
{
switch (class) {
case I915_ENGINE_CLASS_RENDER:
return "Render/3D";
case I915_ENGINE_CLASS_COPY:
return "Blitter";
case I915_ENGINE_CLASS_VIDEO:
return "Video";
case I915_ENGINE_CLASS_VIDEO_ENHANCE:
return "VideoEnhance";
case I915_ENGINE_CLASS_COMPUTE:
return "Compute";
default:
return "[unknown]";
}
}
static const char *class_short_name(unsigned int class)
{
switch (class) {
case I915_ENGINE_CLASS_RENDER:
return "RCS";
case I915_ENGINE_CLASS_COPY:
return "BCS";
case I915_ENGINE_CLASS_VIDEO:
return "VCS";
case I915_ENGINE_CLASS_VIDEO_ENHANCE:
return "VECS";
case I915_ENGINE_CLASS_COMPUTE:
return "CCS";
default:
return "UNKN";
}
}
static int engine_cmp(const void *__a, const void *__b)
{
const struct engine *a = (struct engine *)__a;
const struct engine *b = (struct engine *)__b;
if (a->class != b->class)
return a->class - b->class;
else
return a->instance - b->instance;
}
#define is_igpu(x) (strcmp(x, "i915") == 0)
struct engines *discover_engines(const char *device)
{
char sysfs_root[PATH_MAX];
struct engines *engines;
struct dirent *dent;
int ret = 0;
DIR *d;
snprintf(sysfs_root, sizeof(sysfs_root),
"/sys/devices/%s/events", device);
engines = malloc(sizeof(struct engines));
if (!engines)
return NULL;
memset(engines, 0, sizeof(*engines));
engines->num_engines = 0;
engines->device = device;
engines->discrete = !is_igpu(device);
d = opendir(sysfs_root);
if (!d)
goto err;
while ((dent = readdir(d)) != NULL) {
const char *endswith = "-busy";
const unsigned int endlen = strlen(endswith);
struct engine *engine =
engine_ptr(engines, engines->num_engines);
char buf[256];
if (dent->d_type != DT_REG)
continue;
if (strlen(dent->d_name) >= sizeof(buf)) {
ret = ENAMETOOLONG;
break;
}
strcpy(buf, dent->d_name);
/* xxxN-busy */
if (strlen(buf) < (endlen + 4))
continue;
if (strcmp(&buf[strlen(buf) - endlen], endswith))
continue;
memset(engine, 0, sizeof(*engine));
buf[strlen(buf) - endlen] = 0;
engine->name = strdup(buf);
if (!engine->name) {
ret = errno;
break;
}
engine->busy.config = get_pmu_config(dirfd(d), engine->name,
"busy");
if (engine->busy.config == -1) {
ret = ENOENT;
break;
}
/* Double check config is an engine config. */
if (engine->busy.config >= __I915_PMU_OTHER(0)) {
free((void *)engine->name);
continue;
}
engine->class = (engine->busy.config &
(__I915_PMU_OTHER(0) - 1)) >>
I915_PMU_CLASS_SHIFT;
engine->instance = (engine->busy.config >>
I915_PMU_SAMPLE_BITS) &
((1 << I915_PMU_SAMPLE_INSTANCE_BITS) - 1);
ret = asprintf(&engine->display_name, "%s/%u",
class_display_name(engine->class),
engine->instance);
if (ret <= 0) {
ret = errno;
break;
}
ret = asprintf(&engine->short_name, "%s/%u",
class_short_name(engine->class),
engine->instance);
if (ret <= 0) {
ret = errno;
break;
}
engines->num_engines++;
engines = realloc(engines, sizeof(struct engines) +
engines->num_engines * sizeof(struct engine));
if (!engines) {
ret = errno;
break;
}
ret = 0;
}
if (ret) {
errno = ret;
goto err;
}
qsort(engine_ptr(engines, 0), engines->num_engines,
sizeof(struct engine), engine_cmp);
engines->root = d;
return engines;
err:
free(engines);
return NULL;
}
void free_engines(struct engines *engines)
{
struct pmu_counter **pmu, *free_list[] = {
&engines->r_gpu,
&engines->r_pkg,
&engines->imc_reads,
&engines->imc_writes,
NULL
};
unsigned int i;
if (!engines)
return;
for (pmu = &free_list[0]; *pmu; pmu++) {
if ((*pmu)->present)
free((char *)(*pmu)->units);
}
for (i = 0; i < engines->num_engines; i++) {
struct engine *engine = engine_ptr(engines, i);
free((char *)engine->name);
free((char *)engine->short_name);
free((char *)engine->display_name);
}
closedir(engines->root);
free(engines->class);
free(engines);
}
#define _open_pmu(type, cnt, pmu, fd) \
({ \
int fd__; \
\
fd__ = igt_perf_open_group((type), (pmu)->config, (fd)); \
if (fd__ >= 0) { \
if ((fd) == -1) \
(fd) = fd__; \
(pmu)->present = true; \
(pmu)->idx = (cnt)++; \
} \
\
fd__; \
})
static int imc_parse(struct pmu_counter *pmu, const char *str)
{
return pmu_parse(pmu, "/sys/devices/uncore_imc", str);
}
static void
imc_open(struct pmu_counter *pmu,
const char *domain,
struct engines *engines)
{
int fd;
if (imc_parse(pmu, domain) < 0)
return;
fd = igt_perf_open_group(pmu->type, pmu->config, engines->imc_fd);
if (fd < 0)
return;
if (engines->imc_fd == -1)
engines->imc_fd = fd;
pmu->idx = engines->num_imc++;
pmu->present = true;
}
static void imc_writes_open(struct pmu_counter *pmu, struct engines *engines)
{
imc_open(pmu, "data_writes", engines);
}
static void imc_reads_open(struct pmu_counter *pmu, struct engines *engines)
{
imc_open(pmu, "data_reads", engines);
}
static int get_num_gts(uint64_t type)
{
int fd, cnt;
errno = 0;
for (cnt = 0; cnt < MAX_GTS; cnt++) {
fd = igt_perf_open(type, __I915_PMU_INTERRUPTS(cnt));
if (fd < 0)
break;
close(fd);
}
if (!cnt || (errno && errno != ENOENT))
cnt = -errno;
return cnt;
}
static void init_aggregate_counters(struct engines *engines)
{
struct pmu_counter *pmu;
pmu = &engines->freq_req;
pmu->type = igt_perf_type_id(engines->device);
pmu->config = I915_PMU_REQUESTED_FREQUENCY;
pmu->present = true;
pmu = &engines->freq_act;
pmu->type = igt_perf_type_id(engines->device);
pmu->config = I915_PMU_ACTUAL_FREQUENCY;
pmu->present = true;
pmu = &engines->rc6;
pmu->type = igt_perf_type_id(engines->device);
pmu->config = I915_PMU_RC6_RESIDENCY;
pmu->present = true;
}
int pmu_init(struct engines *engines)
{
unsigned int i;
int fd;
uint64_t type = igt_perf_type_id(engines->device);
engines->fd = -1;
engines->num_counters = 0;
engines->num_gts = get_num_gts(type);
if (engines->num_gts <= 0)
return -1;
engines->irq.config = I915_PMU_INTERRUPTS;
fd = _open_pmu(type, engines->num_counters, &engines->irq, engines->fd);
if (fd < 0)
return -1;
init_aggregate_counters(engines);
for (i = 0; i < engines->num_gts; i++) {
engines->freq_req_gt[i].config = __I915_PMU_REQUESTED_FREQUENCY(i);
_open_pmu(type, engines->num_counters, &engines->freq_req_gt[i], engines->fd);
engines->freq_act_gt[i].config = __I915_PMU_ACTUAL_FREQUENCY(i);
_open_pmu(type, engines->num_counters, &engines->freq_act_gt[i], engines->fd);
engines->rc6_gt[i].config = __I915_PMU_RC6_RESIDENCY(i);
_open_pmu(type, engines->num_counters, &engines->rc6_gt[i], engines->fd);
}
for (i = 0; i < engines->num_engines; i++) {
struct engine *engine = engine_ptr(engines, i);
struct {
struct pmu_counter *pmu;
const char *counter;
} *cnt, counters[] = {
{ .pmu = &engine->busy, .counter = "busy" },
{ .pmu = &engine->wait, .counter = "wait" },
{ .pmu = &engine->sema, .counter = "sema" },
{ .pmu = NULL, .counter = NULL },
};
for (cnt = counters; cnt->pmu; cnt++) {
if (!cnt->pmu->config)
cnt->pmu->config =
get_pmu_config(dirfd(engines->root),
engine->name,
cnt->counter);
fd = _open_pmu(type, engines->num_counters, cnt->pmu,
engines->fd);
if (fd >= 0)
engine->num_counters++;
}
}
engines->rapl_fd = -1;
if (!engines->discrete) {
gpu_power_open(&engines->r_gpu, engines);
pkg_power_open(&engines->r_pkg, engines);
}
engines->imc_fd = -1;
imc_reads_open(&engines->imc_reads, engines);
imc_writes_open(&engines->imc_writes, engines);
return 0;
}
static uint64_t pmu_read_multi(int fd, unsigned int num, uint64_t *val)
{
uint64_t buf[2 + num];
unsigned int i;
ssize_t len;
memset(buf, 0, sizeof(buf));
len = read(fd, buf, sizeof(buf));
assert(len == sizeof(buf));
for (i = 0; i < num; i++)
val[i] = buf[2 + i];
return buf[1];
}
double pmu_calc(struct pmu_pair *p, double d, double t, double s)
{
double v;
v = p->cur - p->prev;
v /= d;
v /= t;
v *= s;
if (s == 100.0 && v > 100.0)
v = 100.0;
return v;
}
static void __update_sample(struct pmu_counter *counter, uint64_t val)
{
counter->val.prev = counter->val.cur;
counter->val.cur = val;
}
static void update_sample(struct pmu_counter *counter, uint64_t *val)
{
if (counter->present)
__update_sample(counter, val[counter->idx]);
}
void pmu_sample(struct engines *engines)
{
const int num_val = engines->num_counters;
uint64_t val[2 + num_val];
unsigned int i;
engines->ts.prev = engines->ts.cur;
engines->ts.cur = pmu_read_multi(engines->fd, num_val, val);
engines->freq_req.val.cur = engines->freq_req.val.prev = 0;
engines->freq_act.val.cur = engines->freq_act.val.prev = 0;
engines->rc6.val.cur = engines->rc6.val.prev = 0;
for (i = 0; i < engines->num_gts; i++) {
update_sample(&engines->freq_req_gt[i], val);
engines->freq_req.val.cur += engines->freq_req_gt[i].val.cur;
engines->freq_req.val.prev += engines->freq_req_gt[i].val.prev;
update_sample(&engines->freq_act_gt[i], val);
engines->freq_act.val.cur += engines->freq_act_gt[i].val.cur;
engines->freq_act.val.prev += engines->freq_act_gt[i].val.prev;
update_sample(&engines->rc6_gt[i], val);
engines->rc6.val.cur += engines->rc6_gt[i].val.cur;
engines->rc6.val.prev += engines->rc6_gt[i].val.prev;
}
engines->freq_req.val.cur /= engines->num_gts;
engines->freq_req.val.prev /= engines->num_gts;
engines->freq_act.val.cur /= engines->num_gts;
engines->freq_act.val.prev /= engines->num_gts;
engines->rc6.val.cur /= engines->num_gts;
engines->rc6.val.prev /= engines->num_gts;
update_sample(&engines->irq, val);
for (i = 0; i < engines->num_engines; i++) {
struct engine *engine = engine_ptr(engines, i);
update_sample(&engine->busy, val);
update_sample(&engine->sema, val);
update_sample(&engine->wait, val);
}
if (engines->num_rapl) {
pmu_read_multi(engines->rapl_fd, engines->num_rapl, val);
update_sample(&engines->r_gpu, val);
update_sample(&engines->r_pkg, val);
}
if (engines->num_imc) {
pmu_read_multi(engines->imc_fd, engines->num_imc, val);
update_sample(&engines->imc_reads, val);
update_sample(&engines->imc_writes, val);
}
}
@@ -0,0 +1,95 @@
#ifndef INTEL_GPU_TOP_H
#define INTEL_GPU_TOP_H
#include <stdbool.h>
#include <stdint.h>
#include <dirent.h>
struct pmu_pair {
uint64_t cur;
uint64_t prev;
};
struct pmu_counter {
uint64_t type;
uint64_t config;
unsigned int idx;
struct pmu_pair val;
double scale;
const char *units;
bool present;
};
struct engine_class {
unsigned int engine_class;
const char *name;
unsigned int num_engines;
};
struct engine {
const char *name;
char *display_name;
char *short_name;
unsigned int class;
unsigned int instance;
unsigned int num_counters;
struct pmu_counter busy;
struct pmu_counter wait;
struct pmu_counter sema;
};
#define MAX_GTS 4
struct engines {
unsigned int num_engines;
unsigned int num_classes;
struct engine_class *class;
unsigned int num_counters;
DIR *root;
int fd;
struct pmu_pair ts;
int rapl_fd;
struct pmu_counter r_gpu, r_pkg;
unsigned int num_rapl;
int imc_fd;
struct pmu_counter imc_reads;
struct pmu_counter imc_writes;
unsigned int num_imc;
struct pmu_counter freq_req;
struct pmu_counter freq_req_gt[MAX_GTS];
struct pmu_counter freq_act;
struct pmu_counter freq_act_gt[MAX_GTS];
struct pmu_counter irq;
struct pmu_counter rc6;
struct pmu_counter rc6_gt[MAX_GTS];
bool discrete;
char *device;
int num_gts;
/* Do not edit below this line.
* This structure is reallocated every time a new engine is
* found and size is increased by sizeof (engine).
*/
struct engine engine;
};
struct engines *discover_engines(const char *device);
void free_engines(struct engines *engines);
int pmu_init(struct engines *engines);
void pmu_sample(struct engines *engines);
double pmu_calc(struct pmu_pair *p, double d, double t, double s);
char* find_intel_gpu_dir();
char* get_intel_device_id(const char* vendor_path);
char *get_intel_device_name(const char *device_id);
#endif
@@ -0,0 +1,97 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include "intel_gpu_top.h"
#include "intel_chipset.h"
#define VENDOR_ID "0x8086"
#define SYSFS_PATH "/sys/class/drm"
#define VENDOR_FILE "vendor"
#define DEVICE_FILE "device"
char* find_intel_gpu_dir() {
DIR *dir;
struct dirent *entry;
static char path[256];
char vendor_path[256];
char vendor_id[16];
if ((dir = opendir(SYSFS_PATH)) == NULL) {
perror("opendir");
return NULL;
}
while ((entry = readdir(dir)) != NULL) {
// Construct the path to the vendor file
snprintf(vendor_path, sizeof(vendor_path), "%s/%s/device/%s", SYSFS_PATH, entry->d_name, VENDOR_FILE);
// Check if the vendor file exists
if (access(vendor_path, F_OK) != -1) {
FILE *file = fopen(vendor_path, "r");
if (file) {
if (fgets(vendor_id, sizeof(vendor_id), file)) {
// Trim the newline character
vendor_id[strcspn(vendor_id, "\n")] = 0;
if (strcmp(vendor_id, VENDOR_ID) == 0) {
// Return the parent directory (i.e., /sys/class/drm/card*)
snprintf(path, sizeof(path), "%s/%s", SYSFS_PATH, entry->d_name);
fclose(file);
closedir(dir);
return path;
}
}
fclose(file);
}
}
}
closedir(dir);
return NULL; // Intel GPU not found
}
char* get_intel_device_id(const char* gpu_dir) {
static char device_path[256];
char device_id[16];
// Construct the path to the device file
snprintf(device_path, sizeof(device_path), "%s/device/%s", gpu_dir, DEVICE_FILE);
FILE *file = fopen(device_path, "r");
if (file) {
if (fgets(device_id, sizeof(device_id), file)) {
fclose(file);
// Trim the newline character
device_id[strcspn(device_id, "\n")] = 0;
// Return a copy of the device ID
return strdup(device_id);
}
fclose(file);
} else {
perror("fopen");
}
return NULL;
}
char *get_intel_device_name(const char *device_id) {
uint16_t devid = strtol(device_id, NULL, 16);
char dev_name[256];
char full_name[256];
const struct intel_device_info *info = intel_get_device_info(devid);
if (info) {
if (info->codename == NULL) {
strcpy(dev_name, "(unknown)");
} else {
strcpy(dev_name, info->codename);
dev_name[0] = toupper(dev_name[0]);
}
snprintf(full_name, sizeof(full_name), "Intel %s (Gen%u)", dev_name, info->graphics_ver);
return strdup(full_name);
}
return NULL;
}
@@ -0,0 +1,2 @@
0f02dc176959e6296866b1bafd3982e277a5e44b
https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
@@ -0,0 +1,218 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2022 Intel Corporation
*/
#ifndef _XE_PCIIDS_H_
#define _XE_PCIIDS_H_
/*
* Lists below can be turned into initializers for a struct pci_device_id
* by defining INTEL_VGA_DEVICE:
*
* #define INTEL_VGA_DEVICE(id, info) { \
* 0x8086, id, \
* ~0, ~0, \
* 0x030000, 0xff0000, \
* (unsigned long) info }
*
* And then calling like:
*
* XE_TGL_12_GT1_IDS(INTEL_VGA_DEVICE, ## __VA_ARGS__)
*
* To turn them into something else, just provide a different macro passed as
* first argument.
*/
/* TGL */
#define XE_TGL_GT1_IDS(MACRO__, ...) \
MACRO__(0x9A60, ## __VA_ARGS__), \
MACRO__(0x9A68, ## __VA_ARGS__), \
MACRO__(0x9A70, ## __VA_ARGS__)
#define XE_TGL_GT2_IDS(MACRO__, ...) \
MACRO__(0x9A40, ## __VA_ARGS__), \
MACRO__(0x9A49, ## __VA_ARGS__), \
MACRO__(0x9A59, ## __VA_ARGS__), \
MACRO__(0x9A78, ## __VA_ARGS__), \
MACRO__(0x9AC0, ## __VA_ARGS__), \
MACRO__(0x9AC9, ## __VA_ARGS__), \
MACRO__(0x9AD9, ## __VA_ARGS__), \
MACRO__(0x9AF8, ## __VA_ARGS__)
#define XE_TGL_IDS(MACRO__, ...) \
XE_TGL_GT1_IDS(MACRO__, ## __VA_ARGS__),\
XE_TGL_GT2_IDS(MACRO__, ## __VA_ARGS__)
/* RKL */
#define XE_RKL_IDS(MACRO__, ...) \
MACRO__(0x4C80, ## __VA_ARGS__), \
MACRO__(0x4C8A, ## __VA_ARGS__), \
MACRO__(0x4C8B, ## __VA_ARGS__), \
MACRO__(0x4C8C, ## __VA_ARGS__), \
MACRO__(0x4C90, ## __VA_ARGS__), \
MACRO__(0x4C9A, ## __VA_ARGS__)
/* DG1 */
#define XE_DG1_IDS(MACRO__, ...) \
MACRO__(0x4905, ## __VA_ARGS__), \
MACRO__(0x4906, ## __VA_ARGS__), \
MACRO__(0x4907, ## __VA_ARGS__), \
MACRO__(0x4908, ## __VA_ARGS__), \
MACRO__(0x4909, ## __VA_ARGS__)
/* ADL-S */
#define XE_ADLS_IDS(MACRO__, ...) \
MACRO__(0x4680, ## __VA_ARGS__), \
MACRO__(0x4682, ## __VA_ARGS__), \
MACRO__(0x4688, ## __VA_ARGS__), \
MACRO__(0x468A, ## __VA_ARGS__), \
MACRO__(0x468B, ## __VA_ARGS__), \
MACRO__(0x4690, ## __VA_ARGS__), \
MACRO__(0x4692, ## __VA_ARGS__), \
MACRO__(0x4693, ## __VA_ARGS__)
/* ADL-P */
#define XE_ADLP_IDS(MACRO__, ...) \
MACRO__(0x46A0, ## __VA_ARGS__), \
MACRO__(0x46A1, ## __VA_ARGS__), \
MACRO__(0x46A2, ## __VA_ARGS__), \
MACRO__(0x46A3, ## __VA_ARGS__), \
MACRO__(0x46A6, ## __VA_ARGS__), \
MACRO__(0x46A8, ## __VA_ARGS__), \
MACRO__(0x46AA, ## __VA_ARGS__), \
MACRO__(0x462A, ## __VA_ARGS__), \
MACRO__(0x4626, ## __VA_ARGS__), \
MACRO__(0x4628, ## __VA_ARGS__), \
MACRO__(0x46B0, ## __VA_ARGS__), \
MACRO__(0x46B1, ## __VA_ARGS__), \
MACRO__(0x46B2, ## __VA_ARGS__), \
MACRO__(0x46B3, ## __VA_ARGS__), \
MACRO__(0x46C0, ## __VA_ARGS__), \
MACRO__(0x46C1, ## __VA_ARGS__), \
MACRO__(0x46C2, ## __VA_ARGS__), \
MACRO__(0x46C3, ## __VA_ARGS__)
/* ADL-N */
#define XE_ADLN_IDS(MACRO__, ...) \
MACRO__(0x46D0, ## __VA_ARGS__), \
MACRO__(0x46D1, ## __VA_ARGS__), \
MACRO__(0x46D2, ## __VA_ARGS__)
/* RPL-S */
#define XE_RPLS_IDS(MACRO__, ...) \
MACRO__(0xA780, ## __VA_ARGS__), \
MACRO__(0xA781, ## __VA_ARGS__), \
MACRO__(0xA782, ## __VA_ARGS__), \
MACRO__(0xA783, ## __VA_ARGS__), \
MACRO__(0xA788, ## __VA_ARGS__), \
MACRO__(0xA789, ## __VA_ARGS__), \
MACRO__(0xA78A, ## __VA_ARGS__), \
MACRO__(0xA78B, ## __VA_ARGS__)
/* RPL-U */
#define XE_RPLU_IDS(MACRO__, ...) \
MACRO__(0xA721, ## __VA_ARGS__), \
MACRO__(0xA7A1, ## __VA_ARGS__), \
MACRO__(0xA7A9, ## __VA_ARGS__), \
MACRO__(0xA7AC, ## __VA_ARGS__), \
MACRO__(0xA7AD, ## __VA_ARGS__)
/* RPL-P */
#define XE_RPLP_IDS(MACRO__, ...) \
XE_RPLU_IDS(MACRO__, ## __VA_ARGS__), \
MACRO__(0xA720, ## __VA_ARGS__), \
MACRO__(0xA7A0, ## __VA_ARGS__), \
MACRO__(0xA7A8, ## __VA_ARGS__), \
MACRO__(0xA7AA, ## __VA_ARGS__), \
MACRO__(0xA7AB, ## __VA_ARGS__)
/* DG2 */
#define XE_DG2_G10_IDS(MACRO__, ...) \
MACRO__(0x5690, ## __VA_ARGS__), \
MACRO__(0x5691, ## __VA_ARGS__), \
MACRO__(0x5692, ## __VA_ARGS__), \
MACRO__(0x56A0, ## __VA_ARGS__), \
MACRO__(0x56A1, ## __VA_ARGS__), \
MACRO__(0x56A2, ## __VA_ARGS__), \
MACRO__(0x56BE, ## __VA_ARGS__), \
MACRO__(0x56BF, ## __VA_ARGS__)
#define XE_DG2_G11_IDS(MACRO__, ...) \
MACRO__(0x5693, ## __VA_ARGS__), \
MACRO__(0x5694, ## __VA_ARGS__), \
MACRO__(0x5695, ## __VA_ARGS__), \
MACRO__(0x56A5, ## __VA_ARGS__), \
MACRO__(0x56A6, ## __VA_ARGS__), \
MACRO__(0x56B0, ## __VA_ARGS__), \
MACRO__(0x56B1, ## __VA_ARGS__), \
MACRO__(0x56BA, ## __VA_ARGS__), \
MACRO__(0x56BB, ## __VA_ARGS__), \
MACRO__(0x56BC, ## __VA_ARGS__), \
MACRO__(0x56BD, ## __VA_ARGS__)
#define XE_DG2_G12_IDS(MACRO__, ...) \
MACRO__(0x5696, ## __VA_ARGS__), \
MACRO__(0x5697, ## __VA_ARGS__), \
MACRO__(0x56A3, ## __VA_ARGS__), \
MACRO__(0x56A4, ## __VA_ARGS__), \
MACRO__(0x56B2, ## __VA_ARGS__), \
MACRO__(0x56B3, ## __VA_ARGS__)
#define XE_DG2_IDS(MACRO__, ...) \
XE_DG2_G10_IDS(MACRO__, ## __VA_ARGS__),\
XE_DG2_G11_IDS(MACRO__, ## __VA_ARGS__),\
XE_DG2_G12_IDS(MACRO__, ## __VA_ARGS__)
#define XE_ATS_M150_IDS(MACRO__, ...) \
MACRO__(0x56C0, ## __VA_ARGS__), \
MACRO__(0x56C2, ## __VA_ARGS__)
#define XE_ATS_M75_IDS(MACRO__, ...) \
MACRO__(0x56C1, ## __VA_ARGS__)
#define XE_ATS_M_IDS(MACRO__, ...) \
XE_ATS_M150_IDS(MACRO__, ## __VA_ARGS__),\
XE_ATS_M75_IDS(MACRO__, ## __VA_ARGS__)
/* MTL / ARL */
#define XE_MTL_IDS(MACRO__, ...) \
MACRO__(0x7D40, ## __VA_ARGS__), \
MACRO__(0x7D41, ## __VA_ARGS__), \
MACRO__(0x7D45, ## __VA_ARGS__), \
MACRO__(0x7D51, ## __VA_ARGS__), \
MACRO__(0x7D55, ## __VA_ARGS__), \
MACRO__(0x7D60, ## __VA_ARGS__), \
MACRO__(0x7D67, ## __VA_ARGS__), \
MACRO__(0x7DD1, ## __VA_ARGS__), \
MACRO__(0x7DD5, ## __VA_ARGS__)
/* PVC */
#define XE_PVC_IDS(MACRO__, ...) \
MACRO__(0x0B69, ## __VA_ARGS__), \
MACRO__(0x0B6E, ## __VA_ARGS__), \
MACRO__(0x0BD4, ## __VA_ARGS__), \
MACRO__(0x0BD5, ## __VA_ARGS__), \
MACRO__(0x0BD6, ## __VA_ARGS__), \
MACRO__(0x0BD7, ## __VA_ARGS__), \
MACRO__(0x0BD8, ## __VA_ARGS__), \
MACRO__(0x0BD9, ## __VA_ARGS__), \
MACRO__(0x0BDA, ## __VA_ARGS__), \
MACRO__(0x0BDB, ## __VA_ARGS__), \
MACRO__(0x0BE0, ## __VA_ARGS__), \
MACRO__(0x0BE1, ## __VA_ARGS__), \
MACRO__(0x0BE5, ## __VA_ARGS__)
#define XE_LNL_IDS(MACRO__, ...) \
MACRO__(0x6420, ## __VA_ARGS__), \
MACRO__(0x64A0, ## __VA_ARGS__), \
MACRO__(0x64B0, ## __VA_ARGS__)
#define XE_BMG_IDS(MACRO__, ...) \
MACRO__(0xE202, ## __VA_ARGS__), \
MACRO__(0xE20B, ## __VA_ARGS__), \
MACRO__(0xE20C, ## __VA_ARGS__), \
MACRO__(0xE20D, ## __VA_ARGS__), \
MACRO__(0xE212, ## __VA_ARGS__)
#endif
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
#include "btop.hpp"
#include <iterator>
#include <ranges>
#include <string_view>
#include <vector>
auto main(int argc, const char* argv[]) -> int {
return btop_main(std::views::counted(std::next(argv), argc - 1) | std::ranges::to<std::vector<std::string_view>>());
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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 },
};
@@ -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);
}
@@ -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);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,114 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include <Availability.h>
#if __MAC_OS_X_VERSION_MIN_REQUIRED > 101504
#include "sensors.hpp"
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hidsystem/IOHIDEventSystemClient.h>
#include <string>
#include <numeric>
#include <vector>
extern "C" {
typedef struct __IOHIDEvent *IOHIDEventRef;
typedef struct __IOHIDServiceClient *IOHIDServiceClientRef;
#ifdef __LP64__
typedef double IOHIDFloat;
#else
typedef float IOHIDFloat;
#endif
#define IOHIDEventFieldBase(type) (type << 16)
#define kIOHIDEventTypeTemperature 15
IOHIDEventSystemClientRef IOHIDEventSystemClientCreate(CFAllocatorRef allocator);
int IOHIDEventSystemClientSetMatching(IOHIDEventSystemClientRef client, CFDictionaryRef match);
int IOHIDEventSystemClientSetMatchingMultiple(IOHIDEventSystemClientRef client, CFArrayRef match);
IOHIDEventRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t, int32_t, int64_t);
CFStringRef IOHIDServiceClientCopyProperty(IOHIDServiceClientRef service, CFStringRef property);
IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef event, int32_t field);
// create a dict ref, like for temperature sensor {"PrimaryUsagePage":0xff00, "PrimaryUsage":0x5}
CFDictionaryRef matching(int page, int usage) {
CFNumberRef nums[2];
CFStringRef keys[2];
keys[0] = CFStringCreateWithCString(0, "PrimaryUsagePage", 0);
keys[1] = CFStringCreateWithCString(0, "PrimaryUsage", 0);
nums[0] = CFNumberCreate(0, kCFNumberSInt32Type, &page);
nums[1] = CFNumberCreate(0, kCFNumberSInt32Type, &usage);
CFDictionaryRef dict = CFDictionaryCreate(0, (const void **)keys, (const void **)nums, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease(keys[0]);
CFRelease(keys[1]);
return dict;
}
double getValue(IOHIDServiceClientRef sc) {
IOHIDEventRef event = IOHIDServiceClientCopyEvent(sc, kIOHIDEventTypeTemperature, 0, 0); // here we use ...CopyEvent
IOHIDFloat temp = 0.0;
if (event != 0) {
temp = IOHIDEventGetFloatValue(event, IOHIDEventFieldBase(kIOHIDEventTypeTemperature));
CFRelease(event);
}
return temp;
}
} // extern C
long long Cpu::ThermalSensors::getSensors() {
CFDictionaryRef thermalSensors = matching(0xff00, 5); // 65280_10 = FF00_16
// thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05
// can be checked by ioreg -lfx
IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
IOHIDEventSystemClientSetMatching(system, thermalSensors);
CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system);
std::vector<double> temps;
if (matchingsrvs) {
long count = CFArrayGetCount(matchingsrvs);
for (int i = 0; i < count; i++) {
IOHIDServiceClientRef sc = (IOHIDServiceClientRef)CFArrayGetValueAtIndex(matchingsrvs, i);
if (sc) {
CFStringRef name = IOHIDServiceClientCopyProperty(sc, CFSTR("Product")); // here we use ...CopyProperty
if (name) {
char buf[200];
CFStringGetCString(name, buf, 200, kCFStringEncodingASCII);
std::string n(buf);
// this is just a guess, nobody knows which sensors mean what
// on my system PMU tdie 3 and 9 are missing...
// there is also PMU tdev1-8 but it has negative values??
// there is also eACC for efficiency package but it only has 2 entries
// and pACC for performance but it has 7 entries (2 - 9) WTF
if (n.starts_with("eACC") or n.starts_with("pACC")) {
temps.push_back(getValue(sc));
}
CFRelease(name);
}
}
}
CFRelease(matchingsrvs);
}
CFRelease(system);
CFRelease(thermalSensors);
if (temps.empty()) return 0ll;
return round(std::accumulate(temps.begin(), temps.end(), 0ll) / temps.size());
}
#endif
@@ -0,0 +1,27 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
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
@@ -0,0 +1,154 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#include "smc.hpp"
static constexpr size_t MaxIndexCount = sizeof("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") - 1;
static constexpr const char *KeyIndexes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static UInt32 _strtoul(char *str, int size, int base) {
UInt32 total = 0;
int i;
for (i = 0; i < size; i++) {
if (base == 16) {
total += str[i] << (size - 1 - i) * 8;
} else {
total += (unsigned char)(str[i] << (size - 1 - i) * 8);
}
}
return total;
}
static void _ultostr(char *str, UInt32 val) {
str[0] = '\0';
snprintf(str, 5, "%c%c%c%c",
(unsigned int)val >> 24,
(unsigned int)val >> 16,
(unsigned int)val >> 8,
(unsigned int)val);
}
namespace Cpu {
SMCConnection::SMCConnection() {
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
result = IOServiceGetMatchingServices(0, matchingDictionary, &iterator);
if (result != kIOReturnSuccess) {
throw std::runtime_error("failed to get AppleSMC");
}
device = IOIteratorNext(iterator);
IOObjectRelease(iterator);
if (device == 0) {
throw std::runtime_error("failed to get SMC device");
}
result = IOServiceOpen(device, mach_task_self(), 0, &conn);
IOObjectRelease(device);
if (result != kIOReturnSuccess) {
throw std::runtime_error("failed to get SMC connection");
}
}
SMCConnection::~SMCConnection() {
IOServiceClose(conn);
}
long long SMCConnection::getSMCTemp(char *key) {
SMCVal_t val;
kern_return_t result;
result = SMCReadKey(key, &val);
if (result == kIOReturnSuccess) {
if (val.dataSize > 0) {
if (strcmp(val.dataType, DATATYPE_SP78) == 0) {
// convert sp78 value to temperature
int intValue = val.bytes[0] * 256 + (unsigned char)val.bytes[1];
return static_cast<long long>(intValue / 256.0);
}
}
}
return -1;
}
// core means physical core in SMC, while in core map it's cpu threads :-/ Only an issue on hackintosh?
// this means we can only get the T per physical core
// another issue with the SMC API is that the key is always 4 chars -> what with systems with more than 9 physical cores?
// no Mac models with more than 18 threads are released, so no problem so far
// according to VirtualSMC docs (hackintosh fake SMC) the enumeration follows with alphabetic chars - not implemented yet here (nor in VirtualSMC)
long long SMCConnection::getTemp(int core) {
char key[] = SMC_KEY_CPU_TEMP;
if (core >= 0) {
if ((size_t)core > MaxIndexCount) {
return -1;
}
snprintf(key, 5, "TC%1cc", KeyIndexes[core]);
}
long long result = getSMCTemp(key);
if (result == -1) {
// try again with C
snprintf(key, 5, "TC%1dC", KeyIndexes[core]);
result = getSMCTemp(key);
}
return result;
}
kern_return_t SMCConnection::SMCReadKey(UInt32Char_t key, SMCVal_t *val) {
kern_return_t result;
SMCKeyData_t inputStructure;
SMCKeyData_t outputStructure;
memset(&inputStructure, 0, sizeof(SMCKeyData_t));
memset(&outputStructure, 0, sizeof(SMCKeyData_t));
memset(val, 0, sizeof(SMCVal_t));
inputStructure.key = _strtoul(key, 4, 16);
inputStructure.data8 = SMC_CMD_READ_KEYINFO;
result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
if (result != kIOReturnSuccess)
return result;
val->dataSize = outputStructure.keyInfo.dataSize;
_ultostr(val->dataType, outputStructure.keyInfo.dataType);
inputStructure.keyInfo.dataSize = val->dataSize;
inputStructure.data8 = SMC_CMD_READ_BYTES;
result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
if (result != kIOReturnSuccess)
return result;
memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
return kIOReturnSuccess;
}
kern_return_t SMCConnection::SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure) {
size_t structureInputSize;
size_t structureOutputSize;
structureInputSize = sizeof(SMCKeyData_t);
structureOutputSize = sizeof(SMCKeyData_t);
return IOConnectCallStructMethod(conn, index,
// inputStructure
inputStructure, structureInputSize,
// outputStructure
outputStructure, &structureOutputSize);
}
} // namespace Cpu
@@ -0,0 +1,117 @@
/* Copyright 2021 Aristocratos (jakob@qvantnet.com)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
indent = tab
tab-size = 4
*/
#pragma once
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/ps/IOPSKeys.h>
#include <IOKit/ps/IOPowerSources.h>
#include <stdexcept>
#define VERSION "0.01"
#define KERNEL_INDEX_SMC 2
#define SMC_CMD_READ_BYTES 5
#define SMC_CMD_WRITE_BYTES 6
#define SMC_CMD_READ_INDEX 8
#define SMC_CMD_READ_KEYINFO 9
#define SMC_CMD_READ_PLIMIT 11
#define SMC_CMD_READ_VERS 12
#define DATATYPE_FPE2 "fpe2"
#define DATATYPE_UINT8 "ui8 "
#define DATATYPE_UINT16 "ui16"
#define DATATYPE_UINT32 "ui32"
#define DATATYPE_SP78 "sp78"
// key values
#define SMC_KEY_CPU_TEMP "TC0P" // proximity temp?
#define SMC_KEY_CPU_DIODE_TEMP "TC0D" // diode temp?
#define SMC_KEY_CPU_DIE_TEMP "TC0F" // die temp?
#define SMC_KEY_CPU1_TEMP "TC1C"
#define SMC_KEY_CPU2_TEMP "TC2C" // etc
#define SMC_KEY_FAN0_RPM_CUR "F0Ac"
typedef struct {
char major;
char minor;
char build;
char reserved[1];
UInt16 release;
} SMCKeyData_vers_t;
typedef struct {
UInt16 version;
UInt16 length;
UInt32 cpuPLimit;
UInt32 gpuPLimit;
UInt32 memPLimit;
} SMCKeyData_pLimitData_t;
typedef struct {
UInt32 dataSize;
UInt32 dataType;
char dataAttributes;
} SMCKeyData_keyInfo_t;
typedef char SMCBytes_t[32];
typedef struct {
UInt32 key;
SMCKeyData_vers_t vers;
SMCKeyData_pLimitData_t pLimitData;
SMCKeyData_keyInfo_t keyInfo;
char result;
char status;
char data8;
UInt32 data32;
SMCBytes_t bytes;
} SMCKeyData_t;
typedef char UInt32Char_t[5];
typedef struct {
UInt32Char_t key;
UInt32 dataSize;
UInt32Char_t dataType;
SMCBytes_t bytes;
} SMCVal_t;
namespace Cpu {
class SMCConnection {
public:
SMCConnection();
virtual ~SMCConnection();
long long getTemp(int core);
private:
kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val);
long long getSMCTemp(char *key);
kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure);
io_connect_t conn;
kern_return_t result;
mach_port_t masterPort;
io_iterator_t iterator;
io_object_t device;
};
} // namespace Cpu