Repository: ocerman/zenmonitor
Branch: master
Commit: fa17e94b3061
Files: 18
Total size: 37.4 KB
Directory structure:
gitextract_ikdbt1cl/
├── LICENSE
├── README.md
├── data/
│ ├── org.pkexec.zenmonitor.policy.in
│ ├── zenmonitor-root.desktop.in
│ └── zenmonitor.desktop.in
├── makefile
└── src/
├── gui.c
├── include/
│ ├── gui.h
│ ├── msr.h
│ ├── os.h
│ ├── sysfs.h
│ ├── zenmonitor.h
│ └── zenpower.h
├── ss/
│ ├── msr.c
│ ├── os.c
│ └── zenpower.c
├── sysfs.c
└── zenmonitor.c
================================================
FILE CONTENTS
================================================
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018-2020 Ondrej Čerman
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 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.
================================================
FILE: README.md
================================================
# 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 (RAPL)
- Core Frequency (from OS)

## Dependencies
- [zenpower driver](https://github.com/ocerman/zenpower/) - For monitoring CPU temperature and SVI2 sensors
- MSR driver - For monitoring Package/Core Power (RAPL)
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
```
## Launching
You can launch app by `sudo ./zenmonitor`, or you can install it to your system and then launch it from your OS menu.
Note: Because superuser privileges are usually needed to access data from MSR driver, you need to launch zenmonitor as root for monitoring CPU power usage (RAPL).
Alternatively, you can set capabilities to zenmonitor executable: `sudo setcap cap_sys_rawio,cap_dac_read_search+ep ./zenmonitor`
## Command line arguments
``--coreid`` - Display core_id instead of core index
## Installing
By default, Zenmonitor will be installed to /usr/local.
```
sudo make install
```
To add menu item for launching zenpower as root (Polkit is required):
```
sudo make install-polkit
```
## Uninstalling
```
sudo make uninstall
```
## 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 make install
sudo make install-polkit
```
## Setup on Arch
You may use the AUR package [zenmonitor-git](https://aur.archlinux.org/packages/zenmonitor-git/) to install via [traditional method](https://wiki.archlinux.org/index.php/Arch_User_Repository) or using an AUR helper (like yay)
================================================
FILE: data/org.pkexec.zenmonitor.policy.in
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<action id="org.pkexec.zenmonitor">
<description>Run Zenmonitor as root</description>
<message>Authentication is required to run Zenmonitor as root.</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@APP_EXEC@</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>
================================================
FILE: data/zenmonitor-root.desktop.in
================================================
[Desktop Entry]
Name=Zenmonitor (root)
Comment=Monitoring software for AMD Zen-based CPUs
Exec=pkexec @APP_EXEC@
Type=Application
Categories=GTK;System;
Terminal=false
Keywords=CPU;AMD;zen;system;core;speed;clock;temperature;voltage;
================================================
FILE: data/zenmonitor.desktop.in
================================================
[Desktop Entry]
Name=Zenmonitor
Comment=Monitoring software for AMD Zen-based CPUs
Exec=@APP_EXEC@
Type=Application
Categories=GTK;System;
Terminal=false
Keywords=CPU;AMD;zen;system;core;speed;clock;temperature;voltage;
================================================
FILE: makefile
================================================
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
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 -Wall
install:
mkdir -p $(DESTDIR)$(PREFIX)/bin
install -m 755 zenmonitor $(DESTDIR)$(PREFIX)/bin
mkdir -p $(DESTDIR)$(PREFIX)/share/applications
sed -e "s|@APP_EXEC@|${DESTDIR}${PREFIX}/bin/zenmonitor|" \
data/zenmonitor.desktop.in > \
$(DESTDIR)$(PREFIX)/share/applications/zenmonitor.desktop
install-polkit:
sed -e "s|@APP_EXEC@|${DESTDIR}${PREFIX}/bin/zenmonitor|" \
data/zenmonitor-root.desktop.in > \
$(DESTDIR)$(PREFIX)/share/applications/zenmonitor-root.desktop
sed -e "s|@APP_EXEC@|${DESTDIR}${PREFIX}/bin/zenmonitor|" \
data/org.pkexec.zenmonitor.policy.in > \
$(DESTDIR)/usr/share/polkit-1/actions/org.pkexec.zenmonitor.policy
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/zenmonitor
rm -f $(DESTDIR)$(PREFIX)/share/applications/zenmonitor.desktop
rm -f $(DESTDIR)$(PREFIX)/share/applications/zenmonitor-root.desktop
rm -f $(DESTDIR)/usr/share/polkit-1/actions/org.pkexec.zenmonitor.policy
clean:
rm -f zenmonitor
================================================
FILE: src/gui.c
================================================
#include <cpuid.h>
#include <gtk/gtk.h>
#include "gui.h"
#include "zenmonitor.h"
GtkWidget *window;
static GtkTreeModel *model = NULL;
static guint timeout = 0;
static SensorSource *sensor_sources;
static const guint defaultHeight = 350;
enum {
COLUMN_NAME,
COLUMN_HINT,
COLUMN_VALUE,
COLUMN_MIN,
COLUMN_MAX,
NUM_COLUMNS
};
static void init_sensors() {
GtkTreeIter iter;
GSList *sensor;
GtkListStore *store;
SensorSource *source;
const SensorInit *data;
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_HINT, data->hint,
COLUMN_VALUE, " --- ",
COLUMN_MIN, " --- ",
COLUMN_MAX, " --- ",
-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, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
return GTK_TREE_MODEL (store);
}
static void set_list_column_value(float num, const gchar *printf_format, GtkTreeIter *iter, gint column){
gchar *value;
if (num != ERROR_VALUE)
value = g_strdup_printf(printf_format, num);
else
value = g_strdup(" ? ? ?");
gtk_list_store_set(GTK_LIST_STORE (model), iter, column, value, -1);
g_free(value);
}
static gboolean update_data (gpointer data) {
GtkTreeIter iter;
GSList *node;
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;
set_list_column_value(*(sensorData->value), sensorData->printf_format, &iter, COLUMN_VALUE);
set_list_column_value(*(sensorData->min), sensorData->printf_format, &iter, COLUMN_MIN);
set_list_column_value(*(sensorData->max), sensorData->printf_format, &iter, COLUMN_MAX);
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;
// 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);
//MIN
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ("Min", renderer,
"text", COLUMN_MIN,
NULL);
g_object_set(renderer, "family", "monotype", NULL);
gtk_tree_view_append_column (treeview, column);
//MAX
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ("Max", renderer,
"text", COLUMN_MAX,
NULL);
g_object_set(renderer, "family", "monotype", NULL);
gtk_tree_view_append_column (treeview, column);
}
static void about_btn_clicked(GtkButton *button, gpointer user_data) {
GtkWidget *dialog;
const gchar *website = "https://github.com/ocerman/zenmonitor";
const gchar *msg = "<b>Zen Monitor</b> %s\n"
"Monitoring software for AMD Zen-based CPUs\n"
"<a href=\"%s\">%s</a>\n\n"
"Created by: Ondrej Čerman";
dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW (window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO, GTK_BUTTONS_OK,
msg, VERSION, website, website);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
}
static void clear_btn_clicked(GtkButton *button, gpointer user_data) {
SensorSource *source;
for (source = sensor_sources; source->drv; source++) {
if (!source->enabled)
continue;
source->func_clear_minmax();
}
}
static gboolean mid_search_eq_func(GtkTreeModel *model, gint column, const gchar *key, GtkTreeIter *iter) {
gchar *iter_string = NULL, *lc_iter_string = NULL, *lc_key = NULL;
gboolean result;
gtk_tree_model_get(model, iter, column, &iter_string, -1);
lc_iter_string = g_utf8_strdown(iter_string, -1);
lc_key = g_utf8_strdown(key, -1);
result = (g_strrstr(lc_iter_string, lc_key) == NULL);
g_free(iter_string);
g_free(lc_iter_string);
g_free(lc_key);
return result;
}
static void resize_to_treeview(GtkWindow* window, GtkTreeView* treeview) {
gint uiHeight, cellHeight, vSeparator, rows;
GdkRectangle r;
GtkTreeViewColumn *col = gtk_tree_view_get_column(treeview, 0);
if (!col)
return;
gtk_tree_view_column_cell_get_size(col, NULL, NULL, NULL, NULL, &cellHeight);
gtk_widget_style_get(GTK_WIDGET(treeview), "vertical-separator", &vSeparator, NULL);
rows = gtk_tree_model_iter_n_children(gtk_tree_view_get_model(treeview), NULL);
gtk_tree_view_get_visible_rect(treeview, &r);
uiHeight = defaultHeight - r.height;
gtk_window_resize(window, 500, uiHeight + (vSeparator + cellHeight) * rows);
}
int start_gui (SensorSource *ss) {
GtkWidget *about_btn;
GtkWidget *clear_btn;
GtkWidget *box;
GtkWidget *header;
GtkWidget *treeview;
GtkWidget *sw;
GtkWidget *vbox;
GtkWidget *dialog;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 500, defaultHeight);
header = gtk_header_bar_new();
gtk_header_bar_set_show_close_button(GTK_HEADER_BAR (header), TRUE);
gtk_header_bar_set_title(GTK_HEADER_BAR (header), "Zen monitor");
gtk_header_bar_set_has_subtitle(GTK_HEADER_BAR (header), TRUE);
gtk_header_bar_set_subtitle(GTK_HEADER_BAR (header), cpu_model());
gtk_window_set_titlebar (GTK_WINDOW (window), header);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (box), "linked");
about_btn = gtk_button_new();
gtk_container_add(GTK_CONTAINER(about_btn), gtk_image_new_from_icon_name("dialog-information", GTK_ICON_SIZE_BUTTON));
gtk_container_add(GTK_CONTAINER(box), about_btn);
gtk_widget_set_tooltip_text(about_btn, "About Zen monitor");
clear_btn = gtk_button_new();
gtk_container_add(GTK_CONTAINER(clear_btn), gtk_image_new_from_icon_name("edit-clear-all", GTK_ICON_SIZE_BUTTON));
gtk_container_add(GTK_CONTAINER(box), clear_btn);
gtk_widget_set_tooltip_text(clear_btn, "Clear Min/Max");
gtk_header_bar_pack_start(GTK_HEADER_BAR(header), box);
g_signal_connect(about_btn, "clicked", G_CALLBACK(about_btn_clicked), NULL);
g_signal_connect(clear_btn, "clicked", G_CALLBACK(clear_btn_clicked), NULL);
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);
gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(treeview), COLUMN_HINT);
gtk_container_add (GTK_CONTAINER(sw), treeview);
add_columns(GTK_TREE_VIEW(treeview));
gtk_widget_show_all(window);
gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COLUMN_NAME);
gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(treeview),
(GtkTreeViewSearchEqualFunc)mid_search_eq_func, model, NULL);
g_object_unref(model);
if (check_zen()){
sensor_sources = ss;
init_sensors();
resize_to_treeview(GTK_WINDOW(window), GTK_TREE_VIEW(treeview));
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;
}
================================================
FILE: src/include/gui.h
================================================
int start_gui();
================================================
FILE: src/include/msr.h
================================================
gboolean msr_init();
void msr_update();
void msr_clear_minmax();
GSList* msr_get_sensors();
================================================
FILE: src/include/os.h
================================================
gboolean os_init(void);
void os_update(void);
void os_clear_minmax(void);
GSList* os_get_sensors(void);
================================================
FILE: src/include/sysfs.h
================================================
#define SYSFS_DIR_CPUS "/sys/devices/system/cpu"
struct cpudev {
gshort coreid;
gshort cpuid;
};
struct cpudev * get_cpu_dev_ids(void);
================================================
FILE: src/include/zenmonitor.h
================================================
#define ERROR_VALUE -999.0
#define VERSION "1.4.2"
typedef struct
{
gchar *label;
gchar *hint;
float *value;
float *min;
float *max;
const gchar *printf_format;
}
SensorInit;
typedef struct {
const gchar *drv;
gboolean (*func_init)();
GSList* (*func_get_sensors)();
void (*func_update)();
void (*func_clear_minmax)();
gboolean enabled;
GSList *sensors;
} SensorSource;
SensorInit* sensor_init_new(void);
void sensor_init_free(SensorInit *s);
gboolean check_zen();
gchar *cpu_model();
guint get_core_count();
extern gboolean display_coreid;
================================================
FILE: src/include/zenpower.h
================================================
gboolean zenpower_init();
GSList* zenpower_get_sensors();
void zenpower_update();
void zenpower_clear_minmax();
================================================
FILE: src/ss/msr.c
================================================
#include <glib.h>
#include <cpuid.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "zenmonitor.h"
#include "msr.h"
#include "sysfs.h"
#define MSR_PWR_PRINTF_FORMAT " %8.3f W"
#define MSR_FID_PRINTF_FORMAT " %8.3f GHz"
#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
static guint cores = 0;
static gdouble energy_unit = 0;
static struct cpudev *cpu_dev_ids;
static gint *msr_files = NULL;
static gulong package_eng_b = 0;
static gulong package_eng_a = 0;
static gulong *core_eng_b = NULL;
static gulong *core_eng_a = NULL;
gfloat package_power;
gfloat package_power_min;
gfloat package_power_max;
gfloat *core_power;
gfloat *core_fid;
gfloat *core_power_min;
gfloat *core_power_max;
gfloat *core_fid_min;
gfloat *core_fid_max;
static gint open_msr(gshort devid) {
gchar msr_path[20];
sprintf(msr_path, "/dev/cpu/%d/msr", devid);
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;
}
gdouble get_core_fid(gint core) {
gdouble ratio;
gulong data;
// By reverse-engineering Ryzen Master, we know that
// this undocumented MSR is responsible for returning
// the FID and FDID for the core used for calculating the
// effective frequency.
//
// The FID is returned in bits [8:0]
// The FDID is returned in bits [14:8]
if (!read_msr(msr_files[core], 0xC0010293, &data))
return 0;
ratio = (gdouble)(data & 0xff) / (gdouble)((data >> 8) & 0x3F);
// The effective ratio is based on increments of 200 MHz.
return ratio * 200.0 / 1000.0;
}
gboolean msr_init() {
guint i;
if (!check_zen())
return FALSE;
cores = get_core_count();
if (cores == 0)
return FALSE;
cpu_dev_ids = get_cpu_dev_ids();
msr_files = malloc(cores * sizeof (gint));
for (i = 0; i < cores; i++) {
msr_files[i] = open_msr(cpu_dev_ids[i].cpuid);
}
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));
core_fid = malloc(cores * sizeof (gfloat));
core_power_min = malloc(cores * sizeof (gfloat));
core_power_max = malloc(cores * sizeof (gfloat));
core_fid_min = malloc(cores * sizeof (gfloat));
core_fid_max = malloc(cores * sizeof (gfloat));
msr_update();
memcpy(core_power_min, core_power, cores * sizeof (gfloat));
memcpy(core_power_max, core_power, cores * sizeof (gfloat));
memcpy(core_fid_min, core_fid, cores * sizeof (gfloat));
memcpy(core_fid_max, core_fid, cores * sizeof (gfloat));
package_power_min = package_power;
package_power_max = package_power;
return TRUE;
}
void msr_update() {
guint 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);
}
if (package_eng_a >= package_eng_b) {
package_power = (package_eng_a - package_eng_b) * energy_unit / MESUREMENT_TIME;
if (package_power < package_power_min)
package_power_min = package_power;
if (package_power > package_power_max)
package_power_max = package_power;
}
for (i = 0; i < cores; i++) {
if (core_eng_a[i] >= core_eng_b[i]) {
core_power[i] = (core_eng_a[i] - core_eng_b[i]) * energy_unit / MESUREMENT_TIME;
if (core_power[i] < core_power_min[i])
core_power_min[i] = core_power[i];
if (core_power[i] > core_power_max[i])
core_power_max[i] = core_power[i];
}
core_fid[i] = get_core_fid(i);
if (core_fid[i] < core_fid_min[i])
core_fid_min[i] = core_fid[i];
if (core_fid[i] > core_fid_max[i])
core_fid_max[i] = core_fid[i];
}
}
void msr_clear_minmax() {
guint i;
package_power_min = package_power;
package_power_max = package_power;
for (i = 0; i < cores; i++) {
core_power_min[i] = core_power[i];
core_power_max[i] = core_power[i];
core_fid_min[i] = core_fid[i];
core_fid_max[i] = core_fid[i];
}
}
GSList* msr_get_sensors() {
GSList *list = NULL;
SensorInit *data;
guint i;
data = sensor_init_new();
data->label = g_strdup("Package Power");
data->hint = g_strdup("Package Power reported by RAPL\nSource: cpu0 MSR");
data->value = &package_power;
data->min = &package_power_min;
data->max = &package_power_max;
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 Effective Frequency", display_coreid ? cpu_dev_ids[i].coreid: i);
data->hint = g_strdup_printf("Source: cpu%d MSR", cpu_dev_ids[i].cpuid);
data->value = &(core_fid[i]);
data->min = &(core_fid_min[i]);
data->max = &(core_fid_max[i]);
data->printf_format = MSR_FID_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", display_coreid ? cpu_dev_ids[i].coreid: i);
data->hint = g_strdup_printf("Core Power reported by RAPL\nSource: cpu%d MSR", cpu_dev_ids[i].cpuid);
data->value = &(core_power[i]);
data->min = &(core_power_min[i]);
data->max = &(core_power_max[i]);
data->printf_format = MSR_PWR_PRINTF_FORMAT;
list = g_slist_append(list, data);
}
return list;
}
================================================
FILE: src/ss/os.c
================================================
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include "zenmonitor.h"
#include "sysfs.h"
#include "os.h"
#define OS_FREQ_PRINTF_FORMAT " %8.3f GHz"
static gchar **frq_files = NULL;
static guint cores;
static struct cpudev *cpu_dev_ids;
gfloat *core_freq;
gfloat *core_freq_min;
gfloat *core_freq_max;
static gdouble get_frequency(guint corei) {
gchar *data;
gdouble freq;
if (!g_file_get_contents(frq_files[corei], &data, NULL, NULL))
return 0.0;
freq = atoi(data) / 1000000.0;
g_free(data);
return freq;
}
gboolean os_init(void) {
guint i;
if (!check_zen())
return FALSE;
cores = get_core_count();
if (cores == 0)
return FALSE;
cpu_dev_ids = get_cpu_dev_ids();
frq_files = malloc(cores * sizeof (gchar*));
for (i = 0; i < cores; i++) {
frq_files[i] = g_strdup_printf(
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq",
cpu_dev_ids[i].cpuid);
}
core_freq = malloc(cores * sizeof (gfloat));
core_freq_min = malloc(cores * sizeof (gfloat));
core_freq_max = malloc(cores * sizeof (gfloat));
os_update();
memcpy(core_freq_min, core_freq, cores * sizeof (gfloat));
memcpy(core_freq_max, core_freq, cores * sizeof (gfloat));
return TRUE;
}
void os_update(void) {
guint i;
for (i = 0; i < cores; i++) {
core_freq[i] = get_frequency(i);
if (core_freq[i] < core_freq_min[i])
core_freq_min[i] = core_freq[i];
if (core_freq[i] > core_freq_max[i])
core_freq_max[i] = core_freq[i];
}
}
void os_clear_minmax(void) {
guint i;
for (i = 0; i < cores; i++) {
core_freq_min[i] = core_freq[i];
core_freq_max[i] = core_freq[i];
}
}
GSList* os_get_sensors(void) {
GSList *list = NULL;
SensorInit *data;
guint i;
for (i = 0; i < cores; i++) {
data = sensor_init_new();
data->label = g_strdup_printf("Core %d Frequency", display_coreid ? cpu_dev_ids[i].coreid: i);
data->hint = g_strdup_printf("Current frequency of the CPU as determined by the governor and cpufreq core.\n Source: %s", frq_files[i]);
data->value = &(core_freq[i]);
data->min = &(core_freq_min[i]);
data->max = &(core_freq_max[i]);
data->printf_format = OS_FREQ_PRINTF_FORMAT;
list = g_slist_append(list, data);
}
return list;
}
================================================
FILE: src/ss/zenpower.c
================================================
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include "zenmonitor.h"
#include "zenpower.h"
GSList *zp_sensors = NULL;
static int nodes = 0;
typedef struct
{
const gchar *label;
const gchar *hint;
const gchar *file;
const gchar *printf_format;
const double adjust_ratio;
} HwmonSensorType;
typedef struct
{
float current_value;
float min;
float max;
HwmonSensorType *type;
gchar *hwmon_dir;
int node;
} HwmonSensor;
static HwmonSensorType hwmon_stype[] = {
{"CPU Temperature (tCtl)", "Reported CPU Temperature", "temp1_input", " %6.2f°C", 1000.0},
{"CPU Temperature (tDie)", "Reported CPU Temperature - offset", "temp2_input", " %6.2f°C", 1000.0},
{"CCD1 Temperature", "Core Complex Die 1 Temperature", "temp3_input", " %6.2f°C", 1000.0},
{"CCD2 Temperature", "Core Complex Die 2 Temperature", "temp4_input", " %6.2f°C", 1000.0},
{"CCD3 Temperature", "Core Complex Die 3 Temperature", "temp5_input", " %6.2f°C", 1000.0},
{"CCD4 Temperature", "Core Complex Die 4 Temperature", "temp6_input", " %6.2f°C", 1000.0},
{"CCD5 Temperature", "Core Complex Die 5 Temperature", "temp7_input", " %6.2f°C", 1000.0},
{"CCD6 Temperature", "Core Complex Die 6 Temperature", "temp8_input", " %6.2f°C", 1000.0},
{"CCD7 Temperature", "Core Complex Die 7 Temperature", "temp9_input", " %6.2f°C", 1000.0},
{"CCD8 Temperature", "Core Complex Die 8 Temperature", "temp10_input", " %6.2f°C", 1000.0},
{"CPU Core Voltage (SVI2)", "Core Voltage reported by SVI2 telemetry", "in1_input", " %8.3f V", 1000.0},
{"SOC Voltage (SVI2)", "SOC Voltage reported by SVI2 telemetry", "in2_input", " %8.3f V", 1000.0},
{"CPU Core Current (SVI2)", "Core Current reported by SVI2 telemetry\n"
"Note: May not be accurate on some systems", "curr1_input", " %8.3f A", 1000.0},
{"SOC Current (SVI2)", "SOC Current reported by SVI2 telemetry\n"
"Note: May not be accurate on some systems", "curr2_input", " %8.3f A", 1000.0},
{"CPU Core Power (SVI2)", "Core Voltage * Current\n"
"Note: May not be accurate on some systems", "power1_input", " %8.3f W", 1000000.0},
{"SOC Power (SVI2)", "Core Voltage * Current\n"
"Note: May not be accurate on some systems", "power2_input", " %8.3f W", 1000000.0},
{0, NULL}
};
static gboolean hwmon_file_exists(const gchar *dir, const gchar *file) {
gchar *full_path;
gboolean result;
full_path = g_strdup_printf("/sys/class/hwmon/%s/%s", dir, file);
result = g_file_test(full_path, G_FILE_TEST_EXISTS);
g_free(full_path);
return result;
}
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;
}
static HwmonSensor *hwmon_sensor_new(HwmonSensorType *type, const gchar *dir, gint node) {
HwmonSensor *s;
s = g_new0(HwmonSensor, 1);
s->min = 999.0;
s->type = type;
s->hwmon_dir = g_strdup(dir);
s->node = node;
return s;
}
gboolean zenpower_init() {
GDir *hwmon;
const gchar *entry;
gchar *name = NULL;
HwmonSensorType *type;
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(g_strchomp(name), "zenpower") == 0) {
for (type = hwmon_stype; type->label; type++) {
if (hwmon_file_exists(entry, type->file)) {
zp_sensors = g_slist_append(zp_sensors, hwmon_sensor_new(type, entry, nodes));
}
}
nodes++;
}
g_free(name);
}
if (zp_sensors == NULL)
return FALSE;
return TRUE;
}
void zenpower_update() {
gchar *tmp = NULL;
GSList *node;
HwmonSensor *sensor;
node = zp_sensors;
while(node) {
sensor = (HwmonSensor *)node->data;
if (read_raw_hwmon_value(sensor->hwmon_dir, sensor->type->file, &tmp)){
sensor->current_value = atof(tmp) / sensor->type->adjust_ratio;
if (sensor->current_value < sensor->min)
sensor->min = sensor->current_value;
if (sensor->current_value > sensor->max)
sensor->max = sensor->current_value;
g_free(tmp);
}
else{
sensor->current_value = ERROR_VALUE;
}
node = node->next;
}
}
void zenpower_clear_minmax() {
HwmonSensor *sensor;
GSList *node;
node = zp_sensors;
while(node) {
sensor = (HwmonSensor *)node->data;
sensor->min = sensor->current_value;
sensor->max = sensor->current_value;
node = node->next;
}
}
GSList* zenpower_get_sensors() {
GSList *list = NULL;
HwmonSensor *sensor;
GSList *node;
SensorInit *data;
node = zp_sensors;
while(node) {
sensor = (HwmonSensor *)node->data;
data = sensor_init_new();
if (nodes > 1){
data->label = g_strdup_printf("Node %d - %s", sensor->node, sensor->type->label);
}
else{
data->label = g_strdup(sensor->type->label);
}
data->hint = g_strdup_printf("%s\nSource: zenpower %s/%s", sensor->type->hint, sensor->hwmon_dir, sensor->type->file);
data->value = &sensor->current_value;
data->min = &sensor->min;
data->max = &sensor->max;
data->printf_format = sensor->type->printf_format;
list = g_slist_append(list, data);
node = node->next;
}
return list;
}
================================================
FILE: src/sysfs.c
================================================
#include <glib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sysfs.h"
#include "zenmonitor.h"
#define CPUD_MAX 512
struct bitset {
guint bits[CPUD_MAX/32];
};
static int bitset_set(struct bitset *set, int id) {
if (id < CPUD_MAX) {
int v = (set->bits[id/32] >> (id & 31)) & 1;
set->bits[id/32] |= 1 << (id & 31);
return v;
}
return 1;
}
static int cmp_cpudev(const void *ap, const void *bp) {
return ((struct cpudev *)ap)->cpuid - ((struct cpudev *)bp)->cpuid;
}
struct cpudev* get_cpu_dev_ids(void) {
struct cpudev *cpu_dev_ids;
gshort coreid, cpuid, siblingid;
GDir *dir;
const gchar *entry;
gchar *filename, *buffer;
gchar **cpusiblings;
gchar **ptr;
guint cores;
gboolean found;
struct bitset seen = { 0 };
int i;
cores = get_core_count();
cpu_dev_ids = malloc(cores * sizeof (*cpu_dev_ids));
for (i=0;i<cores;i++)
cpu_dev_ids[i] = (struct cpudev) { -1, -1 };
dir = g_dir_open(SYSFS_DIR_CPUS, 0, NULL);
if (dir) {
int i = 0;
while ((entry = g_dir_read_name(dir))) {
if (sscanf(entry, "cpu%hd", &cpuid) != 1) {
continue;
}
found = FALSE;
filename = g_build_filename(SYSFS_DIR_CPUS, entry, "topology", "core_id", NULL);
if (g_file_get_contents(filename, &buffer, NULL, NULL)) {
coreid = (gshort) atoi(buffer);
g_free(filename);
g_free(buffer);
filename = g_build_filename(SYSFS_DIR_CPUS, entry, "topology", "thread_siblings_list", NULL);
if (g_file_get_contents(filename, &buffer, NULL, NULL)) {
cpusiblings = g_strsplit(buffer, ",", -1);
found = TRUE;
// check whether cpu device is not for SMT thread sibling
for (ptr = cpusiblings; *ptr; ptr++) {
siblingid = (gshort) atoi(*ptr);
// let's save the cpu device with lower number
if (siblingid < cpuid)
cpuid = siblingid;
if (bitset_set(&seen, siblingid)) {
found = FALSE;
}
}
g_strfreev(cpusiblings);
}
}
if (found && i < cores) {
cpu_dev_ids[i++] = (struct cpudev) { coreid, cpuid };
}
g_free(filename);
g_free(buffer);
}
}
qsort(cpu_dev_ids, cores, sizeof(*cpu_dev_ids), cmp_cpudev);
return cpu_dev_ids;
}
================================================
FILE: src/zenmonitor.c
================================================
#include <gtk/gtk.h>
#include <cpuid.h>
#include <string.h>
#include <stdlib.h>
#include "zenmonitor.h"
#include "zenpower.h"
#include "msr.h"
#include "os.h"
#include "gui.h"
#define AMD_STRING "AuthenticAMD"
#define ZEN_FAMILY 0x17
// AMD PPR = https://www.amd.com/system/files/TechDocs/54945_PPR_Family_17h_Models_00h-0Fh.pdf
gboolean check_zen() {
guint32 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;
}
gchar *cpu_model() {
guint32 eax = 0, ebx = 0, ecx = 0, edx = 0;
char model[48];
// AMD PPR: page 65-68 - CPUID_Fn80000002_EAX-CPUID_Fn80000004_EDX
__get_cpuid(0x80000002, &eax, &ebx, &ecx, &edx);
memcpy(model, &eax, 4);
memcpy(model+4, &ebx, 4);
memcpy(model+8, &ecx, 4);
memcpy(model+12, &edx, 4);
__get_cpuid(0x80000003, &eax, &ebx, &ecx, &edx);
memcpy(model+16, &eax, 4);
memcpy(model+20, &ebx, 4);
memcpy(model+24, &ecx, 4);
memcpy(model+28, &edx, 4);
__get_cpuid(0x80000004, &eax, &ebx, &ecx, &edx);
memcpy(model+32, &eax, 4);
memcpy(model+36, &ebx, 4);
memcpy(model+40, &ecx, 4);
memcpy(model+44, &edx, 4);
model[48] = 0;
return g_strdup(g_strchomp(model));
}
guint get_core_count() {
guint eax = 0, ebx = 0, ecx = 0, edx = 0;
guint logical_cpus, threads_per_code;
// 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 SensorSource sensor_sources[] = {
{
"zenpower",
zenpower_init, zenpower_get_sensors, zenpower_update, zenpower_clear_minmax,
FALSE, NULL
},
{
"msr",
msr_init, msr_get_sensors, msr_update, msr_clear_minmax,
FALSE, NULL
},
{
"os",
os_init, os_get_sensors, os_update, os_clear_minmax,
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->hint);
g_free(s);
}
}
gboolean display_coreid = 0;
static GOptionEntry options[] =
{
{ "coreid", 'c', 0, G_OPTION_ARG_NONE, &display_coreid, "Display core_id instead of core index", NULL },
{ NULL }
};
int main (int argc, char *argv[])
{
GError *error = NULL;
GOptionContext *context;
context = g_option_context_new ("- Zenmonitor display options");
g_option_context_add_main_entries(context, options, NULL);
g_option_context_add_group(context, gtk_get_option_group (TRUE));
if (!g_option_context_parse(context, &argc, &argv, &error)) {
g_print ("option parsing failed: %s\n", error->message);
exit (1);
}
start_gui(sensor_sources);
}
gitextract_ikdbt1cl/
├── LICENSE
├── README.md
├── data/
│ ├── org.pkexec.zenmonitor.policy.in
│ ├── zenmonitor-root.desktop.in
│ └── zenmonitor.desktop.in
├── makefile
└── src/
├── gui.c
├── include/
│ ├── gui.h
│ ├── msr.h
│ ├── os.h
│ ├── sysfs.h
│ ├── zenmonitor.h
│ └── zenpower.h
├── ss/
│ ├── msr.c
│ ├── os.c
│ └── zenpower.c
├── sysfs.c
└── zenmonitor.c
SYMBOL INDEX (54 symbols across 8 files)
FILE: src/gui.c
function init_sensors (line 22) | static void init_sensors() {
function GtkTreeModel (line 56) | static GtkTreeModel* create_model (void) {
function set_list_column_value (line 62) | static void set_list_column_value(float num, const gchar *printf_format,...
function gboolean (line 72) | static gboolean update_data (gpointer data) {
function add_columns (line 107) | static void add_columns (GtkTreeView *treeview) {
function about_btn_clicked (line 144) | static void about_btn_clicked(GtkButton *button, gpointer user_data) {
function clear_btn_clicked (line 161) | static void clear_btn_clicked(GtkButton *button, gpointer user_data) {
function gboolean (line 172) | static gboolean mid_search_eq_func(GtkTreeModel *model, gint column, con...
function resize_to_treeview (line 189) | static void resize_to_treeview(GtkWindow* window, GtkTreeView* treeview) {
function start_gui (line 207) | int start_gui (SensorSource *ss) {
FILE: src/include/sysfs.h
type cpudev (line 3) | struct cpudev {
type cpudev (line 8) | struct cpudev
FILE: src/include/zenmonitor.h
type SensorInit (line 4) | typedef struct
type SensorSource (line 15) | typedef struct {
FILE: src/ss/msr.c
type cpudev (line 22) | struct cpudev
function gint (line 42) | static gint open_msr(gshort devid) {
function gboolean (line 48) | static gboolean read_msr(gint file, guint index, gulong *data) {
function gdouble (line 55) | gdouble get_energy_unit() {
function gulong (line 64) | gulong get_package_energy() {
function gulong (line 73) | gulong get_core_energy(gint core) {
function gdouble (line 82) | gdouble get_core_fid(gint core) {
function gboolean (line 102) | gboolean msr_init() {
function msr_update (line 142) | void msr_update() {
function msr_clear_minmax (line 185) | void msr_clear_minmax() {
function GSList (line 198) | GSList* msr_get_sensors() {
FILE: src/ss/os.c
type cpudev (line 12) | struct cpudev
function gdouble (line 18) | static gdouble get_frequency(guint corei) {
function gboolean (line 31) | gboolean os_init(void) {
function os_update (line 60) | void os_update(void) {
function os_clear_minmax (line 72) | void os_clear_minmax(void) {
function GSList (line 81) | GSList* os_get_sensors(void) {
FILE: src/ss/zenpower.c
type HwmonSensorType (line 10) | typedef struct
type HwmonSensor (line 19) | typedef struct
function gboolean (line 53) | static gboolean hwmon_file_exists(const gchar *dir, const gchar *file) {
function gboolean (line 64) | static gboolean read_raw_hwmon_value(const gchar *dir, const gchar *file...
function HwmonSensor (line 75) | static HwmonSensor *hwmon_sensor_new(HwmonSensorType *type, const gchar ...
function gboolean (line 85) | gboolean zenpower_init() {
function zenpower_update (line 117) | void zenpower_update() {
function zenpower_clear_minmax (line 144) | void zenpower_clear_minmax() {
function GSList (line 156) | GSList* zenpower_get_sensors() {
FILE: src/sysfs.c
type bitset (line 9) | struct bitset {
function bitset_set (line 13) | static int bitset_set(struct bitset *set, int id) {
function cmp_cpudev (line 23) | static int cmp_cpudev(const void *ap, const void *bp) {
type cpudev (line 27) | struct cpudev
type cpudev (line 28) | struct cpudev
type bitset (line 37) | struct bitset
type cpudev (line 43) | struct cpudev
type cpudev (line 85) | struct cpudev
FILE: src/zenmonitor.c
function gboolean (line 16) | gboolean check_zen() {
function gchar (line 41) | gchar *cpu_model() {
function guint (line 68) | guint get_core_count() {
function SensorInit (line 107) | SensorInit *sensor_init_new() {
function sensor_init_free (line 111) | void sensor_init_free(SensorInit *s) {
function main (line 127) | int main (int argc, char *argv[])
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (41K chars).
[
{
"path": "LICENSE",
"chars": 1075,
"preview": "MIT License\n\nCopyright (c) 2018-2020 Ondrej Čerman\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "README.md",
"chars": 2173,
"preview": "# Zen monitor\nZen monitor is monitoring software for AMD Zen-based CPUs.\n\nIt can monitor these values:\n - CPU Temperatur"
},
{
"path": "data/org.pkexec.zenmonitor.policy.in",
"chars": 713,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE policyconfig PUBLIC \"-//freedesktop//DTD PolicyKit Policy Configuration"
},
{
"path": "data/zenmonitor-root.desktop.in",
"chars": 234,
"preview": "[Desktop Entry]\nName=Zenmonitor (root)\nComment=Monitoring software for AMD Zen-based CPUs\nExec=pkexec @APP_EXEC@\nType=Ap"
},
{
"path": "data/zenmonitor.desktop.in",
"chars": 220,
"preview": "[Desktop Entry]\nName=Zenmonitor\nComment=Monitoring software for AMD Zen-based CPUs\nExec=@APP_EXEC@\nType=Application\nCate"
},
{
"path": "makefile",
"chars": 1132,
"preview": "ifeq ($(PREFIX),)\n\tPREFIX := /usr/local\nendif\n\nbuild:\n\tcc -Isrc/include `pkg-config --cflags gtk+-3.0` src/*.c src/ss/*."
},
{
"path": "src/gui.c",
"chars": 10322,
"preview": "#include <cpuid.h>\n#include <gtk/gtk.h>\n#include \"gui.h\"\n#include \"zenmonitor.h\"\n\nGtkWidget *window;\n\nstatic GtkTreeMode"
},
{
"path": "src/include/gui.h",
"chars": 17,
"preview": "int start_gui();\n"
},
{
"path": "src/include/msr.h",
"chars": 92,
"preview": "gboolean msr_init();\nvoid msr_update();\nvoid msr_clear_minmax();\nGSList* msr_get_sensors();\n"
},
{
"path": "src/include/os.h",
"chars": 104,
"preview": "gboolean os_init(void);\nvoid os_update(void);\nvoid os_clear_minmax(void);\nGSList* os_get_sensors(void);\n"
},
{
"path": "src/include/sysfs.h",
"chars": 140,
"preview": "#define SYSFS_DIR_CPUS \"/sys/devices/system/cpu\"\n\nstruct cpudev {\n\tgshort coreid;\n\tgshort cpuid;\n};\n\nstruct cpudev * get"
},
{
"path": "src/include/zenmonitor.h",
"chars": 596,
"preview": "#define ERROR_VALUE -999.0\n#define VERSION \"1.4.2\"\n\ntypedef struct\n{\n gchar *label;\n gchar *hint;\n float *value"
},
{
"path": "src/include/zenpower.h",
"chars": 112,
"preview": "gboolean zenpower_init();\nGSList* zenpower_get_sensors();\nvoid zenpower_update();\nvoid zenpower_clear_minmax();\n"
},
{
"path": "src/ss/msr.c",
"chars": 6720,
"preview": "#include <glib.h>\n#include <cpuid.h>\n#include <stdio.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <math.h>\n#includ"
},
{
"path": "src/ss/os.c",
"chars": 2452,
"preview": "#include <glib.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"zenmonitor.h\"\n#include \"sysfs.h\"\n#include \"os.h\"\n\n#d"
},
{
"path": "src/ss/zenpower.c",
"chars": 6089,
"preview": "#include <glib.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"zenmonitor.h\"\n#include \"zenpower.h\"\n\nGSList *zp_sens"
},
{
"path": "src/sysfs.c",
"chars": 2702,
"preview": "#include <glib.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"sysfs.h\"\n#include \"zenmonitor.h\"\n"
},
{
"path": "src/zenmonitor.c",
"chars": 3409,
"preview": "#include <gtk/gtk.h>\n#include <cpuid.h>\n#include <string.h>\n#include <stdlib.h>\n#include \"zenmonitor.h\"\n#include \"zenpow"
}
]
About this extraction
This page contains the full source code of the ocerman/zenmonitor GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (37.4 KB), approximately 10.7k tokens, and a symbol index with 54 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.