1
0
mirror of https://github.com/Llewellynvdm/conky.git synced 2024-11-15 17:47:09 +00:00

Merge pull request #181 from fonic/patch-1

Rewrite/enhancement of nvidia module
This commit is contained in:
Brenden Matthews 2015-12-11 12:55:17 -08:00
commit 39e0c1db13

View File

@ -28,6 +28,37 @@
*
*/
/*
*
* Author:
* Fonic <fonic.maxxim@live.com>
*
* TODO:
* - Add third argument to module to allow querying multiple GPUs/fans etc.,
* e.g. ${nvidia gputemp 2}, ${nvidia fanlevel 1}
* - Implement static flag; static values are only queried once to save CPU time,
* e.g. min/max values, temp threshold etc.
* - Move decoding of GPU/MEM freqs to print_nvidia_value() using QUERY_SPECIAL
* so that all quirks are located there
* - Implement nvs->print_type to allow control over how the value is printed
* (int, float, temperature...)
* - Rename set_nvidia_type() to set_nvidia_query (requires changes in core.cc)
*
* Showcase (conky.conf):
* --==| NVIDIA | ==--
* GPU ${nvidia gpufreq}MHz (${nvidia gpufreqmin}-${nvidia gpufreqmax}MHz)
* MEM ${nvidia memfreq}MHz (${nvidia memfreqmin}-${nvidia memfreqmax}MHz)
* MTR ${nvidia mtrfreq}MHz (${nvidia mtrfreqmin}-${nvidia mtrfreqmax}MHz)
* PERF Level ${nvidia perflevel} (${nvidia perflevelmin}-${nvidia perflevelmax}), Mode: ${nvidia perfmode}
* VRAM ${nvidia memutil}% (${nvidia memused}MB/${nvidia memtotal}MB)
* LOAD GPU ${nvidia gpuutil}%, RAM ${nvidia membwutil}%, VIDEO ${nvidia videoutil}%, PCIe ${nvidia pcieutil}%
* TEMP GPU ${nvidia gputemp}°C (${nvidia gputempthreshold}°C max.), SYS ${nvidia ambienttemp}°C
* FAN ${nvidia fanspeed}% RPM (${nvidia fanlevel}%)
*
*/
#include "conky.h"
#include "logging.h"
#include "nvidia.h"
@ -35,30 +66,226 @@
#include "x11.h"
#include <NVCtrl/NVCtrlLib.h>
const int nvidia_query_to_attr[] = {NV_CTRL_GPU_CORE_TEMPERATURE,
NV_CTRL_GPU_CORE_THRESHOLD,
NV_CTRL_AMBIENT_TEMPERATURE,
NV_CTRL_GPU_CURRENT_CLOCK_FREQS,
NV_CTRL_GPU_CURRENT_CLOCK_FREQS,
NV_CTRL_IMAGE_SETTINGS};
// Separators for nvidia string parsing
// (sample: "perf=0, nvclock=324, nvclockmin=324, nvclockmax=324 ; perf=1, nvclock=549, nvclockmin=549, nvclockmax=549")
#define NV_KVPAIR_SEPARATORS ", ;"
#define NV_KEYVAL_SEPARATORS "="
// Module arguments
const char* translate_module_argument[] = {
"temp", // Temperatures
"gputemp",
"threshold",
"gputempthreshold",
"ambient",
"ambienttemp",
"gpufreq", // GPU frequency
"gpufreqcur",
"gpufreqmin",
"gpufreqmax",
"memfreq", // Memory frequency
"memfreqcur",
"memfreqmin",
"memfreqmax",
"mtrfreq", // Memory transfer rate frequency
"mtrfreqcur",
"mtrfreqmin",
"mtrfreqmax",
"perflevel", // Performance levels
"perflevelcur",
"perflevelmin",
"perflevelmax",
"perfmode",
"gpuutil", // Load/utilization
"membwutil", // NOTE: this is the memory _bandwidth_ utilization, not the percentage of used/available memory!
"videoutil",
"pcieutil",
"mem", // RAM statistics
"memused",
"memfree",
"memavail",
"memmax",
"memtotal",
"memutil",
"memperc",
"fanspeed", // Fan/cooler
"fanlevel",
"imagequality" // Miscellaneous
};
// Enum for module arguments
typedef enum _ARG_ID {
ARG_TEMP,
ARG_GPU_TEMP,
ARG_THRESHOLD,
ARG_GPU_TEMP_THRESHOLD,
ARG_AMBIENT,
ARG_AMBIENT_TEMP,
ARG_GPU_FREQ,
ARG_GPU_FREQ_CUR,
ARG_GPU_FREQ_MIN,
ARG_GPU_FREQ_MAX,
ARG_MEM_FREQ,
ARG_MEM_FREQ_CUR,
ARG_MEM_FREQ_MIN,
ARG_MEM_FREQ_MAX,
ARG_MTR_FREQ,
ARG_MTR_FREQ_CUR,
ARG_MTR_FREQ_MIN,
ARG_MTR_FREQ_MAX,
ARG_PERF_LEVEL,
ARG_PERF_LEVEL_CUR,
ARG_PERF_LEVEL_MIN,
ARG_PERF_LEVEL_MAX,
ARG_PERF_MODE,
ARG_GPU_UTIL,
ARG_MEM_BW_UTIL,
ARG_VIDEO_UTIL,
ARG_PCIE_UTIL,
ARG_MEM,
ARG_MEM_USED,
ARG_MEM_FREE,
ARG_MEM_AVAIL,
ARG_MEM_MAX,
ARG_MEM_TOTAL,
ARG_MEM_UTIL,
ARG_MEM_PERC,
ARG_FAN_SPEED,
ARG_FAN_LEVEL,
ARG_IMAGEQUALITY,
ARG_UNKNOWN
} ARG_ID;
// Nvidia query targets
const int translate_nvidia_target[] = {
NV_CTRL_TARGET_TYPE_X_SCREEN,
NV_CTRL_TARGET_TYPE_GPU,
NV_CTRL_TARGET_TYPE_FRAMELOCK,
NV_CTRL_TARGET_TYPE_VCSC,
NV_CTRL_TARGET_TYPE_GVI,
NV_CTRL_TARGET_TYPE_COOLER,
NV_CTRL_TARGET_TYPE_THERMAL_SENSOR,
NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER,
NV_CTRL_TARGET_TYPE_DISPLAY,
};
// Enum for nvidia query targets
typedef enum _TARGET_ID {
TARGET_SCREEN,
TARGET_GPU,
TARGET_FRAMELOCK,
TARGET_VCSC,
TARGET_GVI,
TARGET_COOLER,
TARGET_THERMAL,
TARGET_3DVISION,
TARGET_DISPLAY
} TARGET_ID;
// Nvidia query attributes
const int translate_nvidia_attribute[] = {
NV_CTRL_GPU_CORE_TEMPERATURE,
NV_CTRL_GPU_CORE_THRESHOLD,
NV_CTRL_AMBIENT_TEMPERATURE,
NV_CTRL_GPU_CURRENT_CLOCK_FREQS,
NV_CTRL_GPU_CURRENT_CLOCK_FREQS,
NV_CTRL_STRING_PERFORMANCE_MODES,
NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS,
NV_CTRL_GPU_POWER_MIZER_MODE,
NV_CTRL_STRING_GPU_UTILIZATION,
NV_CTRL_USED_DEDICATED_GPU_MEMORY,
0,
NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY, // NOTE: NV_CTRL_TOTAL_GPU_MEMORY would be better, but returns KB instead of MB
0,
NV_CTRL_THERMAL_COOLER_SPEED,
NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL, // NOTE: not sure if this should be NV_CTRL_THERMAL_COOLER_LEVEL instead
NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL,
NV_CTRL_IMAGE_SETTINGS,
};
// Enum for nvidia query attributes
typedef enum _ATTR_ID {
ATTR_GPU_TEMP,
ATTR_GPU_TEMP_THRESHOLD,
ATTR_AMBIENT_TEMP,
ATTR_GPU_FREQ,
ATTR_MEM_FREQ,
ATTR_PERFMODES_STRING,
ATTR_FREQS_STRING,
ATTR_PERF_MODE,
ATTR_UTILS_STRING,
ATTR_MEM_USED,
ATTR_MEM_FREE,
ATTR_MEM_TOTAL,
ATTR_MEM_UTIL,
ATTR_FAN_SPEED,
ATTR_FAN_LEVEL,
ATTR_PERF_LEVEL,
ATTR_IMAGE_QUALITY,
} ATTR_ID;
// Enum for query type
typedef enum _QUERY_ID {
NV_TEMP,
NV_TEMP_THRESHOLD,
NV_TEMP_AMBIENT,
NV_GPU_FREQ,
NV_MEM_FREQ,
NV_IMAGE_QUALITY
QUERY_VALUE,
QUERY_STRING,
QUERY_STRING_VALUE,
QUERY_SPECIAL
} QUERY_ID;
// Enum for string token search mode
typedef enum _SEARCH_ID {
SEARCH_FIRST,
SEARCH_LAST,
SEARCH_MIN,
SEARCH_MAX
} SEARCH_ID;
// Global struct to keep track of queries
struct nvidia_s {
int interval;
int print_as_float;
QUERY_ID type;
QUERY_ID query;
TARGET_ID target;
ATTR_ID attribute;
char *token;
SEARCH_ID search;
};
static Display *nvdisplay;
namespace {
class nvidia_display_setting: public conky::simple_config_setting<std::string> {
typedef conky::simple_config_setting<std::string> Base;
@ -104,76 +331,381 @@ namespace {
nvidia_display_setting nvidia_display;
}
static int get_nvidia_value(QUERY_ID qid){
int tmp;
Display *dpy = nvdisplay ? nvdisplay : display;
if(!dpy || !XNVCTRLQueryAttribute(dpy, 0, 0, nvidia_query_to_attr[qid], &tmp)){
return -1;
}
/* FIXME: when are the low 2 bytes of NV_GPU_FREQ needed? */
if (qid == NV_GPU_FREQ)
return tmp >> 16;
if (qid == NV_MEM_FREQ)
return tmp & 0xFFFF;
return tmp;
}
// Evaluate module parameters and prepare query
int set_nvidia_type(struct text_object *obj, const char *arg)
{
struct nvidia_s *nvs;
int aid;
// Initialize global struct
obj->data.opaque = malloc(sizeof(struct nvidia_s));
nvs = static_cast<nvidia_s *>(obj->data.opaque);
memset(nvs, 0, sizeof(struct nvidia_s));
// Translate parameter to id
for (aid=0; aid < ARG_UNKNOWN; aid++) {
if (strcmp(arg, translate_module_argument[aid]) == 0)
break;
}
//fprintf(stderr, "parameter: %s -> aid: %d\n", arg, aid);
// Evaluate parameter
switch(aid) {
case ARG_TEMP: // GPU temperature
case ARG_GPU_TEMP:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_GPU_TEMP;
break;
case ARG_THRESHOLD: // GPU temperature threshold
case ARG_GPU_TEMP_THRESHOLD:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_GPU_TEMP_THRESHOLD;
break;
case ARG_AMBIENT: // Ambient temperature
case ARG_AMBIENT_TEMP:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_AMBIENT_TEMP;
break;
case ARG_GPU_FREQ: // Current GPU clock
case ARG_GPU_FREQ_CUR:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_GPU_FREQ;
break;
case ARG_GPU_FREQ_MIN: // Minimum GPU clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "nvclockmin";
nvs->search = SEARCH_MIN;
break;
case ARG_GPU_FREQ_MAX: // Maximum GPU clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "nvclockmax";
nvs->search = SEARCH_MAX;
break;
case ARG_MEM_FREQ: // Current memory clock
case ARG_MEM_FREQ_CUR:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_FREQ;
break;
case ARG_MEM_FREQ_MIN: // Minimum memory clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "memclockmin";
nvs->search = SEARCH_MIN;
break;
case ARG_MEM_FREQ_MAX: // Maximum memory clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "memclockmax";
nvs->search = SEARCH_MAX;
break;
switch(arg[0]) {
case 't': // temp or threshold
nvs->print_as_float = 1;
if (arg[1] == 'e')
nvs->type = NV_TEMP;
else if (arg[1] == 'h')
nvs->type = NV_TEMP_THRESHOLD;
else
return 1;
case ARG_MTR_FREQ: // Current memory transfer rate clock
case ARG_MTR_FREQ_CUR:
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_FREQS_STRING;
nvs->token = (char*) "memTransferRate";
nvs->search = SEARCH_FIRST;
break;
case 'a': // ambient temp
nvs->print_as_float = 1;
nvs->type = NV_TEMP_AMBIENT;
case ARG_MTR_FREQ_MIN: // Minimum memory transfer rate clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "memTransferRatemin";
nvs->search = SEARCH_MIN;
break;
case 'g': // gpufreq
nvs->type = NV_GPU_FREQ;
case ARG_MTR_FREQ_MAX: // Maximum memory transfer rate clock
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "memTransferRatemax";
nvs->search = SEARCH_MAX;
break;
case 'm': // memfreq
nvs->type = NV_MEM_FREQ;
case ARG_PERF_LEVEL: // Current performance level
case ARG_PERF_LEVEL_CUR:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERF_LEVEL;
break;
case 'i': // imagequality
nvs->type = NV_IMAGE_QUALITY;
case ARG_PERF_LEVEL_MIN: // Lowest performance level
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "perf";
nvs->search = SEARCH_MIN;
break;
default:
case ARG_PERF_LEVEL_MAX: // Highest performance level
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERFMODES_STRING;
nvs->token = (char*) "perf";
nvs->search = SEARCH_MAX;
break;
case ARG_PERF_MODE: // Performance mode
nvs->query = QUERY_SPECIAL;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_PERF_MODE;
break;
case ARG_GPU_UTIL: // GPU utilization %
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_UTILS_STRING;
nvs->token = (char*) "graphics";
nvs->search = SEARCH_FIRST;
break;
case ARG_MEM_BW_UTIL: // Memory bandwidth utilization %
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_UTILS_STRING;
nvs->token = (char*) "memory";
nvs->search = SEARCH_FIRST;
break;
case ARG_VIDEO_UTIL: // Video engine utilization %
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_UTILS_STRING;
nvs->token = (char*) "video";
nvs->search = SEARCH_FIRST;
break;
case ARG_PCIE_UTIL: // PCIe bandwidth utilization %
nvs->query = QUERY_STRING_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_UTILS_STRING;
nvs->token = (char*) "PCIe";
nvs->search = SEARCH_FIRST;
break;
case ARG_MEM: // Amount of used memory
case ARG_MEM_USED:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_USED;
break;
case ARG_MEM_FREE: // Amount of free memory
case ARG_MEM_AVAIL:
nvs->query = QUERY_SPECIAL;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_FREE;
break;
case ARG_MEM_MAX: // Total amount of memory
case ARG_MEM_TOTAL:
nvs->query = QUERY_VALUE;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_TOTAL;
break;
case ARG_MEM_UTIL: // Memory utilization %
case ARG_MEM_PERC:
nvs->query = QUERY_SPECIAL;
nvs->target = TARGET_GPU;
nvs->attribute = ATTR_MEM_UTIL;
break;
case ARG_FAN_SPEED: // Fan speed
nvs->query = QUERY_VALUE;
nvs->target = TARGET_COOLER;
nvs->attribute = ATTR_FAN_SPEED;
break;
case ARG_FAN_LEVEL: // Fan level %
nvs->query = QUERY_VALUE;
nvs->target = TARGET_COOLER;
nvs->attribute = ATTR_FAN_LEVEL;
break;
case ARG_IMAGEQUALITY: // Image quality
nvs->query = QUERY_VALUE;
nvs->target = TARGET_SCREEN;
nvs->attribute = ATTR_IMAGE_QUALITY;
break;
default: // Unknown/invalid argument
return 1;
}
return 0;
}
void print_nvidia_value(struct text_object *obj, char *p, int p_max_size)
{
int value;
struct nvidia_s *nvs = static_cast<nvidia_s *>(obj->data.opaque);
if (!nvs ||
(value = get_nvidia_value(nvs->type)) == -1) {
snprintf(p, p_max_size, "N/A");
return;
// Retrieve attribute value via nvidia interface
static int get_nvidia_value(TARGET_ID tid, ATTR_ID aid)
{
Display *dpy = nvdisplay ? nvdisplay : display;
int value;
// Query nvidia interface
if(!dpy || !XNVCTRLQueryTargetAttribute(dpy, translate_nvidia_target[tid], 0, 0, translate_nvidia_attribute[aid], &value)){
return -1;
}
if (nvs->type == NV_TEMP)
temp_print(p, p_max_size, (double)value, TEMP_CELSIUS);
else if (nvs->print_as_float &&
value > 0 && value < 100)
snprintf(p, p_max_size, "%.1f", (float)value);
else
snprintf(p, p_max_size, "%d", value);
// Unpack clock values (see NVCtrl.h for details)
if (aid == ATTR_GPU_FREQ)
return value >> 16;
if (aid == ATTR_MEM_FREQ)
return value & 0xFFFF;
// Return value
return value;
}
// Retrieve attribute string via nvidia interface
static char* get_nvidia_string(TARGET_ID tid, ATTR_ID aid)
{
Display *dpy = nvdisplay ? nvdisplay : display;
char *str;
// Query nvidia interface
if (!dpy || !XNVCTRLQueryTargetStringAttribute(dpy, translate_nvidia_target[tid], 0, 0, translate_nvidia_attribute[aid], &str)) {
return NULL;
}
//fprintf(stderr, "%s", str);
return str;
}
// Retrieve token value from nvidia string
static int get_nvidia_string_value(TARGET_ID tid, ATTR_ID aid, char *token, SEARCH_ID search)
{
char *str;
char *kvp, *key, *val;
char *saveptr1, *saveptr2;
int value, temp;
// Get string via nvidia interface
str = get_nvidia_string(tid, aid);
// Split string into 'key=value' substrings, split substring
// into key and value, from value, check if token was found,
// convert value to int, evaluate value according to specified
// token search mode
value = -1;
kvp = strtok_r(str, NV_KVPAIR_SEPARATORS, &saveptr1);
while (kvp) {
key = strtok_r(kvp, NV_KEYVAL_SEPARATORS, &saveptr2);
val = strtok_r(NULL, NV_KEYVAL_SEPARATORS, &saveptr2);
if (key && val && (strcmp(token, key) == 0)) {
temp = (int) strtol(val, NULL, 0);
if (search == SEARCH_FIRST) {
value = temp;
break;
} else if (search == SEARCH_LAST) {
value = temp;
} else if (search == SEARCH_MIN) {
if ((value == -1) || (temp < value))
value = temp;
} else if (search == SEARCH_MAX) {
if (temp > value)
value = temp;
} else {
value = -1;
break;
}
}
kvp = strtok_r(NULL, NV_KVPAIR_SEPARATORS, &saveptr1);
}
// TESTING - print raw string if token was not found;
// string has to be queried again due to strtok_r()
/*if (value == -1) {
free(str);
str = get_nvidia_string(tid, aid);
fprintf(stderr, "%s", str);
}*/
// Free string, return value
free(str);
return value;
}
// Perform query and print result
void print_nvidia_value(struct text_object *obj, char *p, int p_max_size)
{
struct nvidia_s *nvs = static_cast<nvidia_s *>(obj->data.opaque);
int value, temp1, temp2;
char* str;
// Assume failure
value = -1;
str = NULL;
// Perform query
if (nvs != NULL) {
switch (nvs->query) {
case QUERY_VALUE:
value = get_nvidia_value(nvs->target, nvs->attribute);
break;
case QUERY_STRING:
str = get_nvidia_string(nvs->target, nvs->attribute);
break;
case QUERY_STRING_VALUE:
value = get_nvidia_string_value(nvs->target, nvs->attribute, nvs->token, nvs->search);
break;
case QUERY_SPECIAL:
switch (nvs->attribute) {
case ATTR_PERF_MODE:
temp1 = get_nvidia_value(nvs->target, nvs->attribute);
switch (temp1) {
case NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE:
temp2 = asprintf(&str, "Adaptive");
break;
case NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_MAXIMUM_PERFORMANCE:
temp2 = asprintf(&str, "Max. Perf.");
break;
case NV_CTRL_GPU_POWER_MIZER_MODE_AUTO:
temp2 = asprintf(&str, "Auto");
break;
case NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_CONSISTENT_PERFORMANCE:
temp2 = asprintf(&str, "Consistent");
break;
default:
temp2 = asprintf(&str, "Unknown (%d)", value);
}
break;
case ATTR_MEM_FREE:
temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED);
temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL);
value = temp2 - temp1;
break;
case ATTR_MEM_UTIL:
temp1 = get_nvidia_value(nvs->target, ATTR_MEM_USED);
temp2 = get_nvidia_value(nvs->target, ATTR_MEM_TOTAL);
value = ((float)temp1 * 100 / (float)temp2) + 0.5;
break;
}
break;
}
}
// Print result
if (value != -1) {
snprintf(p, p_max_size, "%d", value);
} else if (str != NULL) {
snprintf(p, p_max_size, "%s", str);
free(str);
} else {
snprintf(p, p_max_size, "N/A");
}
}
// Cleanup
void free_nvidia(struct text_object *obj)
{
free_and_zero(obj->data.opaque);