mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
0b4d9a7f26
Cleaning some warnings. Added checks for kstat, so cpu applet can show usage via kstat api. Added tests for kstat in configure script.
520 lines
12 KiB
C++
520 lines
12 KiB
C++
/*
|
|
* IceWM
|
|
*
|
|
* Copyright (C) 1998-2001 Marko Macek
|
|
*
|
|
* CPU Status
|
|
*
|
|
* For eWorkPanel by Mikko Lahteenmaki 2003
|
|
* For ede-panel by Sanel Zukan 2009
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <FL/Fl.H>
|
|
#include <FL/Fl_Group.H>
|
|
#include <FL/fl_draw.H>
|
|
#include <edelib/Nls.h>
|
|
|
|
#if defined(linux)
|
|
# include <sys/sysinfo.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_KSTAT
|
|
# include <kstat.h>
|
|
# include <sys/sysinfo.h>
|
|
# include <string.h> /* strncmp */
|
|
#endif
|
|
|
|
#ifdef __FreeBSD__
|
|
# include <sys/param.h>
|
|
# include <sys/sysctl.h>
|
|
# if __FreeBSD_version < 500101
|
|
# include <sys/dkstat.h>
|
|
# else
|
|
# include <sys/resource.h>
|
|
# endif
|
|
# include <sys/stat.h>
|
|
#endif
|
|
|
|
#include "CpuMonitor.h"
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
/* The part ripped from top... */
|
|
/*
|
|
* Top users/processes display for Unix
|
|
* Version 3
|
|
*
|
|
* This program may be freely redistributed,
|
|
* but this entire comment MUST remain intact.
|
|
*
|
|
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
|
|
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
|
|
*/
|
|
|
|
/*
|
|
* percentages(cnt, out, new, old, diffs) - calculate percentage change
|
|
* between array "old" and "new", putting the percentages i "out".
|
|
* "cnt" is size of each array and "diffs" is used for scratch space.
|
|
* The array "old" is updated on each call.
|
|
* The routine assumes modulo arithmetic. This function is especially
|
|
* useful on BSD mchines for calculating cpu state percentages.
|
|
*/
|
|
|
|
static long cp_time[CPUSTATES];
|
|
static long cp_old[CPUSTATES];
|
|
static long cp_diff[CPUSTATES];
|
|
static int cpu_states[CPUSTATES];
|
|
|
|
long percentages(int cnt, int *out, long *my_new, long *old, long *diffs)
|
|
{
|
|
register int i;
|
|
register long change;
|
|
register long total_change;
|
|
register long *dp;
|
|
long half_total;
|
|
|
|
/* initialization */
|
|
total_change = 0;
|
|
dp = diffs;
|
|
|
|
/* calculate changes for each state and the overall change */
|
|
for (i = 0; i < cnt; i++)
|
|
{
|
|
if ((change = *my_new - *old) < 0)
|
|
{
|
|
/* this only happens when the counter wraps */
|
|
change = (int)
|
|
((unsigned long)*my_new-(unsigned long)*old);
|
|
}
|
|
total_change += (*dp++ = change);
|
|
*old++ = *my_new++;
|
|
}
|
|
|
|
/* avoid divide by zero potential */
|
|
if (total_change == 0)
|
|
{
|
|
total_change = 1;
|
|
}
|
|
|
|
/* calculate percentages based on overall change, rounding up */
|
|
half_total = total_change / 2l;
|
|
|
|
/* Do not divide by 0. Causes Floating point exception */
|
|
if(total_change) {
|
|
for (i = 0; i < cnt; i++)
|
|
{
|
|
*out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
|
|
}
|
|
}
|
|
|
|
/* return the total in case the caller wants to use it */
|
|
return total_change;
|
|
}
|
|
|
|
#endif /* freebsd */
|
|
|
|
|
|
#define UPDATE_INTERVAL .5f
|
|
|
|
void cpu_timeout_cb(void *d) {
|
|
((CPUMonitor*)d)->update_status();
|
|
Fl::repeat_timeout(UPDATE_INTERVAL, cpu_timeout_cb, d);
|
|
}
|
|
|
|
CPUMonitor::CPUMonitor() : Fl_Box(0, 0, 45, 25)
|
|
{
|
|
box(FL_THIN_DOWN_BOX);
|
|
|
|
m_draw_label = true;
|
|
m_samples = m_old_samples = -1;
|
|
cpu = 0;
|
|
|
|
colors[IWM_USER] = FL_RED;
|
|
colors[IWM_NICE] = FL_GREEN;
|
|
colors[IWM_SYS] = FL_DARK3;
|
|
colors[IWM_IDLE] = FL_BACKGROUND_COLOR;
|
|
|
|
layout();
|
|
}
|
|
|
|
void CPUMonitor::clear()
|
|
{
|
|
if(!cpu) return;
|
|
|
|
for (int i = 0; i < samples(); i++)
|
|
delete [] cpu[i];
|
|
|
|
delete [] cpu;
|
|
cpu = 0;
|
|
|
|
m_old_samples = -1;
|
|
}
|
|
|
|
void CPUMonitor::draw()
|
|
{
|
|
draw_box();
|
|
|
|
if(!cpu && label()) {
|
|
draw_label();
|
|
return;
|
|
}
|
|
|
|
int n, c, user, nice, sys, idle, total;
|
|
int W = w() - Fl::box_dw(box());
|
|
int H = h() - Fl::box_dh(box());
|
|
int X = x() + Fl::box_dx(box());
|
|
int Y = y() + Fl::box_dy(box());
|
|
|
|
fl_push_clip(X, Y, W, H);
|
|
|
|
c = 0;
|
|
for (int i = X; i < samples() + X; i++) {
|
|
user = cpu[c][IWM_USER];
|
|
nice = cpu[c][IWM_NICE];
|
|
sys = cpu[c][IWM_SYS];
|
|
idle = cpu[c][IWM_IDLE];
|
|
|
|
total = user + sys + nice + idle;
|
|
c++;
|
|
|
|
int y = Y + H;
|
|
|
|
if (total > 0) {
|
|
if (sys) {
|
|
n = (H * (total - sys)) / total; // check rounding
|
|
if (n >= y) n = y;
|
|
if (n < 1) n = 1;
|
|
fl_color(colors[IWM_SYS]);
|
|
fl_line(i, y, i, n);
|
|
y = n - 1;
|
|
}
|
|
|
|
if (nice) {
|
|
n = (H * (total - sys - nice)) / total;
|
|
if (n >= y) n = y;
|
|
if (n < 1) n = 1;
|
|
fl_color(colors[IWM_NICE]);
|
|
fl_line(i, y, i, n);
|
|
y = n - 1;
|
|
}
|
|
|
|
if (user) {
|
|
n = (H * (total - sys - nice - user)) / total;
|
|
if (n >= y) n = y;
|
|
if (n < 1) n = 1;
|
|
fl_color(colors[IWM_USER]);
|
|
fl_line(i, y, i, n);
|
|
y = n - 1;
|
|
}
|
|
}
|
|
|
|
if (idle) {
|
|
if(colors[IWM_IDLE] != FL_BACKGROUND_COLOR) {
|
|
fl_color(colors[IWM_IDLE]);
|
|
fl_line(i, Fl::box_dy(box()), i, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
draw_label();
|
|
fl_pop_clip();
|
|
}
|
|
|
|
void CPUMonitor::layout() {
|
|
m_samples = w() - Fl::box_dw(box());
|
|
|
|
if(!cpu || m_old_samples != m_samples) {
|
|
clear();
|
|
|
|
cpu = new int*[m_samples];
|
|
for(int i=0; i < m_samples; i++) {
|
|
cpu[i] = new int[IWM_STATES];
|
|
cpu[i][IWM_USER] = cpu[i][IWM_NICE] = cpu[i][IWM_SYS] = 0;
|
|
cpu[i][IWM_IDLE] = 1;
|
|
}
|
|
last_cpu[IWM_USER] = last_cpu[IWM_NICE] = last_cpu[IWM_SYS] = last_cpu[IWM_IDLE] = 0;
|
|
|
|
update_status();
|
|
m_old_samples = m_samples;
|
|
}
|
|
}
|
|
|
|
int CPUMonitor::handle(int e) {
|
|
switch(e) {
|
|
case FL_SHOW: {
|
|
int ret = Fl_Box::handle(e);
|
|
Fl::add_timeout(UPDATE_INTERVAL, cpu_timeout_cb, this);
|
|
return ret;
|
|
}
|
|
|
|
case FL_HIDE:
|
|
Fl::remove_timeout(cpu_timeout_cb);
|
|
/* fallthrough */
|
|
}
|
|
|
|
return Fl_Box::handle(e);
|
|
}
|
|
|
|
void CPUMonitor::update_status() {
|
|
if(!cpu) return;
|
|
|
|
for (int i=1; i < samples(); i++) {
|
|
cpu[i - 1][IWM_USER] = cpu[i][IWM_USER];
|
|
cpu[i - 1][IWM_NICE] = cpu[i][IWM_NICE];
|
|
cpu[i - 1][IWM_SYS] = cpu[i][IWM_SYS];
|
|
cpu[i - 1][IWM_IDLE] = cpu[i][IWM_IDLE];
|
|
}
|
|
|
|
get_cpu_info();
|
|
|
|
// Update tooltip
|
|
static char load[255];
|
|
snprintf(load, sizeof(load)-1,
|
|
_("CPU Load\n"
|
|
"User: %d%%\n"
|
|
"Nice: %d%%\n"
|
|
"Sys: %d%%\n"
|
|
"Idle: %d%%"),
|
|
cpu[samples()-1][0]*2, cpu[samples()-1][1]*2,
|
|
cpu[samples()-1][2]*2, cpu[samples()-1][3]*2);
|
|
|
|
// Update label
|
|
int cpu_percent = cpu[samples()-1][0]*2;
|
|
if(m_draw_label && cpu_percent<=100) {
|
|
static char buf[16];
|
|
snprintf(buf, sizeof(buf), "%i%%", cpu_percent);
|
|
label(buf);
|
|
}
|
|
|
|
tooltip(load);
|
|
}
|
|
|
|
void CPUMonitor::get_cpu_info() {
|
|
if(!cpu) return;
|
|
|
|
#ifdef linux
|
|
char *p, buf[128];
|
|
long cur[IWM_STATES];
|
|
int len, fd = open("/proc/stat", O_RDONLY);
|
|
|
|
cpu[samples()-1][IWM_USER] = 0;
|
|
cpu[samples()-1][IWM_NICE] = 0;
|
|
cpu[samples()-1][IWM_SYS] = 0;
|
|
cpu[samples()-1][IWM_IDLE] = 0;
|
|
|
|
if (fd == -1)
|
|
return;
|
|
len = read(fd, buf, sizeof(buf) - 1);
|
|
if (len != sizeof(buf) - 1) {
|
|
close(fd);
|
|
return;
|
|
}
|
|
buf[len] = 0;
|
|
|
|
p = buf;
|
|
while (*p && (*p < '0' || *p > '9'))
|
|
p++;
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
cur[i] = strtoul(p, &p, 10);
|
|
cpu[samples()-1][i] = cur[i] - last_cpu[i];
|
|
last_cpu[i] = cur[i];
|
|
}
|
|
close(fd);
|
|
#if 0
|
|
fprintf(stderr, "cpu: %d %d %d %d\n",
|
|
cpu[samples()-1][IWM_USER], cpu[samples()-1][IWM_NICE],
|
|
cpu[samples()-1][IWM_SYS], cpu[samples()-1][IWM_IDLE]);
|
|
#endif
|
|
|
|
#endif /* linux */
|
|
|
|
#ifdef HAVE_KSTAT
|
|
# ifdef HAVE_OLD_KSTAT
|
|
# define ui32 ul
|
|
#endif
|
|
static kstat_ctl_t *kc = NULL;
|
|
static kid_t kcid;
|
|
kid_t new_kcid;
|
|
kstat_t *ks = NULL;
|
|
kstat_named_t *kn = NULL;
|
|
int changed,change,total_change;
|
|
unsigned int thiscpu;
|
|
register int i,j;
|
|
static unsigned int ncpus;
|
|
static kstat_t **cpu_ks=NULL;
|
|
static cpu_stat_t *cpu_stat=NULL;
|
|
static long cp_old[CPU_STATES];
|
|
long cp_time[CPU_STATES], cp_pct[CPU_STATES];
|
|
|
|
/* Initialize the kstat */
|
|
if (!kc) {
|
|
kc = kstat_open();
|
|
if (!kc) {
|
|
perror("kstat_open ");
|
|
return;/* FIXME : need err handler? */
|
|
}
|
|
changed = 1;
|
|
kcid = kc->kc_chain_id;
|
|
fcntl(kc->kc_kd, F_SETFD, FD_CLOEXEC);
|
|
} else {
|
|
changed = 0;
|
|
}
|
|
/* Fetch the kstat data. Whenever we detect that the kstat has been
|
|
changed by the kernel, we 'continue' and restart this loop.
|
|
Otherwise, we break out at the end. */
|
|
while (1) {
|
|
new_kcid = kstat_chain_update(kc);
|
|
if (new_kcid) {
|
|
changed = 1;
|
|
kcid = new_kcid;
|
|
}
|
|
if (new_kcid < 0) {
|
|
perror("kstat_chain_update ");
|
|
return;/* FIXME : need err handler? */
|
|
}
|
|
if (new_kcid != 0)
|
|
continue; /* kstat changed - start over */
|
|
ks = kstat_lookup(kc, "unix", 0, "system_misc");
|
|
if (kstat_read(kc, ks, 0) == -1) {
|
|
perror("kstat_read ");
|
|
return;/* FIXME : need err handler? */
|
|
}
|
|
if (changed) {
|
|
/* the kstat has changed - reread the data */
|
|
thiscpu = 0; ncpus = 0;
|
|
kn = (kstat_named_t *)kstat_data_lookup(ks, "ncpus");
|
|
if ((kn) && (kn->value.ui32 > ncpus)) {
|
|
/* I guess I should be using 'new' here... FIXME */
|
|
ncpus = kn->value.ui32;
|
|
if ((cpu_ks = (kstat_t **)
|
|
realloc(cpu_ks, ncpus * sizeof(kstat_t *))) == NULL)
|
|
{
|
|
perror("realloc: cpu_ks ");
|
|
return;/* FIXME : need err handler? */
|
|
}
|
|
if ((cpu_stat = (cpu_stat_t *)
|
|
realloc(cpu_stat, ncpus * sizeof(cpu_stat_t))) == NULL)
|
|
{
|
|
perror("realloc: cpu_stat ");
|
|
return;/* FIXME : need err handler? */
|
|
}
|
|
}
|
|
for (ks = kc->kc_chain; ks; ks = ks->ks_next) {
|
|
if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) {
|
|
new_kcid = kstat_read(kc, ks, NULL);
|
|
if (new_kcid < 0) {
|
|
perror("kstat_read ");
|
|
return;/* FIXME : need err handler? */
|
|
}
|
|
if (new_kcid != kcid)
|
|
break;
|
|
cpu_ks[thiscpu] = ks;
|
|
thiscpu++;
|
|
if (thiscpu > ncpus) {
|
|
fprintf(stderr, "kstat finds too many cpus: should be %d", ncpus);
|
|
return;/* FIXME : need err handler? */
|
|
}
|
|
}
|
|
}
|
|
if (new_kcid != kcid)
|
|
continue;
|
|
ncpus = thiscpu;
|
|
changed = 0;
|
|
}
|
|
for (i = 0; i<(int)ncpus; i++) {
|
|
new_kcid = kstat_read(kc, cpu_ks[i], &cpu_stat[i]);
|
|
if (new_kcid < 0) {
|
|
perror("kstat_read ");
|
|
return;/* FIXME : need err handler? */
|
|
}
|
|
}
|
|
if (new_kcid != kcid)
|
|
continue; /* kstat changed - start over */
|
|
else
|
|
break;
|
|
} /* while (1) */
|
|
|
|
/* Initialize the cp_time array */
|
|
for (i = 0; i < CPU_STATES; i++)
|
|
cp_time[i] = 0L;
|
|
for (i = 0; i < (int)ncpus; i++) {
|
|
for (j = 0; j < CPU_STATES; j++)
|
|
cp_time[j] += (long) cpu_stat[i].cpu_sysinfo.cpu[j];
|
|
}
|
|
/* calculate the percent utilization for each category */
|
|
/* cpu_state calculations */
|
|
total_change = 0;
|
|
for (i = 0; i < CPU_STATES; i++) {
|
|
change = cp_time[i] - cp_old[i];
|
|
if (change < 0) /* The counter rolled over */
|
|
change = (int) ((unsigned long)cp_time[i] - (unsigned long)cp_old[i]);
|
|
cp_pct[i] = change;
|
|
total_change += change;
|
|
cp_old[i] = cp_time[i]; /* copy the data for the next run */
|
|
}
|
|
/* this percent calculation isn't really needed, since the repaint
|
|
routine takes care of this... */
|
|
for (i = 0; i < CPU_STATES; i++)
|
|
cp_pct[i] =
|
|
((total_change > 0) ?
|
|
((int)(((1000.0 * (float)cp_pct[i]) / total_change) + 0.5)) :
|
|
((i == CPU_IDLE) ? (1000) : (0)));
|
|
|
|
/* OK, we've got the data. Now copy it to cpu[][] */
|
|
cpu[samples()-1][IWM_USER] = cp_pct[CPU_USER];
|
|
cpu[samples()-1][IWM_NICE] = cp_pct[CPU_WAIT];
|
|
cpu[samples()-1][IWM_SYS] = cp_pct[CPU_KERNEL];
|
|
cpu[samples()-1][IWM_IDLE] = cp_pct[CPU_IDLE];
|
|
|
|
#endif /* have_kstat_h */
|
|
|
|
#ifdef __FreeBSD__
|
|
size_t len = sizeof(cp_time);
|
|
|
|
cpu[samples()-1][IWM_USER] = 0;
|
|
cpu[samples()-1][IWM_NICE] = 0;
|
|
cpu[samples()-1][IWM_SYS] = 0;
|
|
cpu[samples()-1][IWM_IDLE] = 0;
|
|
|
|
if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) == -1)
|
|
return; /* FIXME : need err handler? */
|
|
|
|
percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
|
|
|
|
// Translate FreeBSD stuff into ours (probably the same thing anyway)
|
|
cpu[samples()-1][IWM_USER] = cp_diff[CP_USER];
|
|
cpu[samples()-1][IWM_NICE] = cp_diff[CP_NICE];
|
|
cpu[samples()-1][IWM_SYS] = cp_diff[CP_SYS];
|
|
cpu[samples()-1][IWM_IDLE] = cp_diff[CP_IDLE];
|
|
|
|
#if 0
|
|
fprintf(stderr, "cpu: %d %d %d %d\n",
|
|
cpu[samples()-1][IWM_USER], cpu[samples()-1][IWM_NICE],
|
|
cpu[samples()-1][IWM_SYS], cpu[samples()-1][IWM_IDLE]);
|
|
#endif
|
|
|
|
#endif /* freebsd */
|
|
}
|
|
|
|
EDE_PANEL_APPLET_EXPORT (
|
|
CPUMonitor,
|
|
EDE_PANEL_APPLET_OPTION_ALIGN_RIGHT,
|
|
"CPU monitor",
|
|
"0.1",
|
|
"empty",
|
|
"various"
|
|
)
|