From: Sebastien GODARD Date: Fri, 23 Mar 2018 11:14:49 +0000 (+0100) Subject: mpstat: Compute stats for node "all" as the sum of individual CPU X-Git-Tag: v11.7.3~11 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e291af0761c674089ff42f7dc583ea50d60e9827;p=sysstat mpstat: Compute stats for node "all" as the sum of individual CPU Don't use global CPU stats from /proc/stat file for node "all". Compute stats for node "all" as the sum of individual ones. Also better handle CPUs that go offline or back online. Signed-off-by: Sebastien GODARD --- diff --git a/mpstat.c b/mpstat.c index 43f8bea..41dde2c 100644 --- a/mpstat.c +++ b/mpstat.c @@ -48,7 +48,6 @@ char *sccsid(void) { return (SCCSID); } #endif -unsigned long long tot_jiffies[3] = {0, 0, 0}; unsigned long long uptime_cs[3] = {0, 0, 0}; /* NOTE: Use array of _char_ for bitmaps to avoid endianness problems...*/ @@ -74,7 +73,9 @@ struct stats_irqcpu *st_softirqcpu[3]; /* * Number of CPU per node, e.g.: - * cpu_per_node[0]: nr of CPU for node 0.. + * 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; @@ -104,6 +105,7 @@ 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; @@ -285,10 +287,13 @@ int get_node_placement(int cpu_nr, int cpu_per_node[], int cpu2node[]) int cpu, node, node_nr = -1; /* Init number of CPU per node */ - memset(cpu_per_node, 0, sizeof(int) * cpu_nr); + memset(cpu_per_node, 0, sizeof(int) * (cpu_nr + 1)); /* CPU belongs to no node by default */ memset(cpu2node, -1, sizeof(int) * cpu_nr); + /* This is node "all" */ + cpu_per_node[0] = cpu_nr; + for (cpu = 0; cpu < cpu_nr; cpu++) { snprintf(line, MAX_PF_NAME, "%s/cpu%d", SYSFS_DEVCPU, cpu); line[MAX_PF_NAME - 1] = '\0'; @@ -307,7 +312,7 @@ int get_node_placement(int cpu_nr, int cpu_per_node[], int cpu2node[]) closedir(dir); return -1; } - cpu_per_node[node]++; + cpu_per_node[node + 1]++; cpu2node[cpu] = node; if (node > node_nr) { node_nr = node; @@ -329,32 +334,77 @@ int get_node_placement(int cpu_nr, int cpu_per_node[], int cpu2node[]) * Compute node statistics: Split CPU statistics among nodes. * * IN: - * @st_cpu Array where current CPU stats have been read. + * @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(struct stats_cpu *st_node, struct stats_cpu *st_cpu) +void set_node_cpu_stats(int prev, int curr) { int cpu; - struct stats_cpu *st_cpu_i, *st_node_i; + 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++) { - /* Don't store stats for node 'all'. They are the same as CPU 'all' */ - st_cpu_i = st_cpu + cpu + 1; - st_node_i = st_node + cpu2node[cpu] + 1; - - st_node_i->cpu_user += st_cpu_i->cpu_user; - st_node_i->cpu_nice += st_cpu_i->cpu_nice; - st_node_i->cpu_sys += st_cpu_i->cpu_sys; - st_node_i->cpu_idle += st_cpu_i->cpu_idle; - st_node_i->cpu_iowait += st_cpu_i->cpu_iowait; - st_node_i->cpu_hardirq += st_cpu_i->cpu_hardirq; - st_node_i->cpu_softirq += st_cpu_i->cpu_softirq; - st_node_i->cpu_steal += st_cpu_i->cpu_steal; - st_node_i->cpu_guest += st_cpu_i->cpu_guest; - st_node_i->cpu_guest_nice += st_cpu_i->cpu_guest_nice; + 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); } } @@ -397,6 +447,10 @@ unsigned long long get_global_cpu_mpstats(int prev, int curr, 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++) { @@ -451,7 +505,8 @@ unsigned long long get_global_cpu_mpstats(int prev, int curr, * 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 stats since boot time. + * 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; @@ -463,35 +518,8 @@ unsigned long long get_global_cpu_mpstats(int prev, int curr, */ deltot_jiffies += get_per_cpu_interval(scc, scp); - scc_all->cpu_user += scc->cpu_user; - scp_all->cpu_user += scp->cpu_user; - - scc_all->cpu_nice += scc->cpu_nice; - scp_all->cpu_nice += scp->cpu_nice; - - scc_all->cpu_sys += scc->cpu_sys; - scp_all->cpu_sys += scp->cpu_sys; - - scc_all->cpu_idle += scc->cpu_idle; - scp_all->cpu_idle += scp->cpu_idle; - - scc_all->cpu_iowait += scc->cpu_iowait; - scp_all->cpu_iowait += scp->cpu_iowait; - - scc_all->cpu_hardirq += scc->cpu_hardirq; - scp_all->cpu_hardirq += scp->cpu_hardirq; - - scc_all->cpu_steal += scc->cpu_steal; - scp_all->cpu_steal += scp->cpu_steal; - - scc_all->cpu_softirq += scc->cpu_softirq; - scp_all->cpu_softirq += scp->cpu_softirq; - - scc_all->cpu_guest += scc->cpu_guest; - scp_all->cpu_guest += scp->cpu_guest; - - scc_all->cpu_guest_nice += scc->cpu_guest_nice; - scp_all->cpu_guest_nice += scp->cpu_guest_nice; + add_cpu_stats(scc_all, scc); + add_cpu_stats(scp_all, scp); } return deltot_jiffies; @@ -503,6 +531,8 @@ unsigned long long get_global_cpu_mpstats(int prev, int curr, * * 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. @@ -513,15 +543,15 @@ unsigned long long get_global_cpu_mpstats(int prev, int curr, * @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, 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 deltot_jiffies = 1; - unsigned char offline_cpu_bitmap[BITMAP_SIZE(NR_CPUS)] = {0}; if (dis) { printf("\n%-11s CPU %%usr %%nice %%sys %%iowait %%irq " @@ -529,14 +559,6 @@ void write_plain_cpu_stats(int dis, int prev, int curr, prev_string); } - /* - * Compute CPU "all" as sum of all individual CPU (on SMP machines) - * and look for offline CPU. - */ - if (cpu_nr > 1) { - deltot_jiffies = get_global_cpu_mpstats(prev, curr, offline_cpu_bitmap); - } - /* * Now display CPU statistics (including CPU "all"), * except for offline CPU or CPU that the user doesn't want to see. @@ -556,14 +578,6 @@ void write_plain_cpu_stats(int dis, int prev, int curr, if (i == 0) { /* This is CPU "all" */ cprintf_in(IS_STR, " %s", " all", 0); - - if (cpu_nr == 1) { - /* - * This is a UP machine. In this case - * interval has still not been calculated. - */ - deltot_jiffies = get_per_cpu_interval(scc, scp); - } } else { cprintf_in(IS_INT, " %4d", "", i - 1); @@ -621,29 +635,24 @@ void write_plain_cpu_stats(int dis, int prev, int curr, * * 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, int prev, int curr) +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; struct stats_cpu *scc, *scp; - unsigned long long deltot_jiffies = 1; - unsigned char offline_cpu_bitmap[BITMAP_SIZE(NR_CPUS)] = {0}; xprintf(tab++, "\"cpu-load\": ["); - /* - * Compute CPU "all" as sum of all individual CPU (on SMP machines) - * and look for offline CPU. - */ - if (cpu_nr > 1) { - deltot_jiffies = get_global_cpu_mpstats(prev, curr, offline_cpu_bitmap); - } - /* * Now display CPU statistics (including CPU "all"), * except for offline CPU or CPU that the user doesn't want to see. @@ -663,17 +672,7 @@ void write_json_cpu_stats(int tab, int prev, int curr) } next = TRUE; - if (i == 0) { - - if (cpu_nr == 1) { - /* - * This is a UP machine. In this case - * interval has still not been calculated. - */ - deltot_jiffies = get_per_cpu_interval(scc, scp); - } - } - else { + if (i != 0) { /* Recalculate itv for current proc */ deltot_jiffies = get_per_cpu_interval(scc, scp); @@ -733,6 +732,8 @@ void write_json_cpu_stats(int tab, int prev, int curr) * * 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. @@ -746,20 +747,25 @@ void write_json_cpu_stats(int tab, int prev, int curr) * @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, int prev, int curr, char *prev_string, - char *curr_string, int tab, int *next) +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 (DISPLAY_JSON_OUTPUT(flags)) { if (*next) { printf(",\n"); } *next = TRUE; - write_json_cpu_stats(tab, prev, curr); + write_json_cpu_stats(tab, deltot_jiffies, prev, curr, + offline_cpu_bitmap); } else { - write_plain_cpu_stats(dis, prev, curr, prev_string, curr_string); + write_plain_cpu_stats(dis, deltot_jiffies, prev, curr, + prev_string, curr_string, offline_cpu_bitmap); } } @@ -771,7 +777,6 @@ void write_cpu_stats(int dis, int prev, int curr, char *prev_string, * @dis TRUE if a header line must be printed. * @deltot_jiffies * Number of jiffies spent on the interval by all processors. - * @itv Interval value in 1/100th of a second. * @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. @@ -784,11 +789,11 @@ void write_cpu_stats(int dis, int prev, int curr, char *prev_string, * when displaying average stats. *************************************************************************** */ -void write_plain_node_stats(int dis, unsigned long long deltot_jiffies, unsigned long long itv, +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 *snc, *snp; - int node; + struct stats_cpu *snc, *snp, *scc, *scp; + int cpu, node; if (dis) { printf("\n%-11s NODE %%usr %%nice %%sys %%iowait %%irq " @@ -796,64 +801,13 @@ void write_plain_node_stats(int dis, unsigned long long deltot_jiffies, unsigned prev_string); } - /* - * Check if we want global stats among all nodes. - * Stats are the same as global CPU stats among all processors. - */ - if (*node_bitmap & 1) { + for (node = 0; node <= node_nr + 1; node++) { - printf("%-11s", curr_string); - cprintf_in(IS_STR, " %s", " all", 0); - - cprintf_pc(NO_UNIT, 10, 7, 2, - (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, - deltot_jiffies), - (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, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_sys, - st_cpu[curr]->cpu_sys, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_iowait, - st_cpu[curr]->cpu_iowait, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_hardirq, - st_cpu[curr]->cpu_hardirq, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_softirq, - st_cpu[curr]->cpu_softirq, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_steal, - st_cpu[curr]->cpu_steal, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_guest, - st_cpu[curr]->cpu_guest, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_guest_nice, - st_cpu[curr]->cpu_guest_nice, - deltot_jiffies), - (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, - deltot_jiffies)); - printf("\n"); - } - - for (node = 0; node <= node_nr; node++) { - - snc = st_node[curr] + node + 1; - snp = st_node[prev] + node + 1; + snc = st_node[curr] + node; + snp = st_node[prev] + node; /* Check if we want stats about this node */ - if (!(*(node_bitmap + ((node + 1) >> 3)) & (1 << ((node + 1) & 0x07)))) + if (!(*(node_bitmap + (node >> 3)) & (1 << (node & 0x07)))) continue; if (!cpu_per_node[node]) @@ -861,45 +815,66 @@ void write_plain_node_stats(int dis, unsigned long long deltot_jiffies, unsigned continue; printf("%-11s", curr_string); - cprintf_in(IS_INT, " %4d", "", node); + 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, - itv * HZ / 100 * cpu_per_node[node]), + 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, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_nice - snc->cpu_guest_nice, deltot_jiffies), ll_sp_value(snp->cpu_sys, - snc->cpu_sys, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_sys, deltot_jiffies), ll_sp_value(snp->cpu_iowait, - snc->cpu_iowait, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_iowait, deltot_jiffies), ll_sp_value(snp->cpu_hardirq, - snc->cpu_hardirq, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_hardirq, deltot_jiffies), ll_sp_value(snp->cpu_softirq, - snc->cpu_softirq, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_softirq, deltot_jiffies), ll_sp_value(snp->cpu_steal, - snc->cpu_steal, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_steal, deltot_jiffies), ll_sp_value(snp->cpu_guest, - snc->cpu_guest, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_guest, deltot_jiffies), ll_sp_value(snp->cpu_guest_nice, - snc->cpu_guest_nice, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_guest_nice, deltot_jiffies), (snc->cpu_idle < snp->cpu_idle) ? 0.0 : ll_sp_value(snp->cpu_idle, - snc->cpu_idle, - itv * HZ / 100 * cpu_per_node[node])); + snc->cpu_idle, deltot_jiffies)); printf("\n"); } } @@ -912,75 +887,28 @@ void write_plain_node_stats(int dis, unsigned long long deltot_jiffies, unsigned * @tab Number of tabs to print. * @deltot_jiffies * Number of jiffies spent on the interval by all processors. - * @itv Interval value. * @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, unsigned long long itv, +void write_json_node_stats(int tab, unsigned long long deltot_jiffies, int prev, int curr) { - struct stats_cpu *snc, *snp; - int node, next = FALSE; + struct stats_cpu *snc, *snp, *scc, *scp; + int cpu, node, next = FALSE; + char node_name[16]; xprintf(tab++, "\"node-load\": ["); - /* Check if we want global stats among all nodes */ - if (*node_bitmap & 1) { + for (node = 0; node <= node_nr + 1; node++) { - next = TRUE; - xprintf0(tab, "{\"node\": \"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, - deltot_jiffies), - (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, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_sys, - st_cpu[curr]->cpu_sys, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_iowait, - st_cpu[curr]->cpu_iowait, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_hardirq, - st_cpu[curr]->cpu_hardirq, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_softirq, - st_cpu[curr]->cpu_softirq, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_steal, - st_cpu[curr]->cpu_steal, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_guest, - st_cpu[curr]->cpu_guest, - deltot_jiffies), - ll_sp_value(st_cpu[prev]->cpu_guest_nice, - st_cpu[curr]->cpu_guest_nice, - deltot_jiffies), - (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, - deltot_jiffies)); - } - - for (node = 0; node <= node_nr; node++) { - - snc = st_node[curr] + node + 1; - snp = st_node[prev] + node + 1; + snc = st_node[curr] + node; + snp = st_node[prev] + node; /* Check if we want stats about this node */ - if (!(*(node_bitmap + ((node + 1) >> 3)) & (1 << ((node + 1) & 0x07)))) + if (!(*(node_bitmap + (node >> 3)) & (1 << (node & 0x07)))) continue; if (!cpu_per_node[node]) @@ -992,45 +920,69 @@ void write_json_node_stats(int tab, unsigned long long deltot_jiffies, unsigned } next = TRUE; - xprintf0(tab, "{\"node\": \"%d\", \"usr\": %.2f, \"nice\": %.2f, \"sys\": %.2f, " + 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); + } + } + + 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); + + continue; + } + } + + 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, + "\"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, - itv * HZ / 100 * cpu_per_node[node]), + 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, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_nice - snc->cpu_guest_nice, deltot_jiffies), ll_sp_value(snp->cpu_sys, - snc->cpu_sys, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_sys, deltot_jiffies), ll_sp_value(snp->cpu_iowait, - snc->cpu_iowait, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_iowait, deltot_jiffies), ll_sp_value(snp->cpu_hardirq, - snc->cpu_hardirq, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_hardirq, deltot_jiffies), ll_sp_value(snp->cpu_softirq, - snc->cpu_softirq, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_softirq, deltot_jiffies), ll_sp_value(snp->cpu_steal, - snc->cpu_steal, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_steal, deltot_jiffies), ll_sp_value(snp->cpu_guest, - snc->cpu_guest, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_guest, deltot_jiffies), ll_sp_value(snp->cpu_guest_nice, - snc->cpu_guest_nice, - itv * HZ / 100 * cpu_per_node[node]), + snc->cpu_guest_nice, deltot_jiffies), (snc->cpu_idle < snp->cpu_idle) ? 0.0 : ll_sp_value(snp->cpu_idle, - snc->cpu_idle, - itv * HZ / 100 * cpu_per_node[node])); + snc->cpu_idle, deltot_jiffies)); } printf("\n"); xprintf0(--tab, "]"); @@ -1044,7 +996,6 @@ void write_json_node_stats(int tab, unsigned long long deltot_jiffies, unsigned * @dis TRUE if a header line must be printed. * @deltot_jiffies * Number of jiffies spent on the interval by all processors. - * @itv Interval value. * @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. @@ -1060,19 +1011,18 @@ void write_json_node_stats(int tab, unsigned long long deltot_jiffies, unsigned * only). *************************************************************************** */ -void write_node_stats(int dis, unsigned long long deltot_jiffies, unsigned long long 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 (DISPLAY_JSON_OUTPUT(flags)) { if (*next) { printf(",\n"); } *next = TRUE; - write_json_node_stats(tab, deltot_jiffies, itv, prev, curr); + write_json_node_stats(tab, deltot_jiffies, prev, curr); } else { - write_plain_node_stats(dis, deltot_jiffies, itv, prev, curr, + write_plain_node_stats(dis, deltot_jiffies, prev, curr, prev_string, curr_string); } } @@ -1625,42 +1575,37 @@ void write_irqcpu_stats(struct stats_irqcpu *st_ic[], int ic_nr, int dis, 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, deltot_jiffies; - 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 the total number of jiffies spent by all processors. - * NB: Don't add cpu_guest/cpu_guest_nice because cpu_user/cpu_nice - * already include them. - */ - tot_jiffies[curr] = st_cpu[curr]->cpu_user + st_cpu[curr]->cpu_nice + - st_cpu[curr]->cpu_sys + st_cpu[curr]->cpu_idle + - st_cpu[curr]->cpu_iowait + st_cpu[curr]->cpu_hardirq + - st_cpu[curr]->cpu_steal + st_cpu[curr]->cpu_softirq; - /* Total number of jiffies spent on the interval */ - deltot_jiffies = get_interval(tot_jiffies[prev], tot_jiffies[curr]); - /* Get time interval */ itv = get_interval(uptime_cs[prev], uptime_cs[curr]); /* Print CPU stats */ if (DISPLAY_CPU(actflags)) { - write_cpu_stats(dis, 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)) { - write_node_stats(dis, deltot_jiffies, itv, prev, curr, prev_string, + set_node_cpu_stats(prev, curr); + write_node_stats(dis, deltot_jiffies, prev, curr, prev_string, curr_string, tab, &next); } @@ -1684,25 +1629,6 @@ void write_stats_core(int prev, int curr, int dis, 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; - } - } } /* @@ -1870,7 +1796,7 @@ void read_interrupts_stat(char *file, struct stats_irqcpu *st_ic[], int ic_nr, i 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; @@ -1880,12 +1806,29 @@ void rw_mpstat_loop(int dis_hdr, int rows) /* Read system uptime and CPU stats */ read_uptime(&(uptime_cs[0])); read_stat_cpu(st_cpu[0], cpu_nr + 1); - tot_jiffies[0] = st_cpu[0]->cpu_user + st_cpu[0]->cpu_nice + - st_cpu[0]->cpu_sys + st_cpu[0]->cpu_idle + - st_cpu[0]->cpu_iowait + st_cpu[0]->cpu_hardirq + - st_cpu[0]->cpu_steal + st_cpu[0]->cpu_softirq; - if (DISPLAY_NODE(actflags)) { - set_node_cpu_stats(st_node[0], st_cpu[0]); + + /* + * 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) { + 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; + } } /* @@ -1933,7 +1876,6 @@ void rw_mpstat_loop(int dis_hdr, int rows) /* Save the first stats collected. Will be used to compute the average */ mp_tstamp[2] = mp_tstamp[0]; - tot_jiffies[2] = tot_jiffies[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)); @@ -1964,12 +1906,7 @@ void rw_mpstat_loop(int dis_hdr, int rows) * 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); - scc = st_node[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); @@ -1977,9 +1914,6 @@ void rw_mpstat_loop(int dis_hdr, int rows) /* Read uptime and CPU stats */ read_uptime(&(uptime_cs[curr])); read_stat_cpu(st_cpu[curr], cpu_nr + 1); - if (DISPLAY_NODE(actflags)) { - set_node_cpu_stats(st_node[curr], st_cpu[curr]); - } /* Read total number of interrupts received among all CPU */ if (DISPLAY_IRQ_SUM(actflags)) {