Implementation obtains CPU usage information using
kern.cp_time and kern.cp_times sysctl(8)s and reports
CPU utilization.
---
include/libvirt/libvirt.h.in | 8 ++++
src/nodeinfo.c | 104 +++++++++++++++++++++++++++++++++++++++++++
tools/virsh-host.c | 11 ++++-
3 files changed, 121 insertions(+), 2 deletions(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index b3ce000..295d551 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -692,6 +692,14 @@ typedef enum {
#define VIR_NODE_CPU_STATS_IOWAIT "iowait"
/**
+ * VIR_NODE_CPU_STATS_INTR:
+ *
+ * The cumulative interrupt CPU time,
+ * since the node booting up (in nanoseconds).
+ */
+#define VIR_NODE_CPU_STATS_INTR "intr"
+
+/**
* VIR_NODE_CPU_STATS_UTILIZATION:
*
* The CPU utilization of a node.
diff --git a/src/nodeinfo.c b/src/nodeinfo.c
index 6ebfb4b..9a1548e 100644
--- a/src/nodeinfo.c
+++ b/src/nodeinfo.c
@@ -34,8 +34,10 @@
#include "conf/domain_conf.h"
#if defined(__FreeBSD__) || defined(__APPLE__)
+# include <sys/time.h>
# include <sys/types.h>
# include <sys/sysctl.h>
+# include <sys/resource.h>
#endif
#include "c-ctype.h"
@@ -99,8 +101,108 @@ appleFreebsdNodeGetMemorySize(unsigned long *memory)
#endif /* defined(__FreeBSD__) || defined(__APPLE__) */
#ifdef __FreeBSD__
+# define BSD_CPU_STATS_ALL 4
# define BSD_MEMORY_STATS_ALL 4
+# define TICK_TO_NSEC (1000ull * 1000ull * 1000ull / (stathz ? stathz : hz))
+
+static int
+freebsdNodeGetCPUStats(int cpuNum,
+ virNodeCPUStatsPtr params,
+ int *nparams)
+{
+ const char *sysctl_name;
+ long *cpu_times;
+ struct clockinfo clkinfo;
+ size_t i, j, cpu_times_size, clkinfo_size;
+ int cpu_times_num, offset, hz, stathz, ret = -1;
+ struct field_cpu_map {
+ const char *field;
+ int idx[CPUSTATES];
+ } cpu_map[] = {
+ {VIR_NODE_CPU_STATS_KERNEL, {CP_SYS}},
+ {VIR_NODE_CPU_STATS_USER, {CP_USER, CP_NICE}},
+ {VIR_NODE_CPU_STATS_IDLE, {CP_IDLE}},
+ {VIR_NODE_CPU_STATS_INTR, {CP_INTR}},
+ {NULL, {0}}
+ };
+
+ if ((*nparams) == 0) {
+ *nparams = BSD_CPU_STATS_ALL;
+ return 0;
+ }
+
+ if ((*nparams) != BSD_CPU_STATS_ALL) {
+ virReportInvalidArg(*nparams,
+ _("nparams in %s must be equal to %d"),
+ __FUNCTION__, BSD_CPU_STATS_ALL);
+ return -1;
+ }
+
+ clkinfo_size = sizeof(clkinfo);
+ if (sysctlbyname("kern.clockrate", &clkinfo, &clkinfo_size, NULL,
0) < 0) {
+ virReportSystemError(errno,
+ _("sysctl failed for '%s'"),
+ "kern.clockrate");
+ return -1;
+ }
+
+ stathz = clkinfo.stathz;
+ hz = clkinfo.hz;
+
+ if (cpuNum == VIR_NODE_CPU_STATS_ALL_CPUS) {
+ sysctl_name = "kern.cp_time";
+ cpu_times_num = 1;
+ offset = 0;
+ } else {
+ sysctl_name = "kern.cp_times";
+ cpu_times_num = appleFreebsdNodeGetCPUCount();
+
+ if (cpuNum >= cpu_times_num) {
+ virReportInvalidArg(cpuNum,
+ _("Invalid cpuNum in %s"),
+ __FUNCTION__);
+ return -1;
+ }
+
+ offset = cpu_times_num * CPUSTATES;
+ }
+
+ cpu_times_size = sizeof(long) * cpu_times_num * CPUSTATES;
+
+ if (VIR_ALLOC_N(cpu_times, cpu_times_num * CPUSTATES) < 0)
+ goto cleanup;
+
+ if (sysctlbyname(sysctl_name, cpu_times, &cpu_times_size, NULL, 0) < 0) {
+ virReportSystemError(errno,
+ _("sysctl failed for '%s'"),
+ sysctl_name);
+ goto cleanup;
+ }
+
+ for (i = 0; cpu_map[i].field != NULL; i++) {
+ virNodeCPUStatsPtr param = ¶ms[i];
+
+ if (virStrcpyStatic(param->field, cpu_map[i].field) == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Field '%s' too long for destination"),
+ cpu_map[i].field);
+ goto cleanup;
+ }
+
+ param->value = 0;
+ for (j = 0; j < ARRAY_CARDINALITY(cpu_map[i].idx); j++)
+ param->value += cpu_times[offset + cpu_map[i].idx[j]] * TICK_TO_NSEC;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(cpu_times);
+
+ return ret;
+}
+
static int
freebsdNodeGetMemoryStats(virNodeMemoryStatsPtr params,
int *nparams)
@@ -1045,6 +1147,8 @@ int nodeGetCPUStats(int cpuNum ATTRIBUTE_UNUSED,
return ret;
}
+#elif defined(__FreeBSD__)
+ return freebsdNodeGetCPUStats(cpuNum, params, nparams);
#else
virReportError(VIR_ERR_NO_SUPPORT, "%s",
_("node CPU stats not implemented on this platform"));
diff --git a/tools/virsh-host.c b/tools/virsh-host.c
index 3438ae7..f4ca7db 100644
--- a/tools/virsh-host.c
+++ b/tools/virsh-host.c
@@ -347,9 +347,10 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
unsigned long long sys;
unsigned long long idle;
unsigned long long iowait;
+ unsigned long long intr;
unsigned long long util;
} cpu_stats[2];
- double user_time, sys_time, idle_time, iowait_time, total_time;
+ double user_time, sys_time, idle_time, iowait_time, intr_time, total_time;
double usage;
if (vshCommandOptInt(cmd, "cpu", &cpuNum) < 0) {
@@ -390,6 +391,8 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
cpu_stats[i].idle = value;
} else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_IOWAIT)) {
cpu_stats[i].iowait = value;
+ } else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_INTR)) {
+ cpu_stats[i].intr = value;
} else if (STREQ(params[j].field, VIR_NODE_CPU_STATS_UTILIZATION)) {
cpu_stats[i].util = value;
flag_utilization = true;
@@ -406,6 +409,7 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
vshPrint(ctl, "%-15s %20llu\n", _("system:"),
cpu_stats[0].sys);
vshPrint(ctl, "%-15s %20llu\n", _("idle:"),
cpu_stats[0].idle);
vshPrint(ctl, "%-15s %20llu\n", _("iowait:"),
cpu_stats[0].iowait);
+ vshPrint(ctl, "%-15s %20llu\n", _("intr:"),
cpu_stats[0].intr);
}
} else {
if (flag_utilization) {
@@ -418,7 +422,8 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
sys_time = cpu_stats[1].sys - cpu_stats[0].sys;
idle_time = cpu_stats[1].idle - cpu_stats[0].idle;
iowait_time = cpu_stats[1].iowait - cpu_stats[0].iowait;
- total_time = user_time + sys_time + idle_time + iowait_time;
+ intr_time = cpu_stats[1].intr - cpu_stats[0].intr;
+ total_time = user_time + sys_time + idle_time + iowait_time + intr_time;
usage = (user_time + sys_time) / total_time * 100;
@@ -432,6 +437,8 @@ cmdNodeCpuStats(vshControl *ctl, const vshCmd *cmd)
_("idle:"), idle_time / total_time * 100);
vshPrint(ctl, "%-15s %5.1lf%%\n",
_("iowait:"), iowait_time / total_time * 100);
+ vshPrint(ctl, "%-15s %5.1lf%%\n",
+ _("intr:"), intr_time / total_time * 100);
}
}
--
1.8.4.3