/*
* mpstat: per-processor statistics
- * (C) 2000-2016 by Sebastien GODARD (sysstat <at> orange.fr)
+ * (C) 2000-2021 by Sebastien GODARD (sysstat <at> orange.fr)
*
***************************************************************************
* This program is free software; you can redistribute it and/or modify it *
#include <unistd.h>
#include <signal.h>
#include <errno.h>
+#include <dirent.h>
#include <ctype.h>
#include <sys/utsname.h>
#include "version.h"
#include "mpstat.h"
-#include "common.h"
#include "rd_stats.h"
#include "count.h"
+#include <locale.h> /* For setlocale() */
#ifdef USE_NLS
-#include <locale.h>
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif
+#ifdef USE_SCCSID
#define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
char *sccsid(void) { return (SCCSID); }
+#endif
-unsigned long long uptime[3] = {0, 0, 0};
-unsigned long long uptime0[3] = {0, 0, 0};
+unsigned long long uptime_cs[3] = {0, 0, 0};
/* NOTE: Use array of _char_ for bitmaps to avoid endianness problems...*/
unsigned char *cpu_bitmap; /* Bit 0: Global; Bit 1: 1st proc; etc. */
+unsigned char *node_bitmap; /* Bit 0: Global; Bit 1: 1st NUMA node; etc. */
-/* Structure used to save CPU stats */
+/* Structures used to save CPU and NUMA nodes CPU stats */
struct stats_cpu *st_cpu[3];
+struct stats_cpu *st_node[3];
+
/*
* Structure used to save total number of interrupts received
* among all CPU and for each CPU.
*/
struct stats_irq *st_irq[3];
+
/*
* Structures used to save, for each interrupt, the number
* received by each CPU.
struct stats_irqcpu *st_irqcpu[3];
struct stats_irqcpu *st_softirqcpu[3];
+/*
+ * Number of CPU per node, e.g.:
+ * cpu_per_node[0]: total nr of CPU (this is node "all")
+ * cpu_per_node[1]: nr of CPU for node 0
+ * etc.
+ */
+int *cpu_per_node;
+
+/*
+ * Node number the CPU belongs to, e.g.:
+ * cpu2node[0]: node nr for CPU 0
+ */
+int *cpu2node;
+
+/* CPU topology */
+struct cpu_topology *st_cpu_topology;
+
struct tm mp_tstamp[3];
/* Activity flag */
/* Interval and count parameters */
long interval = -1, count = 0;
+/* Number of decimal places */
+int dplaces_nr = -1;
-/* Nb of processors on the machine */
+/*
+ * Nb of processors on the machine.
+ * A value of 2 means there are 2 processors (0 and 1).
+ */
int cpu_nr = 0;
+
+/*
+ * Highest NUMA node number found on the machine.
+ * A value of 0 means node 0 (one node).
+ * A value of -1 means no nodes found.
+ * We have: node_nr < cpu_nr (see get_node_placement() function).
+ */
+int node_nr = -1;
+
/* Nb of interrupts per processor */
int irqcpu_nr = 0;
/* Nb of soft interrupts per processor */
progname);
fprintf(stderr, _("Options are:\n"
- "[ -A ] [ -u ] [ -V ] [ -I { SUM | CPU | SCPU | ALL } ]\n"
- "[ -o JSON ] [ -P { <cpu> [,...] | ON | ALL } ]\n"));
+ "[ -A ] [ -n ] [ -T ] [ -u ] [ -V ]\n"
+ "[ -I { SUM | CPU | SCPU | ALL } ] [ -N { <node_list> | ALL } ]\n"
+ "[ --dec={ 0 | 1 | 2 } ] [ -o JSON ] [ -P { <cpu_list> | ALL } ]\n"));
exit(1);
}
/*
***************************************************************************
- * Allocate stats structures and cpu bitmap.
+ * Allocate stats structures and cpu bitmap. Also do it for NUMA nodes
+ * (although the machine may not be a NUMA one). Assume that the number of
+ * nodes is lower or equal than that of CPU.
*
* IN:
* @nr_cpus Number of CPUs. This is the real number of available CPUs + 1
}
memset(st_cpu[i], 0, STATS_CPU_SIZE * nr_cpus);
+ if ((st_node[i] = (struct stats_cpu *) malloc(STATS_CPU_SIZE * nr_cpus))
+ == NULL) {
+ perror("malloc");
+ exit(4);
+ }
+ memset(st_node[i], 0, STATS_CPU_SIZE * nr_cpus);
+
if ((st_irq[i] = (struct stats_irq *) malloc(STATS_IRQ_SIZE * nr_cpus))
== NULL) {
perror("malloc");
exit(4);
}
memset(cpu_bitmap, 0, (nr_cpus >> 3) + 1);
+
+ if ((node_bitmap = (unsigned char *) malloc((nr_cpus >> 3) + 1)) == NULL) {
+ perror("malloc");
+ exit(4);
+ }
+ memset(node_bitmap, 0, (nr_cpus >> 3) + 1);
+
+ if ((cpu_per_node = (int *) malloc(sizeof(int) * nr_cpus)) == NULL) {
+ perror("malloc");
+ exit(4);
+ }
+
+ if ((cpu2node = (int *) malloc(sizeof(int) * nr_cpus)) == NULL) {
+ perror("malloc");
+ exit(4);
+ }
+
+ if ((st_cpu_topology = (struct cpu_topology *) malloc(sizeof(struct cpu_topology) * nr_cpus)) == NULL) {
+ perror("malloc");
+ exit(4);
+ }
}
/*
for (i = 0; i < 3; i++) {
free(st_cpu[i]);
+ free(st_node[i]);
free(st_irq[i]);
free(st_irqcpu[i]);
free(st_softirqcpu[i]);
}
free(cpu_bitmap);
+ free(node_bitmap);
+ free(cpu_per_node);
+ free(cpu2node);
+}
+
+/*
+ ***************************************************************************
+ * Get node placement (which node each CPU belongs to, and total number of
+ * CPU that each node has).
+ *
+ * IN:
+ * @nr_cpus Number of CPU on this machine.
+ *
+ * OUT:
+ * @cpu_per_node Number of CPU per node.
+ * @cpu2node The node the CPU belongs to.
+ *
+ * RETURNS:
+ * Highest node number found (e.g., 0 means node 0).
+ * A value of -1 means no nodes have been found.
+ ***************************************************************************
+ */
+int get_node_placement(int nr_cpus, int cpu_per_node[], int cpu2node[])
+
+{
+ DIR *dir;
+ struct dirent *drd;
+ char line[MAX_PF_NAME];
+ int cpu, node, hi_node_nr = -1;
+
+ /* Init number of CPU per node */
+ memset(cpu_per_node, 0, sizeof(int) * (nr_cpus + 1));
+ /* CPU belongs to no node by default */
+ memset(cpu2node, -1, sizeof(int) * nr_cpus);
+
+ /* This is node "all" */
+ cpu_per_node[0] = nr_cpus;
+
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+ snprintf(line, sizeof(line), "%s/cpu%d", SYSFS_DEVCPU, cpu);
+ line[sizeof(line) - 1] = '\0';
+
+ /* Open relevant /sys directory */
+ if ((dir = opendir(line)) == NULL)
+ return -1;
+
+ /* Get current file entry */
+ while ((drd = readdir(dir)) != NULL) {
+
+ if (!strncmp(drd->d_name, "node", 4) && isdigit(drd->d_name[4])) {
+ node = atoi(drd->d_name + 4);
+ if ((node >= nr_cpus) || (node < 0)) {
+ /* Assume we cannot have more nodes than CPU */
+ closedir(dir);
+ return -1;
+ }
+ cpu_per_node[node + 1]++;
+ cpu2node[cpu] = node;
+ if (node > hi_node_nr) {
+ hi_node_nr = node;
+ }
+ /* Node placement found for current CPU: Go to next CPU directory */
+ break;
+ }
+ }
+
+ /* Close directory */
+ closedir(dir);
+ }
+
+ return hi_node_nr;
+}
+
+/*
+ ***************************************************************************
+ * Read system logical topology: Socket number for each logical core is read
+ * from the /sys/devices/system/cpu/cpu{N}/topology/physical_package_id file,
+ * and the logical core id number is the first number read from the
+ * /sys/devices/system/cpu/cpu{N}/topology/thread_siblings_list file.
+ * Don't use /sys/devices/system/cpu/cpu{N}/topology/core_id as this is the
+ * physical core id (seems to be different from the number displayed by lscpu).
+ *
+ * IN:
+ * @nr_cpus Number of CPU on this machine.
+ * @cpu_topo Structures where socket and core id numbers will be saved.
+ *
+ * OUT:
+ * @cpu_topo Structures where socket and core id numbers have been saved.
+ ***************************************************************************
+ */
+void read_topology(int nr_cpus, struct cpu_topology *cpu_topo)
+{
+ struct cpu_topology *cpu_topo_i;
+ FILE *fp;
+ char filename[MAX_PF_NAME];
+ int cpu, rc;
+
+ /* Init system topology */
+ memset(st_cpu_topology, 0, sizeof(struct cpu_topology) * nr_cpus);
+
+ for (cpu = 0; cpu < nr_cpus; cpu++) {
+
+ cpu_topo_i = cpu_topo + cpu;
+
+ /* Read current CPU's socket number */
+ snprintf(filename, sizeof(filename), "%s/cpu%d/%s", SYSFS_DEVCPU, cpu, PHYS_PACK_ID);
+ filename[sizeof(filename) - 1] = '\0';
+
+ if ((fp = fopen(filename, "r")) != NULL) {
+ rc = fscanf(fp, "%d", &cpu_topo_i->phys_package_id);
+ fclose(fp);
+
+ if (rc < 1) {
+ cpu_topo_i->phys_package_id = -1;
+ }
+ }
+
+ /* Read current CPU's logical core id number */
+ snprintf(filename, sizeof(filename), "%s/cpu%d/%s", SYSFS_DEVCPU, cpu, THREAD_SBL_LST);
+ filename[sizeof(filename) - 1] = '\0';
+
+ if ((fp = fopen(filename, "r")) != NULL) {
+ rc = fscanf(fp, "%d", &cpu_topo_i->logical_core_id);
+ fclose(fp);
+
+ if (rc < 1) {
+ cpu_topo_i->logical_core_id = -1;
+ }
+ }
+ }
+}
+
+/*
+ ***************************************************************************
+ * Compute node statistics: Split CPU statistics among nodes.
+ *
+ * IN:
+ * @src Structure containing CPU stats to add.
+ *
+ * OUT:
+ * @dest Structure containing global CPU stats.
+ ***************************************************************************
+ */
+void add_cpu_stats(struct stats_cpu *dest, struct stats_cpu *src)
+{
+ dest->cpu_user += src->cpu_user;
+ dest->cpu_nice += src->cpu_nice;
+ dest->cpu_sys += src->cpu_sys;
+ dest->cpu_idle += src->cpu_idle;
+ dest->cpu_iowait += src->cpu_iowait;
+ dest->cpu_hardirq += src->cpu_hardirq;
+ dest->cpu_softirq += src->cpu_softirq;
+ dest->cpu_steal += src->cpu_steal;
+ dest->cpu_guest += src->cpu_guest;
+ dest->cpu_guest_nice += src->cpu_guest_nice;
+}
+
+/*
+ ***************************************************************************
+ * Compute node statistics: Split CPU statistics among nodes.
+ *
+ * IN:
+ * @prev Index in array where stats used as reference are.
+ * @curr Index in array for current sample statistics.
+ *
+ * OUT:
+ * @st_node Array where CPU stats for each node have been saved.
+ ***************************************************************************
+ */
+void set_node_cpu_stats(int prev, int curr)
+{
+ int cpu;
+ unsigned long long tot_jiffies_p;
+ struct stats_cpu *scp, *scc, *snp, *snc;
+ struct stats_cpu *scc_all = st_cpu[curr];
+ struct stats_cpu *scp_all = st_cpu[prev];
+ struct stats_cpu *snc_all = st_node[curr];
+ struct stats_cpu *snp_all = st_node[prev];
+
+ /* Reset structures */
+ memset(st_node[prev], 0, STATS_CPU_SIZE * (cpu_nr + 1));
+ memset(st_node[curr], 0, STATS_CPU_SIZE * (cpu_nr + 1));
+
+ /* Node 'all' is the same as CPU 'all' */
+ *snp_all = *scp_all;
+ *snc_all = *scc_all;
+
+ /* Individual nodes */
+ for (cpu = 0; cpu < cpu_nr; cpu++) {
+ scc = st_cpu[curr] + cpu + 1;
+ scp = st_cpu[prev] + cpu + 1;
+ snp = st_node[prev] + cpu2node[cpu] + 1;
+ snc = st_node[curr] + cpu2node[cpu] + 1;
+
+
+ tot_jiffies_p = scp->cpu_user + scp->cpu_nice +
+ scp->cpu_sys + scp->cpu_idle +
+ scp->cpu_iowait + scp->cpu_hardirq +
+ scp->cpu_steal + scp->cpu_softirq;
+ if ((tot_jiffies_p == 0) && (interval != 0))
+ /*
+ * CPU has just come back online with no ref from
+ * previous iteration: Skip it.
+ */
+ continue;
+
+ add_cpu_stats(snp, scp);
+ add_cpu_stats(snc, scc);
+ }
+}
+
+/*
+ ***************************************************************************
+ * Compute global CPU statistics as the sum of individual CPU ones, and
+ * calculate interval for global CPU.
+ * Also identify offline CPU.
+ *
+ * IN:
+ * @prev Index in array where stats used as reference are.
+ * @curr Index in array for current sample statistics.
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
+ *
+ * OUT:
+ * @offline_cpu_bitmap
+ * CPU bitmap with offline CPU.
+ *
+ * RETURNS:
+ * Interval for global CPU.
+ ***************************************************************************
+ */
+unsigned long long get_global_cpu_mpstats(int prev, int curr,
+ unsigned char offline_cpu_bitmap[])
+{
+ int i;
+ unsigned long long tot_jiffies_c, tot_jiffies_p;
+ unsigned long long deltot_jiffies = 0;
+ struct stats_cpu *scc, *scp;
+ struct stats_cpu *scc_all = st_cpu[curr];
+ struct stats_cpu *scp_all = st_cpu[prev];
+
+ /*
+ * For UP machines we keep the values read from global CPU line in /proc/stat.
+ * Also look for offline CPU: They won't be displayed, and some of their values may
+ * have to be modified.
+ */
+ if (cpu_nr > 1) {
+ memset(scc_all, 0, sizeof(struct stats_cpu));
+ memset(scp_all, 0, sizeof(struct stats_cpu));
+ }
+ else {
+ /* This is a UP machine */
+ return get_per_cpu_interval(st_cpu[curr], st_cpu[prev]);
+ }
+
+ for (i = 1; i <= cpu_nr; i++) {
+
+ scc = st_cpu[curr] + i;
+ scp = st_cpu[prev] + i;
+
+ /*
+ * Compute the total number of jiffies spent by current processor.
+ * NB: Don't add cpu_guest/cpu_guest_nice because cpu_user/cpu_nice
+ * already include them.
+ */
+ tot_jiffies_c = scc->cpu_user + scc->cpu_nice +
+ scc->cpu_sys + scc->cpu_idle +
+ scc->cpu_iowait + scc->cpu_hardirq +
+ scc->cpu_steal + scc->cpu_softirq;
+ tot_jiffies_p = scp->cpu_user + scp->cpu_nice +
+ scp->cpu_sys + scp->cpu_idle +
+ scp->cpu_iowait + scp->cpu_hardirq +
+ scp->cpu_steal + scp->cpu_softirq;
+
+ /*
+ * If the CPU is offline then it is omitted from /proc/stat:
+ * All the fields couldn't have been read and the sum of them is zero.
+ */
+ if (tot_jiffies_c == 0) {
+ /*
+ * CPU is currently offline.
+ * Set current struct fields (which have been set to zero)
+ * to values from previous iteration. Hence their values won't
+ * jump from zero when the CPU comes back online.
+ * Note that this workaround no longer fully applies with recent kernels,
+ * as I have noticed that when a CPU comes back online, some fields
+ * restart from their previous value (e.g. user, nice, system)
+ * whereas others restart from zero (idle, iowait)! To deal with this,
+ * the get_per_cpu_interval() function will set these previous values
+ * to zero if necessary.
+ */
+ *scc = *scp;
+
+ /*
+ * Mark CPU as offline to not display it
+ * (and thus it will not be confused with a tickless CPU).
+ */
+ offline_cpu_bitmap[i >> 3] |= 1 << (i & 0x07);
+ }
+
+ if ((tot_jiffies_p == 0) && (interval != 0)) {
+ /*
+ * CPU has just come back online.
+ * Unfortunately, no reference values are available
+ * from a previous iteration, probably because it was
+ * already offline when the first sample has been taken.
+ * So don't display that CPU to prevent "jump-from-zero"
+ * output syndrome, and don't take it into account for CPU "all".
+ * NB: Test for interval != 0 to make sure we don't want stats
+ * since boot time.
+ */
+ offline_cpu_bitmap[i >> 3] |= 1 << (i & 0x07);
+ continue;
+ }
+
+ /*
+ * Get interval for current CPU and add it to global CPU.
+ * Note: Previous idle and iowait values (saved in scp) may be modified here.
+ */
+ deltot_jiffies += get_per_cpu_interval(scc, scp);
+
+ add_cpu_stats(scc_all, scc);
+ add_cpu_stats(scp_all, scp);
+ }
+
+ return deltot_jiffies;
}
/*
*
* IN:
* @dis TRUE if a header line must be printed.
- * @g_itv Interval value in jiffies multiplied by the number of CPU.
+ * @deltot_jiffies
+ * Number of jiffies spent on the interval by all processors.
* @prev Position in array where statistics used as reference are.
* Stats used as reference may be the previous ones read, or
* the very first ones when calculating the average.
* @curr_string String displayed at the beginning of current sample stats.
* This is the timestamp of the current sample, or "Average"
* when displaying average stats.
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
***************************************************************************
*/
-void write_plain_cpu_stats(int dis, unsigned long long g_itv, int prev, int curr,
- char *prev_string, char *curr_string)
+void write_plain_cpu_stats(int dis, unsigned long long deltot_jiffies, int prev, int curr,
+ char *prev_string, char *curr_string, unsigned char offline_cpu_bitmap[])
{
+ int i;
struct stats_cpu *scc, *scp;
- unsigned long long pc_itv;
- int cpu;
+ struct cpu_topology *cpu_topo_i;
if (dis) {
- printf("\n%-11s CPU %%usr %%nice %%sys %%iowait %%irq "
- "%%soft %%steal %%guest %%gnice %%idle\n",
- prev_string);
+ printf("\n%-11s CPU", prev_string);
+ if (DISPLAY_TOPOLOGY(flags)) {
+ printf(" CORE SOCK NODE");
+ }
+ printf(" %%usr %%nice %%sys %%iowait %%irq "
+ "%%soft %%steal %%guest %%gnice %%idle\n");
}
- /* Check if we want global stats among all proc */
- if (*cpu_bitmap & 1) {
+ /*
+ * Now display CPU statistics (including CPU "all"),
+ * except for offline CPU or CPU that the user doesn't want to see.
+ */
+ for (i = 0; i <= cpu_nr; i++) {
+
+ /* Check if we want stats about this proc */
+ if (!(*(cpu_bitmap + (i >> 3)) & (1 << (i & 0x07))) ||
+ offline_cpu_bitmap[i >> 3] & (1 << (i & 0x07)))
+ continue;
+
+ scc = st_cpu[curr] + i;
+ scp = st_cpu[prev] + i;
printf("%-11s", curr_string);
- cprintf_in(IS_STR, " %s", " all", 0);
- cprintf_pc(10, 7, 2,
- (st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest) <
- (st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest) ?
+ if (i == 0) {
+ /* This is CPU "all" */
+ cprintf_in(IS_STR, " %s", " all", 0);
+
+ if (DISPLAY_TOPOLOGY(flags)) {
+ printf(" ");
+ }
+ }
+ else {
+ cprintf_in(IS_INT, " %4d", "", i - 1);
+
+ if (DISPLAY_TOPOLOGY(flags)) {
+ cpu_topo_i = st_cpu_topology + i - 1;
+ cprintf_in(IS_INT, " %4d", "", cpu_topo_i->logical_core_id);
+ cprintf_in(IS_INT, " %4d", "", cpu_topo_i->phys_package_id);
+ cprintf_in(IS_INT, " %4d", "", cpu2node[i - 1]);
+ }
+
+ /* Recalculate itv for current proc */
+ deltot_jiffies = get_per_cpu_interval(scc, scp);
+
+ if (!deltot_jiffies) {
+ /*
+ * If the CPU is tickless then there is no change in CPU values
+ * but the sum of values is not zero.
+ */
+ cprintf_pc(NO_UNIT, 10, 7, 2,
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0);
+ printf("\n");
+
+ continue;
+ }
+ }
+
+ cprintf_pc(NO_UNIT, 10, 7, 2,
+ (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
0.0 :
- ll_sp_value(st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest,
- st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest,
- g_itv),
- (st_cpu[curr]->cpu_nice - st_cpu[curr]->cpu_guest_nice) <
- (st_cpu[prev]->cpu_nice - st_cpu[prev]->cpu_guest_nice) ?
+ ll_sp_value(scp->cpu_user - scp->cpu_guest,
+ scc->cpu_user - scc->cpu_guest, deltot_jiffies),
+ (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
0.0 :
- ll_sp_value(st_cpu[prev]->cpu_nice - st_cpu[prev]->cpu_guest_nice,
- st_cpu[curr]->cpu_nice - st_cpu[curr]->cpu_guest_nice,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_sys,
- st_cpu[curr]->cpu_sys,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_iowait,
- st_cpu[curr]->cpu_iowait,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_hardirq,
- st_cpu[curr]->cpu_hardirq,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_softirq,
- st_cpu[curr]->cpu_softirq,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_steal,
- st_cpu[curr]->cpu_steal,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_guest,
- st_cpu[curr]->cpu_guest,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_guest_nice,
- st_cpu[curr]->cpu_guest_nice,
- g_itv),
- (st_cpu[curr]->cpu_idle < st_cpu[prev]->cpu_idle) ?
+ ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
+ scc->cpu_nice - scc->cpu_guest_nice, deltot_jiffies),
+ ll_sp_value(scp->cpu_sys,
+ scc->cpu_sys, deltot_jiffies),
+ ll_sp_value(scp->cpu_iowait,
+ scc->cpu_iowait, deltot_jiffies),
+ ll_sp_value(scp->cpu_hardirq,
+ scc->cpu_hardirq, deltot_jiffies),
+ ll_sp_value(scp->cpu_softirq,
+ scc->cpu_softirq, deltot_jiffies),
+ ll_sp_value(scp->cpu_steal,
+ scc->cpu_steal, deltot_jiffies),
+ ll_sp_value(scp->cpu_guest,
+ scc->cpu_guest, deltot_jiffies),
+ ll_sp_value(scp->cpu_guest_nice,
+ scc->cpu_guest_nice, deltot_jiffies),
+ (scc->cpu_idle < scp->cpu_idle) ?
0.0 :
- ll_sp_value(st_cpu[prev]->cpu_idle,
- st_cpu[curr]->cpu_idle,
- g_itv));
+ ll_sp_value(scp->cpu_idle,
+ scc->cpu_idle, deltot_jiffies));
printf("\n");
}
+}
- for (cpu = 1; cpu <= cpu_nr; cpu++) {
+/*
+ ***************************************************************************
+ * Display CPU statistics in JSON format.
+ *
+ * IN:
+ * @tab Number of tabs to print.
+ * @deltot_jiffies
+ * Number of jiffies spent on the interval by all processors.
+ * @prev Position in array where statistics used as reference are.
+ * Stats used as reference may be the previous ones read, or
+ * the very first ones when calculating the average.
+ * @curr Position in array where current statistics will be saved.
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
+ ***************************************************************************
+ */
+void write_json_cpu_stats(int tab, unsigned long long deltot_jiffies, int prev, int curr,
+ unsigned char offline_cpu_bitmap[])
+{
+ int i, next = FALSE;
+ char cpu_name[16], topology[1024] = "";
+ struct stats_cpu *scc, *scp;
+ struct cpu_topology *cpu_topo_i;
- scc = st_cpu[curr] + cpu;
- scp = st_cpu[prev] + cpu;
+ xprintf(tab++, "\"cpu-load\": [");
+
+ /*
+ * Now display CPU statistics (including CPU "all"),
+ * except for offline CPU or CPU that the user doesn't want to see.
+ */
+ for (i = 0; i <= cpu_nr; i++) {
/* Check if we want stats about this proc */
- if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))))
+ if (!(*(cpu_bitmap + (i >> 3)) & (1 << (i & 0x07))) ||
+ offline_cpu_bitmap[i >> 3] & (1 << (i & 0x07)))
continue;
- /*
- * If the CPU is offline then it is omited from /proc/stat
- * and the sum of all values is zero.
- * (Remember that guest/guest_nice times are already included in
- * user/nice modes.)
- */
- if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
- scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
- scc->cpu_hardirq + scc->cpu_softirq) == 0) {
-
- if (!DISPLAY_ONLINE_CPU(flags)) {
- printf("%-11s", curr_string);
- cprintf_in(IS_INT, " %4d", "", cpu - 1);
- cprintf_pc(10, 7, 2,
- 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
- printf("\n");
+ scc = st_cpu[curr] + i;
+ scp = st_cpu[prev] + i;
+
+ if (next) {
+ printf(",\n");
+ }
+ next = TRUE;
+
+ if (i == 0) {
+ /* This is CPU "all" */
+ strcpy(cpu_name, "all");
+
+ if (DISPLAY_TOPOLOGY(flags)) {
+ snprintf(topology, 1024,
+ ", \"core\": \"\", \"socket\": \"\", \"node\": \"\"");
}
- continue;
+
}
+ else {
+ snprintf(cpu_name, 16, "%d", i - 1);
+ cpu_name[15] = '\0';
+
+ if (DISPLAY_TOPOLOGY(flags)) {
+ cpu_topo_i = st_cpu_topology + i - 1;
+ snprintf(topology, 1024,
+ ", \"core\": \"%d\", \"socket\": \"%d\", \"node\": \"%d\"",
+ cpu_topo_i->logical_core_id, cpu_topo_i->phys_package_id, cpu2node[i - 1]);
+ }
- printf("%-11s", curr_string);
- cprintf_in(IS_INT, " %4d", "", cpu - 1);
+ /* Recalculate itv for current proc */
+ deltot_jiffies = get_per_cpu_interval(scc, scp);
- /* Recalculate itv for current proc */
- pc_itv = get_per_cpu_interval(scc, scp);
+ if (!deltot_jiffies) {
+ /*
+ * If the CPU is tickless then there is no change in CPU values
+ * but the sum of values is not zero.
+ */
+ xprintf0(tab, "{\"cpu\": \"%d\"%s, \"usr\": 0.00, \"nice\": 0.00, "
+ "\"sys\": 0.00, \"iowait\": 0.00, \"irq\": 0.00, "
+ "\"soft\": 0.00, \"steal\": 0.00, \"guest\": 0.00, "
+ "\"gnice\": 0.00, \"idle\": 100.00}", i - 1, topology);
+ printf("\n");
- if (!pc_itv) {
- /*
- * If the CPU is tickless then there is no change in CPU values
- * but the sum of values is not zero.
- */
- cprintf_pc(10, 7, 2,
- 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0);
- printf("\n");
+ continue;
+ }
}
- else {
- cprintf_pc(10, 7, 2,
- (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
- 0.0 :
- ll_sp_value(scp->cpu_user - scp->cpu_guest,
- scc->cpu_user - scc->cpu_guest,
- pc_itv),
- (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
- 0.0 :
- ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
- scc->cpu_nice - scc->cpu_guest_nice,
- pc_itv),
- ll_sp_value(scp->cpu_sys,
- scc->cpu_sys,
- pc_itv),
- ll_sp_value(scp->cpu_iowait,
- scc->cpu_iowait,
- pc_itv),
- ll_sp_value(scp->cpu_hardirq,
- scc->cpu_hardirq,
- pc_itv),
- ll_sp_value(scp->cpu_softirq,
- scc->cpu_softirq,
- pc_itv),
- ll_sp_value(scp->cpu_steal,
- scc->cpu_steal,
- pc_itv),
- ll_sp_value(scp->cpu_guest,
- scc->cpu_guest,
- pc_itv),
- ll_sp_value(scp->cpu_guest_nice,
- scc->cpu_guest_nice,
- pc_itv),
- (scc->cpu_idle < scp->cpu_idle) ?
- 0.0 :
- ll_sp_value(scp->cpu_idle,
- scc->cpu_idle,
- pc_itv));
- printf("\n");
+ xprintf0(tab, "{\"cpu\": \"%s\"%s, \"usr\": %.2f, \"nice\": %.2f, \"sys\": %.2f, "
+ "\"iowait\": %.2f, \"irq\": %.2f, \"soft\": %.2f, \"steal\": %.2f, "
+ "\"guest\": %.2f, \"gnice\": %.2f, \"idle\": %.2f}",
+ cpu_name, topology,
+ (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
+ 0.0 :
+ ll_sp_value(scp->cpu_user - scp->cpu_guest,
+ scc->cpu_user - scc->cpu_guest, deltot_jiffies),
+ (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
+ 0.0 :
+ ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
+ scc->cpu_nice - scc->cpu_guest_nice, deltot_jiffies),
+ ll_sp_value(scp->cpu_sys,
+ scc->cpu_sys, deltot_jiffies),
+ ll_sp_value(scp->cpu_iowait,
+ scc->cpu_iowait, deltot_jiffies),
+ ll_sp_value(scp->cpu_hardirq,
+ scc->cpu_hardirq, deltot_jiffies),
+ ll_sp_value(scp->cpu_softirq,
+ scc->cpu_softirq, deltot_jiffies),
+ ll_sp_value(scp->cpu_steal,
+ scc->cpu_steal, deltot_jiffies),
+ ll_sp_value(scp->cpu_guest,
+ scc->cpu_guest, deltot_jiffies),
+ ll_sp_value(scp->cpu_guest_nice,
+ scc->cpu_guest_nice, deltot_jiffies),
+ (scc->cpu_idle < scp->cpu_idle) ?
+ 0.0 :
+ ll_sp_value(scp->cpu_idle,
+ scc->cpu_idle, deltot_jiffies));
+ }
+
+ printf("\n");
+ xprintf0(--tab, "]");
+}
+
+/*
+ ***************************************************************************
+ * Display CPU statistics in plain or JSON format.
+ *
+ * IN:
+ * @dis TRUE if a header line must be printed.
+ * @deltot_jiffies
+ * Number of jiffies spent on the interval by all processors.
+ * @prev Position in array where statistics used as reference are.
+ * Stats used as reference may be the previous ones read, or
+ * the very first ones when calculating the average.
+ * @curr Position in array where current statistics will be saved.
+ * @prev_string String displayed at the beginning of a header line. This is
+ * the timestamp of the previous sample, or "Average" when
+ * displaying average stats.
+ * @curr_string String displayed at the beginning of current sample stats.
+ * This is the timestamp of the current sample, or "Average"
+ * when displaying average stats.
+ * @tab Number of tabs to print (JSON format only).
+ * @next TRUE is a previous activity has been displayed (JSON format
+ * only).
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
+ ***************************************************************************
+ */
+void write_cpu_stats(int dis, unsigned long long deltot_jiffies, int prev, int curr,
+ char *prev_string, char *curr_string, int tab, int *next,
+ unsigned char offline_cpu_bitmap[])
+{
+ if (!deltot_jiffies) {
+ /* CPU "all" cannot be tickless */
+ deltot_jiffies = 1;
+ }
+
+ if (DISPLAY_JSON_OUTPUT(flags)) {
+ if (*next) {
+ printf(",\n");
}
+ *next = TRUE;
+ write_json_cpu_stats(tab, deltot_jiffies, prev, curr,
+ offline_cpu_bitmap);
+ }
+ else {
+ write_plain_cpu_stats(dis, deltot_jiffies, prev, curr,
+ prev_string, curr_string, offline_cpu_bitmap);
}
}
/*
***************************************************************************
- * Display CPU statistics in JSON format.
+ * Display CPU statistics for NUMA nodes in plain format.
*
* IN:
- * @tab Number of tabs to print.
- * @g_itv Interval value in jiffies multiplied by the number of CPU.
+ * @dis TRUE if a header line must be printed.
+ * @deltot_jiffies
+ * Number of jiffies spent on the interval by all processors.
* @prev Position in array where statistics used as reference are.
* Stats used as reference may be the previous ones read, or
* the very first ones when calculating the average.
* @curr Position in array where current statistics will be saved.
+ * @prev_string String displayed at the beginning of a header line. This is
+ * the timestamp of the previous sample, or "Average" when
+ * displaying average stats.
* @curr_string String displayed at the beginning of current sample stats.
- * This is the timestamp of the current sample.
+ * This is the timestamp of the current sample, or "Average"
+ * when displaying average stats.
***************************************************************************
*/
-void write_json_cpu_stats(int tab, unsigned long long g_itv, int prev, int curr,
- char *curr_string)
+void write_plain_node_stats(int dis, unsigned long long deltot_jiffies,
+ int prev, int curr, char *prev_string, char *curr_string)
{
- struct stats_cpu *scc, *scp;
- unsigned long long pc_itv;
- int cpu, next = FALSE;
+ struct stats_cpu *snc, *snp, *scc, *scp;
+ int cpu, node;
- xprintf(tab++, "\"cpu-load\": [");
+ if (dis) {
+ printf("\n%-11s NODE %%usr %%nice %%sys %%iowait %%irq "
+ "%%soft %%steal %%guest %%gnice %%idle\n",
+ prev_string);
+ }
- /* Check if we want global stats among all proc */
- if (*cpu_bitmap & 1) {
+ for (node = 0; node <= node_nr + 1; node++) {
- next = TRUE;
- xprintf0(tab, "{\"cpu\": \"all\", \"usr\": %.2f, \"nice\": %.2f, \"sys\": %.2f, "
- "\"iowait\": %.2f, \"irq\": %.2f, \"soft\": %.2f, \"steal\": %.2f, "
- "\"guest\": %.2f, \"gnice\": %.2f, \"idle\": %.2f}",
- (st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest) <
- (st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest) ?
- 0.0 :
- ll_sp_value(st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest,
- st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest,
- g_itv),
- (st_cpu[curr]->cpu_nice - st_cpu[curr]->cpu_guest_nice) <
- (st_cpu[prev]->cpu_nice - st_cpu[prev]->cpu_guest_nice) ?
- 0.0 :
- ll_sp_value(st_cpu[prev]->cpu_nice - st_cpu[prev]->cpu_guest_nice,
- st_cpu[curr]->cpu_nice - st_cpu[curr]->cpu_guest_nice,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_sys,
- st_cpu[curr]->cpu_sys,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_iowait,
- st_cpu[curr]->cpu_iowait,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_hardirq,
- st_cpu[curr]->cpu_hardirq,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_softirq,
- st_cpu[curr]->cpu_softirq,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_steal,
- st_cpu[curr]->cpu_steal,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_guest,
- st_cpu[curr]->cpu_guest,
- g_itv),
- ll_sp_value(st_cpu[prev]->cpu_guest_nice,
- st_cpu[curr]->cpu_guest_nice,
- g_itv),
- (st_cpu[curr]->cpu_idle < st_cpu[prev]->cpu_idle) ?
- 0.0 :
- ll_sp_value(st_cpu[prev]->cpu_idle,
- st_cpu[curr]->cpu_idle,
- g_itv));
+ snc = st_node[curr] + node;
+ snp = st_node[prev] + node;
+
+ /* Check if we want stats about this node */
+ if (!(*(node_bitmap + (node >> 3)) & (1 << (node & 0x07))))
+ continue;
+
+ if (!cpu_per_node[node])
+ /* No CPU in this node */
+ continue;
+
+ printf("%-11s", curr_string);
+ if (node == 0) {
+ /* This is node "all", i.e. CPU "all" */
+ cprintf_in(IS_STR, " %s", " all", 0);
+ }
+ else {
+ cprintf_in(IS_INT, " %4d", "", node - 1);
+
+ /* Recalculate interval for current node */
+ deltot_jiffies = 0;
+ for (cpu = 1; cpu <= cpu_nr; cpu++) {
+ scc = st_cpu[curr] + cpu;
+ scp = st_cpu[prev] + cpu;
+
+ if ((scp->cpu_user + scp->cpu_nice + scp->cpu_sys +
+ scp->cpu_idle + scp->cpu_iowait + scp->cpu_hardirq +
+ scp->cpu_steal + scp->cpu_softirq == 0) && (interval != 0))
+ continue;
+
+ if (cpu2node[cpu - 1] == node - 1) {
+ deltot_jiffies += get_per_cpu_interval(scc, scp);
+ }
+ }
+
+ if (!deltot_jiffies) {
+ /* All CPU in node are tickless and/or offline */
+ cprintf_pc(NO_UNIT, 10, 7, 2,
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0);
+ printf("\n");
+
+ continue;
+ }
+ }
+
+ cprintf_pc(NO_UNIT, 10, 7, 2,
+ (snc->cpu_user - snc->cpu_guest) < (snp->cpu_user - snp->cpu_guest) ?
+ 0.0 :
+ ll_sp_value(snp->cpu_user - snp->cpu_guest,
+ snc->cpu_user - snc->cpu_guest, deltot_jiffies),
+ (snc->cpu_nice - snc->cpu_guest_nice) < (snp->cpu_nice - snp->cpu_guest_nice) ?
+ 0.0 :
+ ll_sp_value(snp->cpu_nice - snp->cpu_guest_nice,
+ snc->cpu_nice - snc->cpu_guest_nice, deltot_jiffies),
+ ll_sp_value(snp->cpu_sys,
+ snc->cpu_sys, deltot_jiffies),
+ ll_sp_value(snp->cpu_iowait,
+ snc->cpu_iowait, deltot_jiffies),
+ ll_sp_value(snp->cpu_hardirq,
+ snc->cpu_hardirq, deltot_jiffies),
+ ll_sp_value(snp->cpu_softirq,
+ snc->cpu_softirq, deltot_jiffies),
+ ll_sp_value(snp->cpu_steal,
+ snc->cpu_steal, deltot_jiffies),
+ ll_sp_value(snp->cpu_guest,
+ snc->cpu_guest, deltot_jiffies),
+ ll_sp_value(snp->cpu_guest_nice,
+ snc->cpu_guest_nice, deltot_jiffies),
+ (snc->cpu_idle < snp->cpu_idle) ?
+ 0.0 :
+ ll_sp_value(snp->cpu_idle,
+ snc->cpu_idle, deltot_jiffies));
+ printf("\n");
}
+}
- for (cpu = 1; cpu <= cpu_nr; cpu++) {
+/*
+ ***************************************************************************
+ * Display CPU statistics for NUMA nodes in JSON format.
+ *
+ * IN:
+ * @tab Number of tabs to print.
+ * @deltot_jiffies
+ * Number of jiffies spent on the interval by all processors.
+ * @prev Position in array where statistics used as reference are.
+ * Stats used as reference may be the previous ones read, or
+ * the very first ones when calculating the average.
+ * @curr Position in array where current statistics will be saved.
+ ***************************************************************************
+ */
+void write_json_node_stats(int tab, unsigned long long deltot_jiffies,
+ int prev, int curr)
+{
+ struct stats_cpu *snc, *snp, *scc, *scp;
+ int cpu, node, next = FALSE;
+ char node_name[16];
- scc = st_cpu[curr] + cpu;
- scp = st_cpu[prev] + cpu;
+ xprintf(tab++, "\"node-load\": [");
- /* Check if we want stats about this proc */
- if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))))
+ for (node = 0; node <= node_nr + 1; node++) {
+
+ snc = st_node[curr] + node;
+ snp = st_node[prev] + node;
+
+ /* Check if we want stats about this node */
+ if (!(*(node_bitmap + (node >> 3)) & (1 << (node & 0x07))))
+ continue;
+
+ if (!cpu_per_node[node])
+ /* No CPU in this node */
continue;
if (next) {
}
next = TRUE;
- /*
- * If the CPU is offline then it is omited from /proc/stat
- * and the sum of all values is zero.
- * (Remember that guest/guest_nice times are already included in
- * user/nice modes.)
- */
- if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
- scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
- scc->cpu_hardirq + scc->cpu_softirq) == 0) {
-
- if (!DISPLAY_ONLINE_CPU(flags)) {
- xprintf0(tab, "{\"cpu\": \"%d\", \"usr\": 0.00, \"nice\": 0.00, "
- "\"sys\": 0.00, \"iowait\": 0.00, \"irq\": 0.00, "
- "\"soft\": 0.00, \"steal\": 0.00, \"guest\": 0.00, "
- "\"gnice\": 0.00, \"idle\": 0.00}", cpu - 1);
- }
- continue;
+ if (node == 0) {
+ /* This is node "all", i.e. CPU "all" */
+ strcpy(node_name, "all");
}
+ else {
+ snprintf(node_name, 16, "%d", node - 1);
+ node_name[15] = '\0';
+
+ /* Recalculate interval for current node */
+ deltot_jiffies = 0;
+ for (cpu = 1; cpu <= cpu_nr; cpu++) {
+ scc = st_cpu[curr] + cpu;
+ scp = st_cpu[prev] + cpu;
+
+ if ((scp->cpu_user + scp->cpu_nice + scp->cpu_sys +
+ scp->cpu_idle + scp->cpu_iowait + scp->cpu_hardirq +
+ scp->cpu_steal + scp->cpu_softirq == 0) && (interval != 0))
+ continue;
+
+ if (cpu2node[cpu - 1] == node - 1) {
+ deltot_jiffies += get_per_cpu_interval(scc, scp);
+ }
+ }
- /* Recalculate itv for current proc */
- pc_itv = get_per_cpu_interval(scc, scp);
+ if (!deltot_jiffies) {
+ /* All CPU in node are tickless and/or offline */
+ xprintf0(tab, "{\"node\": \"%d\", \"usr\": 0.00, \"nice\": 0.00, \"sys\": 0.00, "
+ "\"iowait\": 0.00, \"irq\": 0.00, \"soft\": 0.00, \"steal\": 0.00, "
+ "\"guest\": 0.00, \"gnice\": 0.00, \"idle\": 100.00}", node - 1);
- if (!pc_itv) {
- /*
- * If the CPU is tickless then there is no change in CPU values
- * but the sum of values is not zero.
- */
- xprintf0(tab, "{\"cpu\": \"%d\", \"usr\": 0.00, \"nice\": 0.00, "
- "\"sys\": 0.00, \"iowait\": 0.00, \"irq\": 0.00, "
- "\"soft\": 0.00, \"steal\": 0.00, \"guest\": 0.00, "
- "\"gnice\": 0.00, \"idle\": 100.00}", cpu - 1);
+ continue;
+ }
}
- else {
- xprintf0(tab, "{\"cpu\": \"%d\", \"usr\": %.2f, \"nice\": %.2f, \"sys\": %.2f, "
- "\"iowait\": %.2f, \"irq\": %.2f, \"soft\": %.2f, \"steal\": %.2f, "
- "\"guest\": %.2f, \"gnice\": %.2f, \"idle\": %.2f}", cpu - 1,
- (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
- 0.0 :
- ll_sp_value(scp->cpu_user - scp->cpu_guest,
- scc->cpu_user - scc->cpu_guest,
- pc_itv),
- (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
- 0.0 :
- ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
- scc->cpu_nice - scc->cpu_guest_nice,
- pc_itv),
- ll_sp_value(scp->cpu_sys,
- scc->cpu_sys,
- pc_itv),
- ll_sp_value(scp->cpu_iowait,
- scc->cpu_iowait,
- pc_itv),
- ll_sp_value(scp->cpu_hardirq,
- scc->cpu_hardirq,
- pc_itv),
- ll_sp_value(scp->cpu_softirq,
- scc->cpu_softirq,
- pc_itv),
- ll_sp_value(scp->cpu_steal,
- scc->cpu_steal,
- pc_itv),
- ll_sp_value(scp->cpu_guest,
- scc->cpu_guest,
- pc_itv),
- ll_sp_value(scp->cpu_guest_nice,
- scc->cpu_guest_nice,
- pc_itv),
- (scc->cpu_idle < scp->cpu_idle) ?
- 0.0 :
- ll_sp_value(scp->cpu_idle,
- scc->cpu_idle,
- pc_itv));
- }
+ xprintf0(tab, "{\"node\": \"%s\", \"usr\": %.2f, \"nice\": %.2f, \"sys\": %.2f, "
+ "\"iowait\": %.2f, \"irq\": %.2f, \"soft\": %.2f, \"steal\": %.2f, "
+ "\"guest\": %.2f, \"gnice\": %.2f, \"idle\": %.2f}", node_name,
+ (snc->cpu_user - snc->cpu_guest) < (snp->cpu_user - snp->cpu_guest) ?
+ 0.0 :
+ ll_sp_value(snp->cpu_user - snp->cpu_guest,
+ snc->cpu_user - snc->cpu_guest, deltot_jiffies),
+ (snc->cpu_nice - snc->cpu_guest_nice) < (snp->cpu_nice - snp->cpu_guest_nice) ?
+ 0.0 :
+ ll_sp_value(snp->cpu_nice - snp->cpu_guest_nice,
+ snc->cpu_nice - snc->cpu_guest_nice, deltot_jiffies),
+ ll_sp_value(snp->cpu_sys,
+ snc->cpu_sys, deltot_jiffies),
+ ll_sp_value(snp->cpu_iowait,
+ snc->cpu_iowait, deltot_jiffies),
+ ll_sp_value(snp->cpu_hardirq,
+ snc->cpu_hardirq, deltot_jiffies),
+ ll_sp_value(snp->cpu_softirq,
+ snc->cpu_softirq, deltot_jiffies),
+ ll_sp_value(snp->cpu_steal,
+ snc->cpu_steal, deltot_jiffies),
+ ll_sp_value(snp->cpu_guest,
+ snc->cpu_guest, deltot_jiffies),
+ ll_sp_value(snp->cpu_guest_nice,
+ snc->cpu_guest_nice, deltot_jiffies),
+ (snc->cpu_idle < snp->cpu_idle) ?
+ 0.0 :
+ ll_sp_value(snp->cpu_idle,
+ snc->cpu_idle, deltot_jiffies));
}
printf("\n");
xprintf0(--tab, "]");
/*
***************************************************************************
- * Display CPU statistics in plain or JSON format.
+ * Display nodes statistics in plain or JSON format.
*
* IN:
* @dis TRUE if a header line must be printed.
- * @g_itv Interval value in jiffies multiplied by the number of CPU.
+ * @deltot_jiffies
+ * Number of jiffies spent on the interval by all processors.
* @prev Position in array where statistics used as reference are.
* Stats used as reference may be the previous ones read, or
* the very first ones when calculating the average.
* only).
***************************************************************************
*/
-void write_cpu_stats(int dis, unsigned long long g_itv, int prev, int curr,
- char *prev_string, char *curr_string, int tab, int *next)
+void write_node_stats(int dis, unsigned long long deltot_jiffies, int prev, int curr,
+ char *prev_string, char *curr_string, int tab, int *next)
{
+ if (!deltot_jiffies) {
+ /* CPU "all" cannot be tickless */
+ deltot_jiffies = 1;
+ }
+
if (DISPLAY_JSON_OUTPUT(flags)) {
if (*next) {
printf(",\n");
}
*next = TRUE;
- write_json_cpu_stats(tab, g_itv, prev, curr, curr_string);
+ write_json_node_stats(tab, deltot_jiffies, prev, curr);
}
else {
- write_plain_cpu_stats(dis, g_itv, prev, curr, prev_string, curr_string);
+ write_plain_node_stats(dis, deltot_jiffies, prev, curr,
+ prev_string, curr_string);
}
}
* @curr_string String displayed at the beginning of current sample stats.
* This is the timestamp of the current sample, or "Average"
* when displaying average stats.
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
***************************************************************************
*/
void write_plain_isumcpu_stats(int dis, unsigned long long itv, int prev, int curr,
- char *prev_string, char *curr_string)
+ char *prev_string, char *curr_string, unsigned char offline_cpu_bitmap[])
{
struct stats_cpu *scc, *scp;
struct stats_irq *sic, *sip;
printf("%-11s", curr_string);
cprintf_in(IS_STR, " %s", " all", 0);
/* Print total number of interrupts among all cpu */
- cprintf_f(1, 9, 2,
+ cprintf_f(NO_UNIT, 1, 9, 2,
S_VALUE(st_irq[prev]->irq_nr, st_irq[curr]->irq_nr, itv));
printf("\n");
}
scp = st_cpu[prev] + cpu;
/* Check if we want stats about this CPU */
- if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))))
- continue;
-
- if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
- scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
- scc->cpu_hardirq + scc->cpu_softirq) == 0) {
-
- /* This is an offline CPU */
-
- if (!DISPLAY_ONLINE_CPU(flags)) {
- /*
- * Display offline CPU if requested by the user.
- * Value displayed is 0.00.
- */
- printf("%-11s", curr_string);
- cprintf_in(IS_INT, " %4d", "", cpu - 1);
- cprintf_f(1, 9, 2, 0.0);
- printf("\n");
- }
+ if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))) ||
+ offline_cpu_bitmap[cpu >> 3] & (1 << (cpu & 0x07)))
continue;
- }
printf("%-11s", curr_string);
cprintf_in(IS_INT, " %4d", "", cpu - 1);
if (!pc_itv) {
/* This is a tickless CPU: Value displayed is 0.00 */
- cprintf_f(1, 9, 2, 0.0);
+ cprintf_f(NO_UNIT, 1, 9, 2, 0.0);
printf("\n");
}
else {
/* Display total number of interrupts for current CPU */
- cprintf_f(1, 9, 2,
+ cprintf_f(NO_UNIT, 1, 9, 2,
S_VALUE(sip->irq_nr, sic->irq_nr, itv));
printf("\n");
}
* Stats used as reference may be the previous ones read, or
* the very first ones when calculating the average.
* @curr Position in array where current statistics will be saved.
- * @curr_string String displayed at the beginning of current sample stats.
- * This is the timestamp of the current sample.
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
***************************************************************************
*/
void write_json_isumcpu_stats(int tab, unsigned long long itv, int prev, int curr,
- char *curr_string)
+ unsigned char offline_cpu_bitmap[])
{
struct stats_cpu *scc, *scp;
struct stats_irq *sic, *sip;
scp = st_cpu[prev] + cpu;
/* Check if we want stats about this CPU */
- if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))))
+ if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))) ||
+ offline_cpu_bitmap[cpu >> 3] & (1 << (cpu & 0x07)))
continue;
if (next) {
}
next = TRUE;
- if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
- scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
- scc->cpu_hardirq + scc->cpu_softirq) == 0) {
-
- /* This is an offline CPU */
-
- if (!DISPLAY_ONLINE_CPU(flags)) {
- /*
- * Display offline CPU if requested by the user.
- * Value displayed is 0.00.
- */
- xprintf0(tab, "{\"cpu\": \"%d\", \"intr\": 0.00}",
- cpu - 1);
- }
- continue;
- }
-
/* Recalculate itv for current proc */
pc_itv = get_per_cpu_interval(scc, scp);
* @tab Number of tabs to print (JSON format only).
* @next TRUE is a previous activity has been displayed (JSON format
* only).
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
***************************************************************************
*/
void write_isumcpu_stats(int dis, unsigned long long itv, int prev, int curr,
- char *prev_string, char *curr_string, int tab, int *next)
+ char *prev_string, char *curr_string, int tab, int *next,
+ unsigned char offline_cpu_bitmap[])
{
if (DISPLAY_JSON_OUTPUT(flags)) {
if (*next) {
printf(",\n");
}
*next = TRUE;
- write_json_isumcpu_stats(tab, itv, prev, curr, curr_string);
+ write_json_isumcpu_stats(tab, itv, prev, curr, offline_cpu_bitmap);
}
else {
- write_plain_isumcpu_stats(dis, itv, prev, curr, prev_string, curr_string);
+ write_plain_isumcpu_stats(dis, itv, prev, curr, prev_string, curr_string,
+ offline_cpu_bitmap);
}
}
* @curr_string String displayed at the beginning of current sample stats.
* This is the timestamp of the current sample, or "Average"
* when displaying average stats.
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
***************************************************************************
*/
void write_plain_irqcpu_stats(struct stats_irqcpu *st_ic[], int ic_nr, int dis,
unsigned long long itv, int prev, int curr,
- char *prev_string, char *curr_string)
+ char *prev_string, char *curr_string, unsigned char offline_cpu_bitmap[])
{
- struct stats_cpu *scc;
int j = ic_nr, offset, cpu, colwidth[NR_IRQS];
struct stats_irqcpu *p, *q, *p0, *q0;
for (cpu = 1; cpu <= cpu_nr; cpu++) {
- scc = st_cpu[curr] + cpu;
-
/*
* Check if we want stats about this CPU.
* CPU must have been explicitly selected using option -P,
- * else we display every CPU.
+ * else we display every CPU (unless it's offline).
*/
- if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))) && USE_P_OPTION(flags))
+ if ((!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))) && USE_OPTION_P(flags)) ||
+ offline_cpu_bitmap[cpu >> 3] & (1 << (cpu & 0x07)))
continue;
- if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
- scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
- scc->cpu_hardirq + scc->cpu_softirq) == 0) {
-
- /* Offline CPU found */
-
- if (DISPLAY_ONLINE_CPU(flags))
- continue;
- }
-
printf("%-11s", curr_string);
cprintf_in(IS_INT, " %3d", "", cpu - 1);
if (!strcmp(p0->irq_name, q0->irq_name) || !interval) {
q = st_ic[prev] + (cpu - 1) * ic_nr + offset;
- cprintf_f(1, colwidth[j], 2,
+ cprintf_f(NO_UNIT, 1, colwidth[j], 2,
S_VALUE(q->interrupt, p->interrupt, itv));
}
else {
* Instead of printing "N/A", assume that previous value
* for this new interrupt was zero.
*/
- cprintf_f(1, colwidth[j], 2,
+ cprintf_f(NO_UNIT, 1, colwidth[j], 2,
S_VALUE(0, p->interrupt, itv));
}
}
* Stats used as reference may be the previous ones read, or
* the very first ones when calculating the average.
* @curr Position in array where current statistics will be saved.
- * @curr_string String displayed at the beginning of current sample stats.
- * This is the timestamp of the current sample.
* @type Activity (M_D_IRQ_CPU or M_D_SOFTIRQS).
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
***************************************************************************
*/
void write_json_irqcpu_stats(int tab, struct stats_irqcpu *st_ic[], int ic_nr,
- unsigned long long itv, int prev, int curr,
- char *curr_string, int type)
+ unsigned long long itv, int prev, int curr, int type,
+ unsigned char offline_cpu_bitmap[])
{
- struct stats_cpu *scc;
int j = ic_nr, offset, cpu;
struct stats_irqcpu *p, *q, *p0, *q0;
int nextcpu = FALSE, nextirq;
for (cpu = 1; cpu <= cpu_nr; cpu++) {
- scc = st_cpu[curr] + cpu;
-
/*
* Check if we want stats about this CPU.
* CPU must have been explicitly selected using option -P,
- * else we display every CPU.
+ * else we display every CPU (unless it's offline).
*/
- if (!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))) && USE_P_OPTION(flags))
+ if ((!(*(cpu_bitmap + (cpu >> 3)) & (1 << (cpu & 0x07))) && USE_OPTION_P(flags)) ||
+ offline_cpu_bitmap[cpu >> 3] & (1 << (cpu & 0x07)))
continue;
- if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
- scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
- scc->cpu_hardirq + scc->cpu_softirq) == 0) {
-
- /* Offline CPU found */
-
- if (DISPLAY_ONLINE_CPU(flags))
- continue;
- }
-
if (nextcpu) {
printf(",\n");
}
* @next TRUE is a previous activity has been displayed (JSON format
* only).
* @type Activity (M_D_IRQ_CPU or M_D_SOFTIRQS).
+ * @offline_cpu_bitmap
+ * CPU bitmap for offline CPU.
***************************************************************************
*/
void write_irqcpu_stats(struct stats_irqcpu *st_ic[], int ic_nr, int dis,
unsigned long long itv, int prev, int curr,
char *prev_string, char *curr_string, int tab,
- int *next, int type)
+ int *next, int type, unsigned char offline_cpu_bitmap[])
{
if (DISPLAY_JSON_OUTPUT(flags)) {
if (*next) {
printf(",\n");
}
*next = TRUE;
- write_json_irqcpu_stats(tab, st_ic, ic_nr, itv, prev, curr,
- curr_string, type);
+ write_json_irqcpu_stats(tab, st_ic, ic_nr, itv, prev, curr, type,
+ offline_cpu_bitmap);
}
else {
write_plain_irqcpu_stats(st_ic, ic_nr, dis, itv, prev, curr,
- prev_string, curr_string);
+ prev_string, curr_string, offline_cpu_bitmap);
}
}
void write_stats_core(int prev, int curr, int dis,
char *prev_string, char *curr_string)
{
- struct stats_cpu *scc, *scp;
- unsigned long long itv, g_itv;
- int cpu, tab = 4, next = FALSE;
+ unsigned long long itv, deltot_jiffies = 1;
+ int tab = 4, next = FALSE;
+ unsigned char offline_cpu_bitmap[BITMAP_SIZE(NR_CPUS)] = {0};
/* Test stdout */
TEST_STDOUT(STDOUT_FILENO);
+ /*
+ * Compute CPU "all" as sum of all individual CPU (on SMP machines)
+ * and look for offline CPU.
+ */
+ deltot_jiffies = get_global_cpu_mpstats(prev, curr, offline_cpu_bitmap);
+
if (DISPLAY_JSON_OUTPUT(flags)) {
xprintf(tab++, "{");
xprintf(tab, "\"timestamp\": \"%s\",", curr_string);
}
- /* Compute time interval */
- g_itv = get_interval(uptime[prev], uptime[curr]);
-
- /* Reduce interval value to one processor */
- if (cpu_nr > 1) {
- itv = get_interval(uptime0[prev], uptime0[curr]);
- }
- else {
- itv = g_itv;
- }
+ /* Get time interval */
+ itv = get_interval(uptime_cs[prev], uptime_cs[curr]);
/* Print CPU stats */
if (DISPLAY_CPU(actflags)) {
- write_cpu_stats(dis, g_itv, prev, curr, prev_string, curr_string,
- tab, &next);
+ write_cpu_stats(dis, deltot_jiffies, prev, curr,
+ prev_string, curr_string, tab, &next, offline_cpu_bitmap);
+ }
+
+ /* Print node CPU stats */
+ if (DISPLAY_NODE(actflags)) {
+ set_node_cpu_stats(prev, curr);
+ write_node_stats(dis, deltot_jiffies, prev, curr, prev_string,
+ curr_string, tab, &next);
}
/* Print total number of interrupts per processor */
if (DISPLAY_IRQ_SUM(actflags)) {
write_isumcpu_stats(dis, itv, prev, curr, prev_string, curr_string,
- tab, &next);
+ tab, &next, offline_cpu_bitmap);
}
/* Display each interrupt value for each CPU */
if (DISPLAY_IRQ_CPU(actflags)) {
write_irqcpu_stats(st_irqcpu, irqcpu_nr, dis, itv, prev, curr,
- prev_string, curr_string, tab, &next, M_D_IRQ_CPU);
+ prev_string, curr_string, tab, &next, M_D_IRQ_CPU,
+ offline_cpu_bitmap);
}
if (DISPLAY_SOFTIRQS(actflags)) {
write_irqcpu_stats(st_softirqcpu, softirqcpu_nr, dis, itv, prev, curr,
- prev_string, curr_string, tab, &next, M_D_SOFTIRQS);
+ prev_string, curr_string, tab, &next, M_D_SOFTIRQS,
+ offline_cpu_bitmap);
}
if (DISPLAY_JSON_OUTPUT(flags)) {
printf("\n");
xprintf0(--tab, "}");
}
-
- /* Fix CPU counter values for every offline CPU */
- for (cpu = 1; cpu <= cpu_nr; cpu++) {
-
- scc = st_cpu[curr] + cpu;
- scp = st_cpu[prev] + cpu;
-
- if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
- scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
- scc->cpu_hardirq + scc->cpu_softirq) == 0) {
- /*
- * Offline CPU found.
- * Set current struct fields (which have been set to zero)
- * to values from previous iteration. Hence their values won't
- * jump from zero when the CPU comes back online.
- */
- *scc = *scp;
- }
- }
}
/*
void rw_mpstat_loop(int dis_hdr, int rows)
{
struct stats_cpu *scc;
- int cpu;
+ int i;
int curr = 1, dis = 1;
unsigned long lines = rows;
- /* Dont buffer data if redirected to a pipe */
- setbuf(stdout, NULL);
+ /* Read system uptime and CPU stats */
+ read_uptime(&(uptime_cs[0]));
+ read_stat_cpu(st_cpu[0], cpu_nr + 1);
- /* Read uptime and CPU stats */
+ /*
+ * Calculate global CPU stats as the sum of individual ones.
+ * Done only on SMP machines. On UP machines, we keep the values
+ * read from /proc/stat for global CPU stats.
+ */
if (cpu_nr > 1) {
- /*
- * Init uptime0. So if /proc/uptime cannot fill it,
- * this will be done by /proc/stat.
- */
- uptime0[0] = 0;
- read_uptime(&(uptime0[0]));
+ memset(st_cpu[0], 0, STATS_CPU_SIZE);
+
+ for (i = 1; i <= cpu_nr; i++) {
+ scc = st_cpu[0] + i;
+
+ st_cpu[0]->cpu_user += scc->cpu_user;
+ st_cpu[0]->cpu_nice += scc->cpu_nice;
+ st_cpu[0]->cpu_sys += scc->cpu_sys;
+ st_cpu[0]->cpu_idle += scc->cpu_idle;
+ st_cpu[0]->cpu_iowait += scc->cpu_iowait;
+ st_cpu[0]->cpu_hardirq += scc->cpu_hardirq;
+ st_cpu[0]->cpu_steal += scc->cpu_steal;
+ st_cpu[0]->cpu_softirq += scc->cpu_softirq;
+ st_cpu[0]->cpu_guest += scc->cpu_guest;
+ st_cpu[0]->cpu_guest_nice += scc->cpu_guest_nice;
+ }
+ }
+
+ /* Read system topology */
+ if (DISPLAY_CPU(actflags) && DISPLAY_TOPOLOGY(flags)) {
+ read_topology(cpu_nr, st_cpu_topology);
}
- read_stat_cpu(st_cpu[0], cpu_nr + 1, &(uptime[0]), &(uptime0[0]));
/*
* Read total number of interrupts received among all CPU.
/* Display since boot time */
mp_tstamp[1] = mp_tstamp[0];
memset(st_cpu[1], 0, STATS_CPU_SIZE * (cpu_nr + 1));
+ memset(st_node[1], 0, STATS_CPU_SIZE * (cpu_nr + 1));
memset(st_irq[1], 0, STATS_IRQ_SIZE * (cpu_nr + 1));
memset(st_irqcpu[1], 0, STATS_IRQCPU_SIZE * (cpu_nr + 1) * irqcpu_nr);
if (DISPLAY_SOFTIRQS(actflags)) {
/* Save the first stats collected. Will be used to compute the average */
mp_tstamp[2] = mp_tstamp[0];
- uptime[2] = uptime[0];
- uptime0[2] = uptime0[0];
+ uptime_cs[2] = uptime_cs[0];
memcpy(st_cpu[2], st_cpu[0], STATS_CPU_SIZE * (cpu_nr + 1));
+ memcpy(st_node[2], st_node[0], STATS_CPU_SIZE * (cpu_nr + 1));
memcpy(st_irq[2], st_irq[0], STATS_IRQ_SIZE * (cpu_nr + 1));
memcpy(st_irqcpu[2], st_irqcpu[0], STATS_IRQCPU_SIZE * (cpu_nr + 1) * irqcpu_nr);
if (DISPLAY_SOFTIRQS(actflags)) {
STATS_IRQCPU_SIZE * (cpu_nr + 1) * softirqcpu_nr);
}
- if (!DISPLAY_JSON_OUTPUT(flags)) {
- /* Set a handler for SIGINT */
- memset(&int_act, 0, sizeof(int_act));
- int_act.sa_handler = int_handler;
- sigaction(SIGINT, &int_act, NULL);
- }
+ /* Set a handler for SIGINT */
+ memset(&int_act, 0, sizeof(int_act));
+ int_act.sa_handler = int_handler;
+ sigaction(SIGINT, &int_act, NULL);
- pause();
+ __pause();
if (sigint_caught)
/* SIGINT signal caught during first interval: Exit immediately */
* if corresponding processor is disabled (offline). We set them to zero
* to be able to distinguish between offline and tickless CPUs.
*/
- for (cpu = 1; cpu <= cpu_nr; cpu++) {
- scc = st_cpu[curr] + cpu;
- memset(scc, 0, STATS_CPU_SIZE);
- }
+ memset(st_cpu[curr], 0, STATS_CPU_SIZE * (cpu_nr + 1));
/* Get time */
get_localtime(&(mp_tstamp[curr]), 0);
/* Read uptime and CPU stats */
- if (cpu_nr > 1) {
- uptime0[curr] = 0;
- read_uptime(&(uptime0[curr]));
+ read_uptime(&(uptime_cs[curr]));
+ read_stat_cpu(st_cpu[curr], cpu_nr + 1);
+
+ /* Read system topology */
+ if (DISPLAY_CPU(actflags) && DISPLAY_TOPOLOGY(flags)) {
+ read_topology(cpu_nr, st_cpu_topology);
}
- read_stat_cpu(st_cpu[curr], cpu_nr + 1, &(uptime[curr]), &(uptime0[curr]));
/* Read total number of interrupts received among all CPU */
if (DISPLAY_IRQ_SUM(actflags)) {
if (count) {
- if (DISPLAY_JSON_OUTPUT(flags)) {
- printf(",\n");
- }
- pause();
+ __pause();
if (sigint_caught) {
/* SIGINT signal caught => Display average stats */
count = 0;
- printf("\n"); /* Skip "^C" displayed on screen */
}
else {
+ if (DISPLAY_JSON_OUTPUT(flags)) {
+ printf(",\n");
+ }
curr ^= 1;
}
}
*/
salloc_mp_struct(cpu_nr + 1);
- while (++opt < argc) {
+ /* Get NUMA node placement */
+ node_nr = get_node_placement(cpu_nr, cpu_per_node, cpu2node);
- if (!strcmp(argv[opt], "-I")) {
- if (argv[++opt]) {
- actset = TRUE;
+ while (++opt < argc) {
- for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
- if (!strcmp(t, K_SUM)) {
- /* Display total number of interrupts per CPU */
- actflags |= M_D_IRQ_SUM;
- }
- else if (!strcmp(t, K_CPU)) {
- /* Display interrupts per CPU */
- actflags |= M_D_IRQ_CPU;
- }
- else if (!strcmp(t, K_SCPU)) {
- /* Display soft interrupts per CPU */
- actflags |= M_D_SOFTIRQS;
- }
- else if (!strcmp(t, K_ALL)) {
- actflags |= M_D_IRQ_SUM + M_D_IRQ_CPU + M_D_SOFTIRQS;
- }
- else {
- usage(argv[0]);
- }
- }
+ if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
+ /* Get number of decimal places */
+ dplaces_nr = atoi(argv[opt] + 6);
+ if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
+ usage(argv[0]);
}
- else {
+ }
+
+ else if (!strcmp(argv[opt], "-I")) {
+ if (!argv[++opt]) {
usage(argv[0]);
}
+ actset = TRUE;
+
+ for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
+ if (!strcmp(t, K_SUM)) {
+ /* Display total number of interrupts per CPU */
+ actflags |= M_D_IRQ_SUM;
+ }
+ else if (!strcmp(t, K_CPU)) {
+ /* Display interrupts per CPU */
+ actflags |= M_D_IRQ_CPU;
+ }
+ else if (!strcmp(t, K_SCPU)) {
+ /* Display soft interrupts per CPU */
+ actflags |= M_D_SOFTIRQS;
+ }
+ else if (!strcmp(t, K_ALL)) {
+ actflags |= M_D_IRQ_SUM + M_D_IRQ_CPU + M_D_SOFTIRQS;
+ }
+ else {
+ usage(argv[0]);
+ }
+ }
}
else if (!strcmp(argv[opt], "-o")) {
}
}
+ else if (!strcmp(argv[opt], "-N")) {
+ if (!argv[++opt]) {
+ usage(argv[0]);
+ }
+ if (node_nr >= 0) {
+ flags |= F_OPTION_N;
+ actflags |= M_D_NODE;
+ actset = TRUE;
+ dis_hdr = 9;
+ if (parse_values(argv[opt], node_bitmap, node_nr + 1, K_LOWERALL)) {
+ usage(argv[0]);
+ }
+ }
+ }
+
else if (!strcmp(argv[opt], "-P")) {
/* '-P ALL' can be used on UP machines */
- if (argv[++opt]) {
- flags |= F_P_OPTION;
- dis_hdr++;
-
- for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
- if (!strcmp(t, K_ALL) || !strcmp(t, K_ON)) {
- if (cpu_nr) {
- dis_hdr = 9;
- }
- /*
- * Set bit for every processor.
- * Also indicate to display stats for CPU 'all'.
- */
- memset(cpu_bitmap, 0xff, ((cpu_nr + 1) >> 3) + 1);
- if (!strcmp(t, K_ON)) {
- /* Display stats only for online CPU */
- flags |= F_P_ON;
- }
- }
- else {
- if (strspn(t, DIGITS) != strlen(t)) {
- usage(argv[0]);
- }
- i = atoi(t); /* Get cpu number */
- if (i >= cpu_nr) {
- fprintf(stderr, _("Not that many processors!\n"));
- exit(1);
- }
- i++;
- *(cpu_bitmap + (i >> 3)) |= 1 << (i & 0x07);
- }
- }
+ if (!argv[++opt]) {
+ usage(argv[0]);
}
- else {
+ flags |= F_OPTION_P;
+ dis_hdr = 9;
+
+ if (parse_values(argv[opt], cpu_bitmap, cpu_nr, K_LOWERALL)) {
usage(argv[0]);
}
}
switch (*(argv[opt] + i)) {
case 'A':
+ flags |= F_OPTION_A;
actflags |= M_D_CPU + M_D_IRQ_SUM + M_D_IRQ_CPU + M_D_SOFTIRQS;
+ if (node_nr >= 0) {
+ actflags |= M_D_NODE;
+ }
actset = TRUE;
- /* Select all processors */
- flags |= F_P_OPTION;
- memset(cpu_bitmap, 0xff, ((cpu_nr + 1) >> 3) + 1);
+ break;
+
+ case 'n':
+ /* Display CPU stats based on NUMA node placement */
+ if (node_nr >= 0) {
+ actflags |= M_D_NODE;
+ actset = TRUE;
+ }
+ break;
+
+ case 'T':
+ /* Display logical topology */
+ flags |= F_TOPOLOGY;
break;
case 'u':
}
}
- /* Default: Display CPU */
- if (!actset) {
+ /* Default: Display CPU (e.g., "mpstat", "mpstat -P 1", "mpstat -P 1 -n", "mpstat -P 1 -N 1"... */
+ if (!actset ||
+ (USE_OPTION_P(flags) && !(actflags & ~M_D_NODE))) {
actflags |= M_D_CPU;
}
dis_hdr = 9;
}
- if (!USE_P_OPTION(flags)) {
+ if (USE_OPTION_A(flags)) {
+ /*
+ * Set -P ALL -N ALL only if individual CPU and/or nodes
+ * have not been selected.
+ */
+ if ((node_nr >= 0) && !USE_OPTION_N(flags)) {
+ memset(node_bitmap, ~0, ((cpu_nr + 1) >> 3) + 1);
+ flags += F_OPTION_N;
+ }
+ if (!USE_OPTION_P(flags)) {
+ memset(cpu_bitmap, ~0, ((cpu_nr + 1) >> 3) + 1);
+ flags += F_OPTION_P;
+ }
+ }
+
+ if (!USE_OPTION_P(flags)) {
/* Option -P not used: Set bit 0 (global stats among all proc) */
*cpu_bitmap = 1;
}
+ if (!USE_OPTION_N(flags)) {
+ /* Option -N not used: Set bit 0 (global stats among all nodes) */
+ *node_bitmap = 1;
+ }
if (dis_hdr < 0) {
dis_hdr = 0;
}
/* Get time */
get_localtime(&(mp_tstamp[0]), 0);
+ /*
+ * Don't buffer data if redirected to a pipe.
+ * Note: With musl-c, the behavior of this function is undefined except
+ * when it is the first operation on the stream.
+ */
+ setbuf(stdout, NULL);
+
/* Get system name, release number and hostname */
- uname(&header);
+ __uname(&header);
print_gal_header(&(mp_tstamp[0]), header.sysname, header.release,
header.nodename, header.machine, get_cpu_nr(~0, FALSE),
DISPLAY_JSON_OUTPUT(flags));