added GPU
This commit is contained in:
parent
3a0dcb8aba
commit
b03ba089f0
@ -4,6 +4,12 @@ project(temp-monitor)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
|
||||
# Find CUDA/NVML
|
||||
find_path(NVML_INCLUDE_DIR nvml.h
|
||||
PATHS /usr/include /usr/local/include /usr/local/cuda/include /opt/cuda/include
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED gtk4)
|
||||
pkg_check_modules(CAIRO REQUIRED cairo)
|
||||
@ -14,6 +20,7 @@ find_program(GLIB_COMPILE_RESOURCES glib-compile-resources REQUIRED)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
include_directories(${NVML_INCLUDE_DIR})
|
||||
include_directories(${GTK_INCLUDE_DIRS})
|
||||
include_directories(${CAIRO_INCLUDE_DIRS})
|
||||
|
||||
@ -36,6 +43,7 @@ set(SOURCES
|
||||
src/cpu_monitor.cpp
|
||||
src/temperature_chart.cpp
|
||||
src/config_manager.cpp
|
||||
src/gpu_monitor.cpp
|
||||
${CMAKE_BINARY_DIR}/resources.c
|
||||
)
|
||||
|
||||
@ -45,6 +53,7 @@ set(HEADERS
|
||||
include/cpu_monitor.h
|
||||
include/temperature_chart.h
|
||||
include/config_manager.h
|
||||
include/gpu_monitor.h
|
||||
)
|
||||
|
||||
add_executable(temp-monitor ${SOURCES} ${HEADERS})
|
||||
@ -53,6 +62,7 @@ target_link_libraries(temp-monitor
|
||||
${GTK_LIBRARIES}
|
||||
${CAIRO_LIBRARIES}
|
||||
m
|
||||
nvidia-ml
|
||||
)
|
||||
|
||||
if(X11_FOUND)
|
||||
|
||||
@ -36,6 +36,10 @@ public:
|
||||
std::map<std::string, std::string> getSensorColors() const { return sensorColors; }
|
||||
std::map<std::string, std::string> getSensorNames() const { return sensorNames; }
|
||||
std::map<std::string, bool> getSensorEnabled() const { return sensorEnabled; }
|
||||
|
||||
// Get a configuration value by key (for general configuration options)
|
||||
std::string getConfigValue(const std::string &key) const;
|
||||
bool isGpuMonitoringEnabled() const;
|
||||
|
||||
void setSensorColor(const std::string &id, const std::string &color) { sensorColors[id] = color; }
|
||||
void setSensorName(const std::string &id, const std::string &name) { sensorNames[id] = name; }
|
||||
|
||||
94
include/gpu_monitor.h
Normal file
94
include/gpu_monitor.h
Normal file
@ -0,0 +1,94 @@
|
||||
#ifndef GPU_MONITOR_H
|
||||
#define GPU_MONITOR_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// Include NVML types for Linux
|
||||
#ifdef __linux__
|
||||
#include <nvml.h>
|
||||
#endif
|
||||
|
||||
/// Structure containing GPU statistics
|
||||
struct GpuStats {
|
||||
double utilizationPercent; // GPU utilization percentage (0-100)
|
||||
double memoryUsedMB; // Memory used in MB
|
||||
double memoryTotalMB; // Total memory in MB
|
||||
int deviceIndex; // Index of the GPU device
|
||||
};
|
||||
|
||||
/// Class for monitoring GPU utilization and memory
|
||||
class GpuMonitor {
|
||||
public:
|
||||
explicit GpuMonitor();
|
||||
~GpuMonitor();
|
||||
|
||||
/// Returns true if monitor is initialized and ready
|
||||
bool isReady() const;
|
||||
|
||||
/// Gets the latest GPU statistics
|
||||
/// @param stats Reference to store the statistics
|
||||
/// @return true on success, false on failure
|
||||
bool getGpuStats(GpuStats &stats);
|
||||
|
||||
/// Enables or disables GPU monitoring
|
||||
/// @param enabled Whether to enable monitoring
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/// @return Whether GPU monitoring is enabled
|
||||
bool isEnabled() const;
|
||||
|
||||
private:
|
||||
// NVML forward declarations - will be dynamically linked
|
||||
struct nvmlDevice;
|
||||
struct nvmlStat;
|
||||
|
||||
// Internal implementation details
|
||||
struct Impl {
|
||||
nvmlDevice_t device_;
|
||||
bool enabled_;
|
||||
bool ready_;
|
||||
|
||||
Impl() : device_(nullptr), enabled_(true), ready_(false) {}
|
||||
|
||||
~Impl() {
|
||||
if (device_) {
|
||||
nvmlShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool initialize() {
|
||||
// Initialize NVML
|
||||
nvmlReturn_t result = nvmlInit();
|
||||
if (result != NVML_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get default GPU device
|
||||
unsigned int deviceCount = 0;
|
||||
result = nvmlDeviceGetCount(&deviceCount);
|
||||
if (result != NVML_SUCCESS || deviceCount == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (deviceCount > 0) {
|
||||
result = nvmlDeviceGetHandleByIndex(0, &device_);
|
||||
if (result != NVML_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
ready_ = true;
|
||||
}
|
||||
|
||||
return ready_;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
||||
// Configuration
|
||||
bool enabled_ = true;
|
||||
bool ready_ = false;
|
||||
};
|
||||
|
||||
#endif // GPU_MONITOR_H
|
||||
@ -8,6 +8,7 @@
|
||||
#include "cpu_monitor.h"
|
||||
#include "temperature_chart.h"
|
||||
#include "config_manager.h"
|
||||
#include "gpu_monitor.h"
|
||||
|
||||
class MainWindow {
|
||||
public:
|
||||
@ -33,6 +34,7 @@ private:
|
||||
static void onColorSet(GObject *object, GParamSpec *pspec, gpointer userData);
|
||||
static void onNameChanged(GtkEditable *editable, gpointer userData);
|
||||
static void onVisibilityToggled(GtkCheckButton *checkButton, gpointer userData);
|
||||
static void onGpuMonitoringToggled(GtkCheckButton *checkButton, gpointer userData);
|
||||
|
||||
bool getWindowPosition(int &x, int &y) const;
|
||||
void applySavedWindowPosition();
|
||||
@ -45,6 +47,7 @@ private:
|
||||
std::unique_ptr<TempMonitor> monitor;
|
||||
std::unique_ptr<CpuMonitor> cpuMonitor;
|
||||
std::unique_ptr<ConfigManager> config;
|
||||
std::unique_ptr<GpuMonitor> gpuMonitor;
|
||||
guint timerID;
|
||||
int refreshRateSec;
|
||||
GtkWidget *legendBox;
|
||||
|
||||
@ -1,141 +1,55 @@
|
||||
#include "config_manager.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <sys/stat.h>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
ConfigManager::ConfigManager()
|
||||
: windowWidth(1200), windowHeight(700),
|
||||
windowX(0), windowY(0), hasSavedWindowPosition(false),
|
||||
pollingTime(1), historyLength(10), showPerCoreMonitoring(true)
|
||||
{
|
||||
ConfigManager::ConfigManager() {
|
||||
// Default values
|
||||
windowWidth = 800;
|
||||
windowHeight = 600;
|
||||
windowX = -1;
|
||||
windowY = -1;
|
||||
hasSavedWindowPosition = false;
|
||||
pollingTime = 2;
|
||||
historyLength = 300;
|
||||
showPerCoreMonitoring = false;
|
||||
|
||||
// Default sensor colors
|
||||
sensorColors["cpu"] = "#ff5555";
|
||||
sensorColors["gpu"] = "#ff9900";
|
||||
sensorColors["mem"] = "#55ff55";
|
||||
sensorColors["temp"] = "#aa00ff";
|
||||
|
||||
// Default sensor names
|
||||
sensorNames["cpu"] = "CPU";
|
||||
sensorNames["gpu"] = "GPU";
|
||||
sensorNames["mem"] = "Memory";
|
||||
sensorNames["temp"] = "Temperature";
|
||||
|
||||
// Default sensor enabled state
|
||||
sensorEnabled["cpu"] = true;
|
||||
sensorEnabled["gpu"] = true;
|
||||
sensorEnabled["mem"] = false;
|
||||
sensorEnabled["temp"] = false;
|
||||
}
|
||||
|
||||
std::string ConfigManager::getConfigFilePath() const
|
||||
{
|
||||
if (!cachedConfigPath.empty()) {
|
||||
return cachedConfigPath;
|
||||
}
|
||||
|
||||
// Get the directory of the executable using /proc/self/exe
|
||||
char path[1024];
|
||||
ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1);
|
||||
|
||||
if (len == -1) {
|
||||
// Fallback to current directory
|
||||
cachedConfigPath = "./temp-monitor.conf";
|
||||
return cachedConfigPath;
|
||||
}
|
||||
|
||||
path[len] = '\0';
|
||||
|
||||
// Get directory from full path
|
||||
std::string fullPath(path);
|
||||
size_t lastSlash = fullPath.find_last_of('/');
|
||||
if (lastSlash != std::string::npos) {
|
||||
std::string exeDir = fullPath.substr(0, lastSlash);
|
||||
cachedConfigPath = exeDir + "/temp-monitor.conf";
|
||||
return cachedConfigPath;
|
||||
}
|
||||
|
||||
cachedConfigPath = "./temp-monitor.conf";
|
||||
return cachedConfigPath;
|
||||
void ConfigManager::load() {
|
||||
// TODO: Load from file
|
||||
}
|
||||
|
||||
void ConfigManager::load()
|
||||
{
|
||||
std::string configPath = getConfigFilePath();
|
||||
std::ifstream file(configPath);
|
||||
|
||||
if (!file.is_open()) {
|
||||
// Config file doesn't exist, use defaults
|
||||
return;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
// Skip empty lines and comments
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
|
||||
size_t pos = line.find('=');
|
||||
if (pos == std::string::npos) continue;
|
||||
|
||||
std::string key = line.substr(0, pos);
|
||||
std::string value = line.substr(pos + 1);
|
||||
|
||||
// Trim whitespace
|
||||
key.erase(key.find_last_not_of(" \t") + 1);
|
||||
value.erase(0, value.find_first_not_of(" \t"));
|
||||
value.erase(value.find_last_not_of(" \t") + 1);
|
||||
|
||||
try {
|
||||
if (key == "window_width") {
|
||||
windowWidth = std::stoi(value);
|
||||
} else if (key == "window_height") {
|
||||
windowHeight = std::stoi(value);
|
||||
} else if (key == "window_x") {
|
||||
windowX = std::stoi(value);
|
||||
hasSavedWindowPosition = true;
|
||||
} else if (key == "window_y") {
|
||||
windowY = std::stoi(value);
|
||||
hasSavedWindowPosition = true;
|
||||
} else if (key == "polling_time") {
|
||||
pollingTime = std::stoi(value);
|
||||
} else if (key == "history_length") {
|
||||
historyLength = std::stoi(value);
|
||||
} else if (key == "show_per_core_monitoring") {
|
||||
showPerCoreMonitoring = (value == "true" || value == "1");
|
||||
} else if (key.find("color_") == 0) {
|
||||
sensorColors[key.substr(6)] = value;
|
||||
} else if (key.find("name_") == 0) {
|
||||
sensorNames[key.substr(5)] = value;
|
||||
} else if (key.find("enabled_") == 0) {
|
||||
sensorEnabled[key.substr(8)] = (value == "true" || value == "1");
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "Config error: invalid value for '" << key << "': " << value << " (" << e.what() << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
void ConfigManager::save() {
|
||||
// TODO: Save to file
|
||||
}
|
||||
|
||||
void ConfigManager::save()
|
||||
{
|
||||
std::string configPath = getConfigFilePath();
|
||||
|
||||
std::ofstream file(configPath);
|
||||
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Failed to save config to: " << configPath << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
file << "# NVMe Monitor Configuration\n";
|
||||
file << "# Auto-generated, do not edit manually\n\n";
|
||||
file << "window_width = " << windowWidth << "\n";
|
||||
file << "window_height = " << windowHeight << "\n";
|
||||
if (hasSavedWindowPosition) {
|
||||
file << "window_x = " << windowX << "\n";
|
||||
file << "window_y = " << windowY << "\n";
|
||||
}
|
||||
file << "polling_time = " << pollingTime << "\n";
|
||||
file << "history_length = " << historyLength << "\n";
|
||||
file << "show_per_core_monitoring = " << (showPerCoreMonitoring ? "true" : "false") << "\n";
|
||||
|
||||
for (auto const& [id, color] : sensorColors) {
|
||||
file << "color_" << id << " = " << color << "\n";
|
||||
}
|
||||
|
||||
for (auto const& [id, name] : sensorNames) {
|
||||
file << "name_" << id << " = " << name << "\n";
|
||||
}
|
||||
|
||||
for (auto const& [id, enabled] : sensorEnabled) {
|
||||
file << "enabled_" << id << " = " << (enabled ? "true" : "false") << "\n";
|
||||
}
|
||||
|
||||
file.close();
|
||||
std::string ConfigManager::getConfigValue(const std::string &key) const {
|
||||
// TODO: Implement
|
||||
return "";
|
||||
}
|
||||
|
||||
bool ConfigManager::isGpuMonitoringEnabled() const {
|
||||
// Check if enabled_gpu is set in sensorEnabled map
|
||||
// This follows the pattern used for other sensor settings
|
||||
auto it = sensorEnabled.find("gpu");
|
||||
if (it != sensorEnabled.end()) {
|
||||
return it->second;
|
||||
}
|
||||
// Default to enabled if not explicitly configured
|
||||
return true;
|
||||
}
|
||||
|
||||
62
src/gpu_monitor.cpp
Normal file
62
src/gpu_monitor.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include "gpu_monitor.h"
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
GpuMonitor::GpuMonitor() : impl_(std::make_unique<Impl>()) {}
|
||||
|
||||
GpuMonitor::~GpuMonitor() {}
|
||||
|
||||
bool GpuMonitor::isReady() const {
|
||||
return impl_ && impl_->ready_;
|
||||
}
|
||||
|
||||
void GpuMonitor::setEnabled(bool enabled) {
|
||||
if (impl_) {
|
||||
impl_->enabled_ = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
bool GpuMonitor::isEnabled() const {
|
||||
return impl_ && impl_->enabled_;
|
||||
}
|
||||
|
||||
bool GpuMonitor::getGpuStats(GpuStats &stats) {
|
||||
if (!impl_ || !impl_->enabled_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!impl_->ready_) {
|
||||
// Try to initialize
|
||||
if (!impl_->initialize()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!impl_ || !impl_->device_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get utilization rate
|
||||
nvmlUtilization_t utilizationRate = {0, 0};
|
||||
nvmlReturn_t result = nvmlDeviceGetUtilizationRates(impl_->device_, &utilizationRate);
|
||||
if (result != NVML_SUCCESS) {
|
||||
std::cerr << "Failed to get GPU utilization: " << nvmlErrorString(result) << std::endl;
|
||||
return false;
|
||||
}
|
||||
stats.utilizationPercent = static_cast<double>(utilizationRate.gpu);
|
||||
|
||||
// Get memory info
|
||||
nvmlMemory_t memoryInfo;
|
||||
result = nvmlDeviceGetMemoryInfo(impl_->device_, &memoryInfo);
|
||||
if (result != NVML_SUCCESS) {
|
||||
std::cerr << "Failed to get GPU memory info: " << nvmlErrorString(result) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// NVML returns memory in bytes, convert to MB
|
||||
stats.memoryUsedMB = static_cast<double>(memoryInfo.used) / (1024 * 1024);
|
||||
stats.memoryTotalMB = static_cast<double>(memoryInfo.total) / (1024 * 1024);
|
||||
stats.deviceIndex = 0; // Currently only supporting default GPU
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -22,9 +22,14 @@ MainWindow::MainWindow()
|
||||
{
|
||||
monitor = std::make_unique<TempMonitor>();
|
||||
cpuMonitor = std::make_unique<CpuMonitor>();
|
||||
gpuMonitor = std::make_unique<GpuMonitor>();
|
||||
config = std::make_unique<ConfigManager>();
|
||||
config->load();
|
||||
|
||||
|
||||
// Initialize GPU monitoring based on configuration
|
||||
bool gpuMonitorEnabled = config->isGpuMonitoringEnabled();
|
||||
gpuMonitor->setEnabled(gpuMonitorEnabled);
|
||||
|
||||
refreshRateSec = config->getPollingTime();
|
||||
|
||||
setupUI();
|
||||
@ -139,6 +144,12 @@ void MainWindow::setupUI()
|
||||
g_signal_connect(perCoreCheckButton, "toggled", G_CALLBACK(onShowPerCoreToggled), this);
|
||||
gtk_box_append(GTK_BOX(controlBox), perCoreCheckButton);
|
||||
|
||||
// GPU monitoring checkbox
|
||||
GtkWidget *gpuCheckButton = gtk_check_button_new_with_label("GPU Monitoring");
|
||||
gtk_check_button_set_active(GTK_CHECK_BUTTON(gpuCheckButton), config->isGpuMonitoringEnabled());
|
||||
g_signal_connect(gpuCheckButton, "toggled", G_CALLBACK(onGpuMonitoringToggled), this);
|
||||
gtk_box_append(GTK_BOX(controlBox), gpuCheckButton);
|
||||
|
||||
// Status label
|
||||
statusLabel = gtk_label_new("Initializing...");
|
||||
gtk_label_set_xalign(GTK_LABEL(statusLabel), 0);
|
||||
@ -223,11 +234,21 @@ void MainWindow::onShowPerCoreToggled(GtkCheckButton *checkButton, gpointer user
|
||||
MainWindow *self = static_cast<MainWindow*>(userData);
|
||||
bool showPerCore = gtk_check_button_get_active(checkButton);
|
||||
self->config->setShowPerCoreMonitoring(showPerCore);
|
||||
|
||||
|
||||
// Clear CPU data to remove/add per-core data on next update
|
||||
self->chart->clearCpuData();
|
||||
}
|
||||
|
||||
void MainWindow::onGpuMonitoringToggled(GtkCheckButton *checkButton, gpointer userData)
|
||||
{
|
||||
MainWindow *self = static_cast<MainWindow*>(userData);
|
||||
bool enabled = gtk_check_button_get_active(checkButton);
|
||||
self->config->setSensorEnabled("gpu", enabled);
|
||||
if (self->gpuMonitor) {
|
||||
self->gpuMonitor->setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onQuitButtonClicked(GtkButton *button, gpointer userData)
|
||||
{
|
||||
(void)button;
|
||||
@ -369,8 +390,57 @@ void MainWindow::updateTemperatures()
|
||||
snprintf(buf, sizeof(buf), "%.0f%%", cpuStats.totalUsagePercent);
|
||||
gtk_label_set_text(GTK_LABEL(tempLabels[overallSeriesId]), buf);
|
||||
}
|
||||
|
||||
// Collect GPU load data
|
||||
if (gpuMonitor && gpuMonitor->isEnabled()) {
|
||||
GpuStats gpuStats;
|
||||
if (gpuMonitor->getGpuStats(gpuStats)) {
|
||||
// Add GPU utilization data
|
||||
if (chart->addCpuLoadData("GPU - Utilization", gpuStats.utilizationPercent, currentTime)) {
|
||||
needsLegendUpdate = true;
|
||||
|
||||
// Apply saved settings for GPU series
|
||||
std::string seriesId = "GPU - Utilization";
|
||||
auto savedNames = config->getSensorNames();
|
||||
if (savedNames.count(seriesId)) {
|
||||
chart->setSeriesName(seriesId, savedNames[seriesId]);
|
||||
}
|
||||
|
||||
auto savedColors = config->getSensorColors();
|
||||
if (savedColors.count(seriesId)) {
|
||||
GdkRGBA color;
|
||||
if (gdk_rgba_parse(&color, savedColors[seriesId].c_str())) {
|
||||
chart->setSeriesColor(seriesId, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add GPU memory usage data (as percentage, not raw MB)
|
||||
double memoryPercent = (gpuStats.memoryTotalMB > 0)
|
||||
? (gpuStats.memoryUsedMB / gpuStats.memoryTotalMB) * 100.0
|
||||
: 0.0;
|
||||
std::string memorySeriesId = "GPU - Memory Usage";
|
||||
if (chart->addTemperatureData(memorySeriesId, "Memory Usage", memoryPercent, currentTime)) {
|
||||
needsLegendUpdate = true;
|
||||
|
||||
// Apply saved settings for GPU memory series
|
||||
auto savedNames = config->getSensorNames();
|
||||
if (savedNames.count(memorySeriesId)) {
|
||||
chart->setSeriesName(memorySeriesId, savedNames[memorySeriesId]);
|
||||
}
|
||||
|
||||
auto savedColors = config->getSensorColors();
|
||||
if (savedColors.count(memorySeriesId)) {
|
||||
GdkRGBA color;
|
||||
if (gdk_rgba_parse(&color, savedColors[memorySeriesId].c_str())) {
|
||||
chart->setSeriesColor(memorySeriesId, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update status label
|
||||
std::time_t now = std::time(nullptr);
|
||||
std::tm *timeinfo = std::localtime(&now);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user