New version - code rewritten to c, added GTK3 gui, added Core and Package Power monitoring via MSR
This commit is contained in:
parent
afdd058cfb
commit
37fc68ca76
44
README.md
44
README.md
|
@ -1,2 +1,42 @@
|
|||
# zenmonitor
|
||||
Sensors monitor for [zenpower](https://github.com/ocerman/zenpower/)
|
||||
# Zen monitor
|
||||
Zen monitor is monitoring software for AMD Zen-based CPUs.
|
||||
|
||||
It can monitor these values:
|
||||
- CPU Temperature
|
||||
- CPU Core (SVI2) Voltage, Current and Power
|
||||
- SOC (SVI2) Voltage, Current and Power
|
||||
- Package and Core Power
|
||||
|
||||
![screenshot](screenshot.png)
|
||||
|
||||
## Dependencies
|
||||
- [zenpower driver](https://github.com/ocerman/zenpower/) - For monitoring CPU temperature and SVI2 sensors
|
||||
- MSR driver - For monitoring Package/Core Power
|
||||
|
||||
Follow [zenpower README.md](https://github.com/ocerman/zenpower/blob/master/README.md) to install and activate zenpower module.
|
||||
Enter `sudo modprobe msr` to enable MSR driver.
|
||||
|
||||
## Building
|
||||
Make sure that GTK3 dev package and common build tools are installed.
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
## Running
|
||||
```
|
||||
sudo ./zenpower
|
||||
```
|
||||
|
||||
## Setup on ubuntu
|
||||
First follow [installation instructions on zenpower](https://github.com/ocerman/zenpower/blob/master/README.md#installation-commands-for-ubuntu)
|
||||
Then:
|
||||
```
|
||||
sudo modprobe msr
|
||||
sudo bash -c 'echo "msr" > /etc/modules-load.d/msr.conf'
|
||||
sudo apt install build-essential libgtk-3-dev git
|
||||
cd ~
|
||||
git clone https://github.com/ocerman/zenmonitor
|
||||
cd zenmonitor
|
||||
make
|
||||
sudo ./zenmonitor
|
||||
```
|
||||
|
|
2
makefile
Executable file
2
makefile
Executable file
|
@ -0,0 +1,2 @@
|
|||
build:
|
||||
cc -Isrc/include `pkg-config --cflags gtk+-3.0` src/*.c src/ss/*.c -o zenmonitor `pkg-config --libs gtk+-3.0` -lm -no-pie
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
164
src/gui.c
Normal file
164
src/gui.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include <cpuid.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "gui.h"
|
||||
#include "zenmonitor.h"
|
||||
|
||||
static GtkTreeModel *model = NULL;
|
||||
static guint timeout = 0;
|
||||
static SensorSource *sensor_sources;
|
||||
|
||||
enum {
|
||||
COLUMN_NAME,
|
||||
COLUMN_VALUE,
|
||||
NUM_COLUMNS
|
||||
};
|
||||
|
||||
static void init_sensors() {
|
||||
GtkTreeIter iter;
|
||||
GSList *sensor;
|
||||
GtkListStore *store;
|
||||
SensorSource *source;
|
||||
const SensorInit *data;
|
||||
gboolean added;
|
||||
guint i = 0;
|
||||
|
||||
store = GTK_LIST_STORE(model);
|
||||
for (source = sensor_sources; source->drv; source++) {
|
||||
if (source->func_init()){
|
||||
source->sensors = source->func_get_sensors();
|
||||
if (source->sensors != NULL) {
|
||||
source->enabled = TRUE;
|
||||
|
||||
sensor = source->sensors;
|
||||
while (sensor) {
|
||||
data = (SensorInit*)sensor->data;
|
||||
gtk_list_store_append(store, &iter);
|
||||
gtk_list_store_set(store, &iter,
|
||||
COLUMN_NAME, data->label,
|
||||
COLUMN_VALUE, " --- ",
|
||||
-1);
|
||||
sensor = sensor->next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GtkTreeModel* create_model (void) {
|
||||
GtkListStore *store;
|
||||
store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
|
||||
return GTK_TREE_MODEL (store);
|
||||
}
|
||||
|
||||
static gboolean update_data (gpointer data) {
|
||||
GtkTreeIter iter;
|
||||
guint number;
|
||||
GSList *node;
|
||||
gchar *value;
|
||||
SensorSource *source;
|
||||
const SensorInit *sensorData;
|
||||
|
||||
if (model == NULL)
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
if (!gtk_tree_model_get_iter_first (model, &iter))
|
||||
return G_SOURCE_REMOVE;
|
||||
|
||||
for (source = sensor_sources; source->drv; source++) {
|
||||
if (!source->enabled)
|
||||
continue;
|
||||
|
||||
source->func_update();
|
||||
if (source->sensors){
|
||||
node = source->sensors;
|
||||
|
||||
while(node) {
|
||||
sensorData = (SensorInit*)node->data;
|
||||
if (*(sensorData->value) != ERROR_VALUE)
|
||||
value = g_strdup_printf(sensorData->printf_format, *(sensorData->value));
|
||||
else
|
||||
value = g_strdup(" ? ? ?");
|
||||
|
||||
gtk_list_store_set(GTK_LIST_STORE (model), &iter, COLUMN_VALUE, value, -1);
|
||||
node = node->next;
|
||||
if (!gtk_tree_model_iter_next(model, &iter))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void add_columns (GtkTreeView *treeview) {
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *column;
|
||||
GtkTreeModel *model = gtk_tree_view_get_model (treeview);
|
||||
|
||||
// NAME
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
|
||||
column = gtk_tree_view_column_new_with_attributes ("Sensor", renderer,
|
||||
"text", COLUMN_NAME,
|
||||
NULL);
|
||||
g_object_set(renderer, "family", "monotype", NULL);
|
||||
gtk_tree_view_append_column (treeview, column);
|
||||
|
||||
//VALUE
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes ("Value", renderer,
|
||||
"text", COLUMN_VALUE,
|
||||
NULL);
|
||||
g_object_set(renderer, "family", "monotype", NULL);
|
||||
gtk_tree_view_append_column (treeview, column);
|
||||
}
|
||||
|
||||
|
||||
int start_gui (SensorSource *ss) {
|
||||
GtkWidget *window;
|
||||
GtkWidget *treeview;
|
||||
GtkWidget *sw;
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *dialog;
|
||||
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title(GTK_WINDOW(window), "Zen monitor");
|
||||
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
||||
gtk_window_set_default_size(GTK_WINDOW(window), 330, 300);
|
||||
|
||||
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
|
||||
|
||||
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
|
||||
gtk_container_add(GTK_CONTAINER (window), vbox);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
|
||||
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_box_pack_start(GTK_BOX (vbox), sw, TRUE, TRUE, 0);
|
||||
|
||||
model = create_model();
|
||||
treeview = gtk_tree_view_new_with_model(model);
|
||||
g_object_unref(model);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER(sw), treeview);
|
||||
add_columns(GTK_TREE_VIEW(treeview));
|
||||
gtk_widget_show_all(window);
|
||||
|
||||
if (check_zen()){
|
||||
sensor_sources = ss;
|
||||
init_sensors();
|
||||
timeout = g_timeout_add(300, update_data, NULL);
|
||||
}
|
||||
else{
|
||||
dialog = gtk_message_dialog_new(GTK_WINDOW (window),
|
||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
|
||||
"Zen CPU not detected!");
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
gtk_main();
|
||||
return 0;
|
||||
}
|
||||
|
1
src/include/gui.h
Normal file
1
src/include/gui.h
Normal file
|
@ -0,0 +1 @@
|
|||
int start_gui();
|
3
src/include/msr.h
Normal file
3
src/include/msr.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
gboolean msr_init();
|
||||
void msr_update();
|
||||
GSList* msr_get_sensors();
|
23
src/include/zenmonitor.h
Normal file
23
src/include/zenmonitor.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#define ERROR_VALUE -999.0
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gchar *label;
|
||||
float *value;
|
||||
const gchar *printf_format;
|
||||
}
|
||||
SensorInit;
|
||||
|
||||
typedef struct {
|
||||
const gchar *drv;
|
||||
gboolean (*func_init)();
|
||||
GSList* (*func_get_sensors)();
|
||||
void (*func_update)();
|
||||
gboolean enabled;
|
||||
GSList *sensors;
|
||||
|
||||
} SensorSource;
|
||||
|
||||
SensorInit* sensor_init_new(void);
|
||||
void sensor_init_free(SensorInit *s);
|
||||
gboolean check_zen();
|
3
src/include/zenpower.h
Normal file
3
src/include/zenpower.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
gboolean zenpower_init();
|
||||
GSList* zenpower_get_sensors();
|
||||
void zenpower_update();
|
156
src/ss/msr.c
Normal file
156
src/ss/msr.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
#include <glib.h>
|
||||
#include <cpuid.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include "zenmonitor.h"
|
||||
#include "msr.h"
|
||||
|
||||
#define MSR_PWR_PRINTF_FORMAT " %8.3f W"
|
||||
#define MESUREMENT_TIME 0.1
|
||||
|
||||
// AMD PPR = https://www.amd.com/system/files/TechDocs/54945_PPR_Family_17h_Models_00h-0Fh.pdf
|
||||
// AMD OSRR = https://developer.amd.com/wp-content/resources/56255_3_03.PDF
|
||||
|
||||
guint cores = 0;
|
||||
guint threads_per_code = 0;
|
||||
gdouble energy_unit = 0;
|
||||
|
||||
gint *msr_files = NULL;
|
||||
|
||||
gulong package_eng_b = 0;
|
||||
gulong package_eng_a = 0;
|
||||
gulong *core_eng_b = NULL;
|
||||
gulong *core_eng_a = NULL;
|
||||
|
||||
gfloat package_power;
|
||||
gfloat *core_power;
|
||||
|
||||
static guint get_core_count() {
|
||||
guint eax = 0, ebx = 0, ecx = 0, edx = 0;
|
||||
guint logical_cpus;
|
||||
|
||||
// AMD PPR: page 57 - CPUID_Fn00000001_EBX
|
||||
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||
logical_cpus = (ebx >> 16) & 0xFF;
|
||||
|
||||
// AMD PPR: page 82 - CPUID_Fn8000001E_EBX
|
||||
__get_cpuid(0x8000001E, &eax, &ebx, &ecx, &edx);
|
||||
threads_per_code = ((ebx >> 8) & 0xF) + 1;
|
||||
|
||||
if (threads_per_code == 0)
|
||||
return logical_cpus;
|
||||
|
||||
return logical_cpus / threads_per_code;
|
||||
}
|
||||
|
||||
static gint open_msr(gshort core) {
|
||||
gchar msr_path[20];
|
||||
sprintf(msr_path, "/dev/cpu/%d/msr", core * threads_per_code);
|
||||
return open(msr_path, O_RDONLY);
|
||||
}
|
||||
|
||||
static gboolean read_msr(gint file, guint index, gulong *data) {
|
||||
if (file < 0)
|
||||
return FALSE;
|
||||
|
||||
return pread(file, data, sizeof *data, index) == sizeof *data;
|
||||
}
|
||||
|
||||
gdouble get_energy_unit() {
|
||||
gulong data;
|
||||
// AMD OSRR: page 139 - MSRC001_0299
|
||||
if (!read_msr(msr_files[0], 0xC0010299, &data))
|
||||
return 0.0;
|
||||
|
||||
return pow(1.0/2.0, (double)((data >> 8) & 0x1F));
|
||||
}
|
||||
|
||||
gulong get_package_energy() {
|
||||
gulong data;
|
||||
// AMD OSRR: page 139 - MSRC001_029B
|
||||
if (!read_msr(msr_files[0], 0xC001029B, &data))
|
||||
return 0;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
gulong get_core_energy(gint core) {
|
||||
gulong data;
|
||||
// AMD OSRR: page 139 - MSRC001_029A
|
||||
if (!read_msr(msr_files[core], 0xC001029A, &data))
|
||||
return 0;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
gboolean msr_init() {
|
||||
int i;
|
||||
|
||||
if (!check_zen())
|
||||
return FALSE;
|
||||
|
||||
cores = get_core_count();
|
||||
if (cores == 0)
|
||||
return FALSE;
|
||||
|
||||
msr_files = malloc(cores * sizeof (gint));
|
||||
for (i = 0; i < cores; i++) {
|
||||
msr_files[i] = open_msr(i);
|
||||
}
|
||||
|
||||
energy_unit = get_energy_unit();
|
||||
if (energy_unit == 0)
|
||||
return FALSE;
|
||||
|
||||
core_eng_b = malloc(cores * sizeof (gulong));
|
||||
core_eng_a = malloc(cores * sizeof (gulong));
|
||||
core_power = malloc(cores * sizeof (gfloat));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void msr_update() {
|
||||
GSList *list = NULL;
|
||||
gint i;
|
||||
|
||||
package_eng_b = get_package_energy();
|
||||
for (i = 0; i < cores; i++) {
|
||||
core_eng_b[i] = get_core_energy(i);
|
||||
}
|
||||
|
||||
usleep(MESUREMENT_TIME*1000000);
|
||||
|
||||
package_eng_a = get_package_energy();
|
||||
for (i = 0; i < cores; i++) {
|
||||
core_eng_a[i] = get_core_energy(i);
|
||||
}
|
||||
|
||||
package_power = (package_eng_a - package_eng_b) * energy_unit / MESUREMENT_TIME;
|
||||
for (i = 0; i < cores; i++) {
|
||||
core_power[i] = (core_eng_a[i] - core_eng_b[i]) * energy_unit / MESUREMENT_TIME;
|
||||
}
|
||||
}
|
||||
|
||||
GSList* msr_get_sensors() {
|
||||
GSList *list = NULL;
|
||||
SensorInit *data;
|
||||
gint i;
|
||||
|
||||
data = sensor_init_new();
|
||||
data->label = g_strdup("Package Power");
|
||||
data->value = &package_power;
|
||||
data->printf_format = MSR_PWR_PRINTF_FORMAT;
|
||||
list = g_slist_append(list, data);
|
||||
|
||||
for (i = 0; i < cores; i++) {
|
||||
data = sensor_init_new();
|
||||
data->label = g_strdup_printf("Core %d Power", i);
|
||||
data->value = &(core_power[i]);
|
||||
data->printf_format = MSR_PWR_PRINTF_FORMAT;
|
||||
list = g_slist_append(list, data);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
91
src/ss/zenpower.c
Normal file
91
src/ss/zenpower.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
#include <glib.h>
|
||||
#include "zenmonitor.h"
|
||||
#include "zenpower.h"
|
||||
|
||||
static gchar *zenpowerDir = NULL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar *label;
|
||||
const gchar *file;
|
||||
const gchar *printf_format;
|
||||
const double adjust_ratio;
|
||||
float current_value;
|
||||
} HwmonSensor;
|
||||
|
||||
HwmonSensor hwmon_sensors[] = {
|
||||
{"CPU Temperature (tCtrl)", "temp1_input", " %6.2f°C", 1000.0, 0.0},
|
||||
{"CPU Temperature (tDie)", "temp2_input", " %6.2f°C", 1000.0, 0.0},
|
||||
{"CPU Core Voltage (SVI2)", "in1_input", " %8.3f V", 1000.0, 0.0},
|
||||
{"SOC Voltage (SVI2)", "in2_input", " %8.3f V", 1000.0, 0.0},
|
||||
{"CPU Core Current (SVI2)", "curr1_input", " %8.3f A", 1000.0, 0.0},
|
||||
{"SOC Current (SVI2)", "curr2_input", " %8.3f A", 1000.0, 0.0},
|
||||
{"CPU Core Power (SVI2)", "power1_input", " %8.3f W", 1000000.0, 0.0},
|
||||
{"SOC Power (SVI2)", "power2_input", " %8.3f W", 1000000.0, 0.0},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
static gboolean read_raw_hwmon_value(const gchar *dir, const gchar *file, gchar **result) {
|
||||
gchar *full_path;
|
||||
gboolean file_result;
|
||||
|
||||
full_path = g_strdup_printf("/sys/class/hwmon/%s/%s", dir, file);
|
||||
file_result = g_file_get_contents(full_path, result, NULL, NULL);
|
||||
|
||||
g_free(full_path);
|
||||
return file_result;
|
||||
}
|
||||
|
||||
gboolean zenpower_init() {
|
||||
GDir *hwmon;
|
||||
const gchar *entry;
|
||||
gchar* name = NULL;
|
||||
|
||||
hwmon = g_dir_open("/sys/class/hwmon", 0, NULL);
|
||||
if (!hwmon)
|
||||
return FALSE;
|
||||
|
||||
while ((entry = g_dir_read_name(hwmon))) {
|
||||
read_raw_hwmon_value(entry, "name", &name);
|
||||
if (strcmp(name, "zenpower")) {
|
||||
zenpowerDir = g_strdup(entry);
|
||||
break;
|
||||
}
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
if (!zenpowerDir)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void zenpower_update() {
|
||||
gchar *tmp = NULL;
|
||||
GSList *list = NULL;
|
||||
HwmonSensor *sensor;
|
||||
|
||||
for (sensor = hwmon_sensors; sensor->label; sensor++) {
|
||||
if (read_raw_hwmon_value(zenpowerDir, sensor->file, &tmp)){
|
||||
sensor->current_value = atof(tmp) / sensor->adjust_ratio;
|
||||
g_free(tmp);
|
||||
}
|
||||
else{
|
||||
sensor->current_value = ERROR_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GSList* zenpower_get_sensors() {
|
||||
GSList *list = NULL;
|
||||
HwmonSensor *sensor;
|
||||
SensorInit *data;
|
||||
|
||||
for (sensor = hwmon_sensors; sensor->label; sensor++) {
|
||||
data = sensor_init_new();
|
||||
data->label = g_strdup(sensor->label);
|
||||
data->value = &sensor->current_value;
|
||||
data->printf_format = sensor->printf_format;
|
||||
list = g_slist_append(list, data);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
57
src/zenmonitor.c
Normal file
57
src/zenmonitor.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
#include <gtk/gtk.h>
|
||||
#include <cpuid.h>
|
||||
#include "zenmonitor.h"
|
||||
#include "zenpower.h"
|
||||
#include "msr.h"
|
||||
#include "gui.h"
|
||||
|
||||
#define AMD_STRING "AuthenticAMD"
|
||||
#define ZEN_FAMILY 0x17
|
||||
|
||||
gboolean check_zen() {
|
||||
unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0, ext_family;
|
||||
char vendor[13];
|
||||
|
||||
__get_cpuid(0, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
memcpy(vendor, &ebx, 4);
|
||||
memcpy(vendor+4, &edx, 4);
|
||||
memcpy(vendor+8, &ecx, 4);
|
||||
vendor[12] = 0;
|
||||
|
||||
if (strcmp(vendor, AMD_STRING) != 0){
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
ext_family = ((eax >> 8) & 0xF) + ((eax >> 20) & 0xFF);
|
||||
if (ext_family != ZEN_FAMILY){
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static SensorSource sensor_sources[] = {
|
||||
{ "zenpower", zenpower_init, zenpower_get_sensors, zenpower_update, FALSE, NULL },
|
||||
{ "msr", msr_init, msr_get_sensors, msr_update, FALSE, NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
SensorInit *sensor_init_new() {
|
||||
return g_new0(SensorInit, 1);
|
||||
}
|
||||
|
||||
void sensor_init_free(SensorInit *s) {
|
||||
if (s) {
|
||||
g_free(s->label);
|
||||
g_free(s);
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
gtk_init(&argc, &argv);
|
||||
start_gui(sensor_sources);
|
||||
}
|
44
zenmonitor
44
zenmonitor
|
@ -1,44 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
hwmon="/sys/class/hwmon"
|
||||
mdevs=`ls $hwmon`
|
||||
zenmon=""
|
||||
|
||||
echo -n "Looking for zenpower ..."
|
||||
|
||||
for dev in $mdevs; do
|
||||
path="$hwmon/$dev"
|
||||
devname=`cat $path/name`
|
||||
if [ "$devname" == "zenpower" ]; then
|
||||
zenmon="$path"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$zenmon" ]; then
|
||||
echo "NOT FOUND"
|
||||
exit
|
||||
else
|
||||
echo "Found"
|
||||
fi
|
||||
|
||||
echo Starting Monitor...
|
||||
echo Press Q to exit.
|
||||
echo
|
||||
|
||||
while true; do
|
||||
core=(`cat $zenmon/in1_input $zenmon/curr1_input $zenmon/power1_input`)
|
||||
soc=(`cat $zenmon/in2_input $zenmon/curr2_input $zenmon/power2_input`)
|
||||
temps=(`cat $zenmon/temp1_input $zenmon/temp2_input`)
|
||||
|
||||
echo "${core[0]} ${core[1]} ${core[2]}" | awk '{ printf " Core: %7.3fV %7.3fA %7.3fW\n", $1 / 1000, $2 / 1000, $3 / 1000000 }'
|
||||
echo "${soc[0]} ${soc[1]} ${soc[2]}" | awk '{ printf " SoC: %7.3fV %7.3fA %7.3fW\n", $1 / 1000, $2 / 1000, $3 / 1000000 }'
|
||||
echo "${temps[0]} ${temps[1]}" | awk '{ printf " tDie: %6.2f°C\ntCtrl: %6.2f°C\n", $1 / 1000, $2 / 1000 }'
|
||||
|
||||
read -t 0.2 -N 1 input
|
||||
if [[ $input = "q" ]] || [[ $input = "Q" ]]; then
|
||||
echo
|
||||
break
|
||||
fi
|
||||
|
||||
echo -en "\e[4A"
|
||||
done
|
Loading…
Reference in a new issue