Commit c2769985 authored by Martin "eto" Misuth's avatar Martin "eto" Misuth
Browse files

added cpu monitoring for both single core and smp systems, some notes in process monitor

parent bc84920b
......@@ -23,13 +23,20 @@ in the source distribution for its full text.
#include <sys/param.h>
#include <sys/jail.h>
#include <sys/uio.h>
#include <sys/resource.h>
#define JAIL_ERRMSGLEN 1024
char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ {
unsigned long long int totalTime;
unsigned long long int totalPeriod;
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
} CPUData;
typedef struct FreeBSDProcessList_ {
......@@ -38,6 +45,12 @@ typedef struct FreeBSDProcessList_ {
CPUData* cpus;
unsigned long *cp_time_o;
unsigned long *cp_time_n;
unsigned long *cp_times_o;
unsigned long *cp_times_n;
} FreeBSDProcessList;
}*/
......@@ -46,25 +59,17 @@ static int MIB_vm_stats_vm_v_wire_count[4];
static int MIB_vm_stats_vm_v_cache_count[4];
static int MIB_hw_physmem[2];
static int MIB_kern_cp_time[2];
static int MIB_kern_cp_times[2];
static int pageSizeKb;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
FreeBSDProcessList* fpl = calloc(1, sizeof(FreeBSDProcessList));
ProcessList* pl = (ProcessList*) fpl;
ProcessList_init(pl, Class(FreeBSDProcess), usersTable, pidWhiteList, userId);
int cpus = 1;
size_t sizeof_cpus = sizeof(cpus);
int err = sysctlbyname("hw.ncpu", &cpus, &sizeof_cpus, NULL, 0);
if (err) cpus = 1;
pl->cpuCount = MAX(cpus, 1);
fpl->cpus = realloc(fpl->cpus, cpus * sizeof(CPUData));
for (int i = 0; i < cpus; i++) {
fpl->cpus[i].totalTime = 1;
fpl->cpus[i].totalPeriod = 1;
}
size_t len;
len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", MIB_vm_stats_vm_v_wire_count, &len);
len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len);
......@@ -74,6 +79,50 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
fpl->kd = kvm_open(NULL, "/dev/null", NULL, 0, NULL);
assert(fpl->kd);
int smp = 0;
size_t sizeof_smp = sizeof(smp);
if (sysctlbyname("kern.smp.active", &smp, &sizeof_smp, NULL, 0) != 0 || sizeof_smp != sizeof(smp)) {
smp = 0;
}
int cpus = 1;
size_t sizeof_cpus = sizeof(cpus);
if (smp) {
int err = sysctlbyname("kern.smp.cpus", &cpus, &sizeof_cpus, NULL, 0);
if (err) cpus = 1;
} else {
cpus = 1;
}
size_t sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
len = 2; sysctlnametomib("kern.cp_time", MIB_kern_cp_time, &len);
fpl->cp_time_o = calloc(cpus, sizeof_cp_time_array);
fpl->cp_time_n = calloc(cpus, sizeof_cp_time_array);
len = sizeof_cp_time_array;
// fetch intial single (or average) CPU clicks from kernel
sysctl(MIB_kern_cp_time, 2, fpl->cp_time_o, &len, NULL, 0);
// on smp box, fetch rest of intial CPU's clicks
if (cpus > 1) {
len = 2; sysctlnametomib("kern.cp_times", MIB_kern_cp_times, &len);
fpl->cp_times_o = calloc(cpus, sizeof_cp_time_array);
fpl->cp_times_n = calloc(cpus, sizeof_cp_time_array);
len = cpus * sizeof_cp_time_array;
sysctl(MIB_kern_cp_times, 2, fpl->cp_times_o, &len, NULL, 0);
}
pl->cpuCount = MAX(cpus, 1);
if (cpus == 1 ) {
fpl->cpus = realloc(fpl->cpus, sizeof(CPUData));
} else {
// on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well)
fpl->cpus = realloc(fpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
return pl;
}
......@@ -81,10 +130,95 @@ void ProcessList_delete(ProcessList* this) {
const FreeBSDProcessList* fpl = (FreeBSDProcessList*) this;
if (fpl->kd) kvm_close(fpl->kd);
if (fpl->cp_time_o != NULL) free(fpl->cp_time_o);
if (fpl->cp_time_n != NULL) free(fpl->cp_time_n);
if (fpl->cp_times_o != NULL) free(fpl->cp_times_o);
if (fpl->cp_times_n != NULL) free(fpl->cp_times_n);
ProcessList_done(this);
free(this);
}
static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
const FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl;
int cpus = pl->cpuCount; // actual CPU count
int maxcpu = cpus; // max iteration (in case we have average + smp)
int cp_times_offset;
assert(cpus > 0);
size_t sizeof_cp_time_array;
unsigned long *cp_time_n; // old clicks state
unsigned long *cp_time_o; // current clicks state
unsigned long long total_o = 0;
unsigned long long total_n = 0;
unsigned long long total_d = 0;
unsigned long cp_time_d[CPUSTATES];
double cp_time_p[CPUSTATES];
// get averages or single CPU clicks
sizeof_cp_time_array = sizeof(unsigned long) * CPUSTATES;
sysctl(MIB_kern_cp_time, 2, fpl->cp_time_n, &sizeof_cp_time_array, NULL, 0);
// get rest of CPUs
if (cpus > 1) {
// on smp systems FreeBSD kernel concats all CPU states into one long array in
// kern.cp_times sysctl OID
// we store averages in fpl->cpus[0], and actual cores after that
maxcpu = cpus + 1;
sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES;
sysctl(MIB_kern_cp_times, 2, fpl->cp_times_n, &sizeof_cp_time_array, NULL, 0);
}
for (int i = 0; i < maxcpu; i++) {
if (cpus == 1) {
// single CPU box
cp_time_n = fpl->cp_time_n;
cp_time_o = fpl->cp_time_o;
} else {
if (i == 0 ) {
// average
cp_time_n = fpl->cp_time_n;
cp_time_o = fpl->cp_time_o;
} else {
// specific smp cores
cp_times_offset = i - 1;
cp_time_n = fpl->cp_times_n + (cp_times_offset * CPUSTATES);
cp_time_o = fpl->cp_times_o + (cp_times_offset * CPUSTATES);
}
}
// diff old vs new
for (int s = 0; s < CPUSTATES; s++) {
cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
total_o += cp_time_o[s];
total_n += cp_time_n[s];
}
// totals
total_d = total_n - total_o;
if (total_d < 1 ) total_d = 1;
// save current state as old and calc percentages
for (int s = 0; s < CPUSTATES; ++s) {
cp_time_o[s] = cp_time_n[s];
cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100;
}
CPUData* cpuData = &(fpl->cpus[i]);
cpuData->userPercent = cp_time_p[CP_USER];
cpuData->nicePercent = cp_time_p[CP_NICE];
cpuData->systemPercent = cp_time_p[CP_SYS];
cpuData->irqPercent = cp_time_p[CP_INTR];
cpuData->systemAllPercent = cp_time_p[CP_SYS] + cp_time_p[CP_INTR];
// this one is not really used, but we store it anyway
cpuData->idlePercent = cp_time_p[CP_IDLE];
}
}
static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) {
const FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl;
......@@ -186,6 +320,7 @@ void ProcessList_goThroughEntries(ProcessList* this) {
bool hideUserlandThreads = settings->hideUserlandThreads;
FreeBSDProcessList_scanMemoryInfo(this);
FreeBSDProcessList_scanCPUTime(this);
int count = 0;
struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_ALL, 0, &count);
......@@ -199,7 +334,6 @@ void ProcessList_goThroughEntries(ProcessList* this) {
proc->show = ! ((hideKernelThreads && Process_isKernelThread(fp)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
if (!preExisting) {
fp->jid = kproc->ki_jid;
proc->pid = kproc->ki_pid;
......@@ -220,11 +354,17 @@ void ProcessList_goThroughEntries(ProcessList* this) {
fp->jname = FreeBSDProcessList_readJailName(kproc);
} else {
if(fp->jid != kproc->ki_jid) {
// proces can enter jail anytime
fp->jid = kproc->ki_jid;
free(fp->jname);
fp->jname = FreeBSDProcessList_readJailName(kproc);
}
if (proc->ppid != kproc->ki_ppid) {
// if there are reapers in the system, proces can get reparented anytime
proc->ppid = kproc->ki_ppid;
}
if(proc->st_uid != kproc->ki_uid) {
// some proceses change users (eg. to lower privs)
proc->st_uid = kproc->ki_uid;
proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
}
......@@ -234,8 +374,6 @@ void ProcessList_goThroughEntries(ProcessList* this) {
}
}
proc->ppid = kproc->ki_ppid;
proc->m_size = kproc->ki_size / pageSizeKb / 1000;
proc->m_resident = kproc->ki_rssize; // * pageSizeKb;
proc->nlwp = kproc->ki_numthreads;
......@@ -253,7 +391,6 @@ void ProcessList_goThroughEntries(ProcessList* this) {
proc->nice = PRIO_MAX + 1 + kproc->ki_pri.pri_level - PRI_MIN_IDLE;
}
switch (kproc->ki_stat) {
case SIDL: proc->state = 'I'; break;
case SRUN: proc->state = 'R'; break;
......
......@@ -16,6 +16,7 @@ in the source distribution for its full text.
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "FreeBSDProcess.h"
#include "FreeBSDProcessList.h"
#include <sys/types.h>
#include <sys/sysctl.h>
......@@ -105,7 +106,7 @@ int Platform_getUptime() {
struct timeval bootTime, currTime;
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
size_t size = sizeof(bootTime);
int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
if (err) {
return -1;
......@@ -119,7 +120,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
struct loadavg loadAverage;
int mib[2] = { CTL_VM, VM_LOADAVG };
size_t size = sizeof(loadAverage);
int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0);
if (err) {
*one = 0;
......@@ -143,7 +144,36 @@ int Platform_getMaxPid() {
}
double Platform_setCPUValues(Meter* this, int cpu) {
// TODO
FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
int cpus = this->pl->cpuCount;
CPUData* cpuData;
if (cpus == 1) {
// single CPU box has everything in fpl->cpus[0]
cpuData = &(fpl->cpus[0]);
} else {
cpuData = &(fpl->cpus[cpu]);
}
double percent;
double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePercent;
v[CPU_METER_NORMAL] = cpuData->userPercent;
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
} else {
v[2] = cpuData->systemAllPercent;
Meter_setItems(this, 3);
percent = v[0]+v[1]+v[2];
}
percent = MIN(100.0, MAX(0.0, percent));
if (isnan(percent)) percent = 0.0;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment