On Monday 19 July 2010 21:40, Marek Polacek wrote: > Thank you for your review, it was very valuable.
Hey Marek, let's be less formal. :D My "very valuable" input was also very late :( Sorry. > > memcpy(buf, "Average:", 16); > > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > > After ":" you are copying garbage into buf[] > > > > buf[15] = '\0'; > > write_stats_core(2, current, buf, buf); > > } > What I've done was very wrong. I should have used memccpy() or maybe > strncpy() (I'm not a big fan of str*() functions though). str*() functions are not bad at all. Really. Some are even inlined by compiler. > But I've > solved it differently: I've added const qualifiers to char * arguments > in functions write_irqcpu_stats() and write_stats_core(). And into > write_stats_avg() I've added line (memcpy is gone): > > static const char buf[] = "Average:"; > > it's static, so we have exactly one copy and it is read-only. _If_ I'm > not wrong, this way it'll go into .rodata. Yes, it will go to .rodata > If we would omit keyword > static, it would be allocated (generally) on stack or TLS. Where do we > want it to go to? I am very curious about this. static const char buf[] = "Average:" is ok. > > /* > > * Compute uptime in jiffies (1/HZ), multiply with > > * number of CPUs. > > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > > Where is the" multiply" part? I don't see it. > "Multiply" is maybe inappropriate word. In fact it means, that it is the > sum of individual CPU's uptimes. Ok, where is the sum operator then? The comment says one thing and the code does something a bit different. This is wrong. Comment seems to be unclear. > I am sending version with (hopefully) corrected write_stats_core(). Let's see. It's getting better, but still a lot to do. ll_sp_value() is such a "great" function name it makes the reader want to strangle the author. :D Variable names like "itv" are also bad. So, with non-informative names like that, you end up with code like this: /* Compute interval again for current proc */ pc_itv = get_per_cpu_interval(scc, scp); if (!pc_itv) printf(" %9.2f\n", 0.0); else printf(" %9.2f\n", ll_s_value(sip->irq_nr, sic->irq_nr, itv)); See anything strange? Why do we pass *itv* to ll_s_value? Shall we pass *pc_itv* instead? /* Compute time interval */ itv = g_itv = jiffies_diff(G.uptime[prev], G.uptime[current]); /* Reduce interval to one CPU */ if (G.cpu_nr > 1) itv = jiffies_diff(G.uptime0[prev], G.uptime0[current]); What is the difference between G.uptime and G.uptime0? Can we give them better names (ones which forestall questions like that)? q0 = per_cpu_stats[prev] + offset; if (strcmp(p0->irq_name, q0->irq_name) != 0 && (j + 1 < total_irqs) ) { offset = j + 1; } q0 = per_cpu_stats[prev] + offset; Why do we recompute q0 _unconditionally_? Please find updated applet attached. function old new delta write_stats_core 844 1290 +446 percent_value - 88 +88 starts_with_cpu - 31 +31 write_irqcpu_stats 597 588 -9 get_cpu_statistics 350 333 -17 mpstat_main 1620 1596 -24 write_stats 128 99 -29 get_irqs_from_interrupts 576 519 -57 ll_sp_value 115 - -115 ll_s_value 127 - -127 print_stats_cpu_struct 363 - -363 ------------------------------------------------------------------------------ (add/remove: 2/3 grow/shrink: 1/5 up/down: 565/-741) Total: -176 bytes Please look over the changes - I might break something... Please rename [foo_]itv variables so that they are understandable. > You've changed > > #define debug(fmt, ...) /* Nothing */ > to > #define debug(fmt, ...) ((void)0) > > -- is this just a cosmetics change, or there are situations, where would > it actually make a difference? Well, in most real-world cases it does not matter, but here is a contrived example with "comma expression" thing: return (debug("returning 1\n"), 1); gcc will not like this: return (/* Nothing */, 1); > BTW: "if ((buf[0] - 'c') | (buf[1] - 'p') | (buf[2] - 'u'))" -- this is > excellent :-)) Not sure about it being so great... I just hope it's the smallest code, need to check it. -- vda
/* vi: set sw=4 ts=4: */ /* * Per-processor statistics, based on sysstat version 9.1.2 by Sebastien Godard * * Copyright (C) 2010 Marek Polacek <mmpola...@gmail.com> * * Licensed under GPLv2, see file License in this tarball for details. */ //applet:IF_MPSTAT(APPLET(mpstat, _BB_DIR_BIN, _BB_SUID_DROP)) //kbuild:lib-$(CONFIG_MPSTAT) += mpstat.o //config:config MPSTAT //config: bool "mpstat" //config: default y //config: help //config: Per-processor statistics #include "libbb.h" #include <sys/utsname.h> /* struct utsname */ //#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) #define debug(fmt, ...) ((void)0) /* Size of /proc/interrupts line, CPU data excluded */ #define INTERRUPTS_LINE 64 /* Maximum number of interrupts */ #define NR_IRQS 256 #define NR_IRQCPU_PREALLOC 3 #define MAX_IRQ_LEN 16 #define MAX_PF_NAME 512 /* System files */ #define SYSFS_DEVCPU "/sys/devices/system/cpu" #define PROCFS_STAT "/proc/stat" #define PROCFS_INTERRUPTS "/proc/interrupts" #define PROCFS_SOFTIRQS "/proc/softirqs" #define PROCFS_UPTIME "/proc/uptime" #if 1 typedef unsigned long long data_t; typedef long long idata_t; #define FMT_DATA "ll" #else typedef unsigned long data_t; typedef long idata_t; #define FMT_DATA "l" #endif struct stats_irqcpu { unsigned interrupt; char irq_name[MAX_IRQ_LEN]; }; /* Structure for CPU statistics */ struct stats_cpu { data_t cpu_user; data_t cpu_nice; data_t cpu_system; data_t cpu_idle; data_t cpu_iowait; data_t cpu_steal; data_t cpu_irq; data_t cpu_softirq; data_t cpu_guest; }; /* Struct for interrupts statistics */ struct stats_irq { data_t irq_nr; }; /* Globals. Try to sort by size. */ struct globals { int interval; int count; unsigned cpu_nr; /* Number of CPUs */ unsigned irqcpu_nr; /* Number of interrupts per CPU */ unsigned softirqcpu_nr; /* Number of soft interrupts per CPU */ unsigned options; unsigned hz; unsigned cpu_bitmap_len; smallint p_option; smallint header_done; smallint avg_header_done; unsigned char *cpu_bitmap; /* Bit 0: global, bit 1: 1st proc... */ data_t uptime[3]; data_t uptime0[3]; struct stats_cpu *st_cpu[3]; struct stats_irq *st_irq[3]; struct stats_irqcpu *st_irqcpu[3]; struct stats_irqcpu *st_softirqcpu[3]; struct tm timestamp[3]; }; #define G (*ptr_to_globals) #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ } while (0) /* The selected interrupts statistics (bits in G.options) */ enum { D_CPU = 1 << 0, D_IRQ_SUM = 1 << 1, D_IRQ_CPU = 1 << 2, D_SOFTIRQS = 1 << 3, }; /* Does str start with "cpu"? */ static int starts_with_cpu(const char *str) { return !((str[0] - 'c') | (str[1] - 'p') | (str[2] - 'u')); } /* Is option on? */ static ALWAYS_INLINE int display_opt(int opt) { return (opt & G.options); } /* * Handle overflow conditions properly for counters which can be * unsigned long long or unsigned long, depending on the kernel version. */ static double percent_value(data_t prev, data_t curr, data_t itv) { data_t v = curr - prev; if (sizeof(data_t) > 32 && (idata_t)v < 0 /* counter overflow? */ && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */ ) { /* double shift defeats "warning: left shift count >= width of type" */ v += ((data_t)1 << 16) << 16; } return (double)v / itv * 100; } static double hz_value(data_t prev, data_t curr, data_t itv) { data_t v = curr - prev; if (sizeof(data_t) > 32 && (idata_t)v < 0 /* counter overflow? */ && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */ ) { v += ((data_t)1 << 16) << 16; } return (double)v / itv * G.hz; } static ALWAYS_INLINE data_t jiffies_diff(data_t old, data_t new) { data_t diff = new - old; return (diff == 0) ? 1 : diff; } static int is_cpu_in_bitmap(unsigned cpu) { return G.cpu_bitmap[cpu >> 3] & (1 << (cpu & 7)); } static void write_irqcpu_stats(struct stats_irqcpu *per_cpu_stats[], int total_irqs, data_t itv, int prev, int current, const char *prev_str, const char *current_str) { int j; int offset, cpu; struct stats_irqcpu *p0, *q0; /* Check if number of IRQs has changed */ if (G.interval != 0) { for (j = 0; j <= total_irqs; j++) { p0 = &per_cpu_stats[current][j]; if (p0->irq_name[0] != '\0') { q0 = &per_cpu_stats[prev][j]; if (strcmp(p0->irq_name, q0->irq_name) != 0) { /* Strings are different */ break; } } } } /* Print header */ printf("\n%-11s CPU", prev_str); for (j = 0; j < total_irqs; j++) { p0 = &per_cpu_stats[current][j]; if (p0->irq_name[0] != '\0') printf(" %8s/s", p0->irq_name); } bb_putchar('\n'); for (cpu = 1; cpu <= G.cpu_nr; cpu++) { /* Check if we want stats about this CPU */ if (!is_cpu_in_bitmap(cpu) && G.p_option) { continue; } printf("%-11s %4u", current_str, cpu - 1); for (j = 0; j < total_irqs; j++) { /* IRQ field set only for proc 0 */ p0 = &per_cpu_stats[current][j]; /* * An empty string for irq name means that * interrupt is no longer used. */ if (p0->irq_name[0] != '\0') { q0 = &per_cpu_stats[prev][j]; offset = j; /* * If we want stats for the time since boot * we have p0->irq != q0->irq. */ if (strcmp(p0->irq_name, q0->irq_name) != 0 && G.interval != 0 ) { if (j) offset = j - 1; q0 = &per_cpu_stats[prev][offset]; if (strcmp(p0->irq_name, q0->irq_name) != 0 && (j + 1 < total_irqs) ) { offset = j + 1; q0 = &per_cpu_stats[prev][offset]; } } if (strcmp(p0->irq_name, q0->irq_name) == 0 || G.interval == 0 ) { struct stats_irqcpu *p, *q; p = &per_cpu_stats[current][(cpu - 1) * total_irqs + j]; q = &per_cpu_stats[prev][(cpu - 1) * total_irqs + offset]; printf(" %10.2f", (double)(p->interrupt - q->interrupt) / itv * G.hz); } else { printf(" N/A"); } } } bb_putchar('\n'); } } static data_t get_per_cpu_interval(struct stats_cpu *scc, struct stats_cpu *scp) { return ((scc->cpu_user + scc->cpu_nice + scc->cpu_system + scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal + scc->cpu_irq + scc->cpu_softirq) - (scp->cpu_user + scp->cpu_nice + scp->cpu_system + scp->cpu_iowait + scp->cpu_idle + scp->cpu_steal + scp->cpu_irq + scp->cpu_softirq)); } static void print_stats_cpu_struct(struct stats_cpu *p, struct stats_cpu *c, data_t itv) { printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", percent_value(p->cpu_user - p->cpu_guest, /**/ c->cpu_user - c->cpu_guest, itv), percent_value(p->cpu_nice , c->cpu_nice , itv), percent_value(p->cpu_system , c->cpu_system , itv), percent_value(p->cpu_iowait , c->cpu_iowait , itv), percent_value(p->cpu_irq , c->cpu_irq , itv), percent_value(p->cpu_softirq, c->cpu_softirq, itv), percent_value(p->cpu_steal , c->cpu_steal , itv), percent_value(p->cpu_guest , c->cpu_guest , itv), /// Why this one is different? (c->cpu_idle < p->cpu_idle) ? 0.0 : percent_value(p->cpu_idle, c->cpu_idle, itv) ); } static void write_stats_core(int prev, int current, const char *prev_str, const char *current_str) { struct stats_cpu *scc, *scp; data_t itv, g_itv; int cpu; /* Compute time interval */ itv = g_itv = jiffies_diff(G.uptime[prev], G.uptime[current]); /* Reduce interval to one CPU */ if (G.cpu_nr > 1) itv = jiffies_diff(G.uptime0[prev], G.uptime0[current]); /* Print CPU stats */ if (display_opt(D_CPU)) { /* This is done exactly once */ if (!G.header_done) { printf("\n%-11s CPU %%usr %%nice %%sys %%iowait %%irq %%soft %%steal %%guest %%idle\n", prev_str ); G.header_done = 1; } for (cpu = 0; cpu <= G.cpu_nr; cpu++) { data_t pc_itv; /* Print stats about this particular CPU? */ if (!is_cpu_in_bitmap(cpu)) continue; scc = &G.st_cpu[current][cpu]; scp = &G.st_cpu[prev][cpu]; pc_itv = g_itv; printf((cpu ? "%-11s %4u" : "%-11s all"), current_str, cpu - 1); if (cpu) { double idle; /* * If the CPU is offline, then it isn't in /proc/stat, * so all values are 0. * NB: Guest time is in user mode. ///maybe "Guest time is in user TIME"? */ if ((scc->cpu_user | scc->cpu_nice | scc->cpu_system | scc->cpu_iowait | scc->cpu_idle | scc->cpu_steal | scc->cpu_irq | scc->cpu_softirq) == 0 ) { /* * Set current struct fields to values from prev. * iteration. Then their values won't jump from * zero, when the CPU comes back online. */ *scc = *scp; idle = 0.0; goto print_zeros; } /* Compute interval again for current proc */ pc_itv = get_per_cpu_interval(scc, scp); if (pc_itv == 0) { /* * If the CPU is tickless then there is no change in CPU values * but the sum of values is not zero. */ idle = 100.0; print_zeros: printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, idle); continue; } } print_stats_cpu_struct(scp, scc, pc_itv); } } /* Print total number of IRQs per CPU */ if (display_opt(D_IRQ_SUM)) { /* Print average header, this is done exactly once */ if (!G.avg_header_done) { printf("\n%-11s CPU intr/s\n", prev_str); G.avg_header_done = 1; } for (cpu = 0; cpu <= G.cpu_nr; cpu++) { data_t pc_itv; /* Print stats about this CPU? */ if (!is_cpu_in_bitmap(cpu)) continue; pc_itv = itv; printf((cpu ? "%-11s %4u" : "%-11s all"), current_str, cpu - 1); if (cpu) { scc = &G.st_cpu[current][cpu]; scp = &G.st_cpu[prev][cpu]; /* Compute interval again for current proc */ pc_itv = get_per_cpu_interval(scc, scp); if (pc_itv == 0) { printf(" %9.2f\n", 0.0); continue; } } printf(" %9.2f\n", hz_value(G.st_irq[prev][cpu].irq_nr, G.st_irq[current][cpu].irq_nr, pc_itv)); } } if (display_opt(D_IRQ_CPU)) { write_irqcpu_stats(G.st_irqcpu, G.irqcpu_nr, itv, prev, current, prev_str, current_str ); } if (display_opt(D_SOFTIRQS)) { write_irqcpu_stats(G.st_softirqcpu, G.softirqcpu_nr, itv, prev, current, prev_str, current_str ); } } /* * Print the statistics */ static void write_stats(int current) { char prev_time[16]; char curr_time[16]; strftime(prev_time, sizeof(prev_time), "%X", &G.timestamp[!current]); strftime(curr_time, sizeof(curr_time), "%X", &G.timestamp[current]); write_stats_core(!current, current, prev_time, curr_time); } static void write_stats_avg(int current) { write_stats_core(2, current, "Average:", "Average:"); } /* * Read CPU statistics */ static void get_cpu_statistics(struct stats_cpu *cpu, data_t *up, data_t *up0) { FILE *fp; char buf[1024]; fp = xfopen_for_read(PROCFS_STAT); while (fgets(buf, sizeof(buf), fp)) { data_t sum; unsigned cpu_number; struct stats_cpu *cp; if (!starts_with_cpu(buf)) continue; /* not "cpu" */ if (buf[3] == ' ') { /* "cpu " */ cp = cpu; } else { /* "cpuN" */ if (G.cpu_nr == 0 || sscanf(buf + 3, "%u ", &cpu_number) != 1 || cpu_number >= G.cpu_nr ) { continue; } cp = &cpu[cpu_number + 1]; } /* Read the jiffies, save them */ /* Not all fields have to be present */ memset(cp, 0, sizeof(*cp)); sscanf(skip_non_whitespace(buf + 3), " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u" " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u" " %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u", &cp->cpu_user, &cp->cpu_nice, &cp->cpu_system, &cp->cpu_idle, &cp->cpu_iowait, &cp->cpu_irq, &cp->cpu_softirq, &cp->cpu_steal, &cp->cpu_guest ); /* * Compute uptime in jiffies (1/HZ), it'll be the sum of * individual CPU's uptimes. * NB: We have to omit cpu_guest, because cpu_user includes it. */ sum = cp->cpu_user + cp->cpu_nice + cp->cpu_system + cp->cpu_idle + cp->cpu_iowait + cp->cpu_irq + cp->cpu_softirq + cp->cpu_steal; if (buf[3] == ' ') { /* "cpu " */ *up = sum; } else { /* "cpuN" */ if (cpu_number == 0 && *up0 != 0) { /* Compute uptime of single CPU */ *up0 = sum; } } } fclose(fp); } /* * Read IRQs from /proc/stat */ static void get_irqs_from_stat(struct stats_irq *irq) { FILE *fp; char buf[1024]; fp = fopen_for_read(PROCFS_STAT); if (!fp) return; while (fgets(buf, sizeof(buf), fp)) { if (strncmp(buf, "intr ", 5) == 0) /* Read total number of IRQs since system boot */ sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr); } fclose(fp); } /* * Read stats from /proc/interrupts or /proc/softirqs */ static void get_irqs_from_interrupts(const char *fname, struct stats_irqcpu *per_cpu_stats[], int irqs_per_cpu, int current) { FILE *fp; struct stats_irq *irq_i; struct stats_irqcpu *ic; char *buf; unsigned buflen; unsigned cpu; unsigned irq; int cpu_index[G.cpu_nr]; int iindex; int len, digit; for (cpu = 1; cpu <= G.cpu_nr; cpu++) { irq_i = &G.st_irq[current][cpu]; irq_i->irq_nr = 0; } fp = fopen_for_read(fname); if (!fp) return; buflen = INTERRUPTS_LINE + 11 * G.cpu_nr; buf = xmalloc(buflen); /* Parse header and determine, which CPUs are online */ iindex = 0; while (fgets(buf, buflen, fp)) { char *cp, *next; next = buf; while ((cp = strstr(next, "CPU")) != NULL && iindex < G.cpu_nr ) { cpu = strtoul(cp + 3, &next, 10); cpu_index[iindex++] = cpu; } if (iindex) /* We found header */ break; } irq = 0; while (fgets(buf, buflen, fp) && irq < irqs_per_cpu ) { char *cp; /* Skip over "<irq>:" */ cp = strchr(buf, ':'); if (!cp) continue; ic = &per_cpu_stats[current][irq]; len = cp - buf; if (len > sizeof(ic->irq_name)) { len = sizeof(ic->irq_name); } safe_strncpy(ic->irq_name, buf, len); digit = isdigit(buf[len - 1]); cp++; for (cpu = 0; cpu < iindex; cpu++) { char *next; ic = &per_cpu_stats[current][cpu_index[cpu] * irqs_per_cpu + irq]; irq_i = &G.st_irq[current][cpu_index[cpu] + 1]; ic->interrupt = strtoul(cp, &next, 10); if (digit) { /* Do not count non-numerical IRQs */ irq_i->irq_nr += ic->interrupt; } cp = next; } irq++; } fclose(fp); free(buf); while (irq < irqs_per_cpu) { /* Number of interrupts per CPU has changed */ ic = &per_cpu_stats[current][irq]; ic->irq_name[0] = '\0'; /* False interrupt */ irq++; } } static void get_uptime(data_t *uptime) { FILE *fp; char buf[32]; /* 32 is enough for unsigned long.unsigned long */ unsigned long uptime_sec, decimal; fp = fopen_for_read(PROCFS_UPTIME); if (!fp) return; if (fgets(buf, 32, fp)) { if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) { *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100; } } fclose(fp); } static void get_localtime(struct tm *tm) { time_t timer; time(&timer); localtime_r(&timer, tm); } static void alarm_handler(int sig UNUSED_PARAM) { signal(SIGALRM, alarm_handler); alarm(G.interval); } static void main_loop(void) { unsigned current; unsigned cpus; /* Read the stats */ if (G.cpu_nr > 1) { G.uptime0[0] = 0; get_uptime(&G.uptime0[0]); } /* Get CPU stats */ get_cpu_statistics(G.st_cpu[0], &G.uptime[0], &G.uptime0[0]); if (display_opt(D_IRQ_SUM)) get_irqs_from_stat(G.st_irq[0]); if (display_opt(D_IRQ_SUM | D_IRQ_CPU)) get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu, G.irqcpu_nr, 0); if (display_opt(D_SOFTIRQS)) get_irqs_from_interrupts(PROCFS_SOFTIRQS, G.st_softirqcpu, G.softirqcpu_nr, 0); if (G.interval == 0) { /* Display since boot time */ cpus = G.cpu_nr + 1; G.timestamp[1] = G.timestamp[0]; memset(G.st_cpu[1], 0, sizeof(G.st_cpu[1][0]) * cpus); memset(G.st_irq[1], 0, sizeof(G.st_irq[1][0]) * cpus); memset(G.st_irqcpu[1], 0, sizeof(G.st_irqcpu[1][0]) * cpus * G.irqcpu_nr); memset(G.st_softirqcpu[1], 0, sizeof(G.st_softirqcpu[1][0]) * cpus * G.softirqcpu_nr); write_stats(0); /* And we're done */ return; } /* Set a handler for SIGALRM */ alarm_handler(0); /* Save the stats we already have. We need them to compute the average */ G.timestamp[2] = G.timestamp[0]; G.uptime[2] = G.uptime[0]; G.uptime0[2] = G.uptime0[0]; cpus = G.cpu_nr + 1; memcpy(G.st_cpu[2], G.st_cpu[0], sizeof(G.st_cpu[0][0]) * cpus); memcpy(G.st_irq[2], G.st_irq[0], sizeof(G.st_irq[0][0]) * cpus); memcpy(G.st_irqcpu[2], G.st_irqcpu[0], sizeof(G.st_irqcpu[0][0]) * cpus * G.irqcpu_nr); if (display_opt(D_SOFTIRQS)) { memcpy(G.st_softirqcpu[2], G.st_softirqcpu[0], sizeof(G.st_softirqcpu[0][0]) * cpus * G.softirqcpu_nr); } current = 1; while (1) { /* Suspend until a signal is received */ pause(); /* Set structures to 0 to distinguish off/online CPUs */ memset(&G.st_cpu[current][/*cpu:*/ 1], 0, sizeof(G.st_cpu[0][0]) * G.cpu_nr); get_localtime(&G.timestamp[current]); /* Read stats */ if (G.cpu_nr > 1) { G.uptime0[current] = 0; get_uptime(&G.uptime0[current]); } get_cpu_statistics(G.st_cpu[current], &G.uptime[current], &G.uptime0[current]); if (display_opt(D_IRQ_SUM)) get_irqs_from_stat(G.st_irq[current]); if (display_opt(D_IRQ_SUM | D_IRQ_CPU)) get_irqs_from_interrupts(PROCFS_INTERRUPTS, G.st_irqcpu, G.irqcpu_nr, current); if (display_opt(D_SOFTIRQS)) get_irqs_from_interrupts(PROCFS_SOFTIRQS, G.st_softirqcpu, G.softirqcpu_nr, current); write_stats(current); if (G.count > 0) { if (--G.count == 0) break; } current ^= 1; } /* Print average statistics */ write_stats_avg(current); } /* Initialization */ /* Get number of clock ticks per sec */ static ALWAYS_INLINE unsigned get_hz(void) { return sysconf(_SC_CLK_TCK); } static void alloc_struct(int cpus) { int i; for (i = 0; i < 3; i++) { G.st_cpu[i] = xzalloc(sizeof(G.st_cpu[i][0]) * cpus); G.st_irq[i] = xzalloc(sizeof(G.st_irq[i][0]) * cpus); G.st_irqcpu[i] = xzalloc(sizeof(G.st_irqcpu[i][0]) * cpus * G.irqcpu_nr); G.st_softirqcpu[i] = xzalloc(sizeof(G.st_softirqcpu[i][0]) * cpus * G.softirqcpu_nr); } G.cpu_bitmap_len = (cpus >> 3) + 1; G.cpu_bitmap = xzalloc(G.cpu_bitmap_len); } static void print_header(struct tm *t, char *sysname, char *release, char *nodename, char *machine, int cpus) { char cur_date[16]; strftime(cur_date, sizeof(cur_date), "%x", t); printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n", sysname, release, nodename, cur_date, machine, cpus); } /* * Get number of processors in /sys */ static int get_sys_cpu_nr(void) { DIR *dir; struct dirent *d; struct stat buf; char line[MAX_PF_NAME]; int proc_nr = 0; /* Open the SYSFS_DEVCPU directory */ dir = opendir(SYSFS_DEVCPU); if (!dir) return 0; /* /sys not mounted */ /* Get current file entry */ while ((d = readdir(dir)) != NULL) { if (starts_with_cpu(d->d_name) && isdigit(d->d_name[3])) { snprintf(line, MAX_PF_NAME, "%s/%s", SYSFS_DEVCPU, d->d_name); line[MAX_PF_NAME - 1] = '\0'; /* Get information about file */ if (stat(line, &buf) < 0) continue; /* If found 'cpuN', we have one more processor */ if (S_ISDIR(buf.st_mode)) proc_nr++; } } closedir(dir); return proc_nr; } /* * Get number of processors in /proc/stat * Return value '0' means one CPU and non SMP kernel. * Otherwise N means N processor(s) and SMP kernel. */ static int get_proc_cpu_nr(void) { FILE *fp; char line[256]; int proc_nr = -1; fp = xfopen_for_read(PROCFS_STAT); while (fgets(line, sizeof(line), fp)) { if (!starts_with_cpu(line)) { if (proc_nr >= 0) break; /* we are past "cpuN..." lines */ continue; } if (line[3] != ' ') { /* "cpuN" */ int num_proc; if (sscanf(line + 3, "%u", &num_proc) == 1 && num_proc > proc_nr ) { proc_nr = num_proc; } } } fclose(fp); return proc_nr + 1; } static int get_cpu_nr(void) { int n; /* Try to use /sys, if possible */ n = get_sys_cpu_nr(); if (n == 0) /* Otherwise use /proc/stat */ n = get_proc_cpu_nr(); return n; } /* * Get number of interrupts available per processor */ static int get_irqcpu_nr(const char *f, int max_irqs) { FILE *fp; char *line = NULL; unsigned irq = 0; int p; fp = fopen_for_read(f); if (!fp) /* No interrupts file */ return 0; line = xmalloc(INTERRUPTS_LINE + 11 * G.cpu_nr); while (fgets(line, INTERRUPTS_LINE + 11 * G.cpu_nr, fp) && irq < max_irqs ) { p = strcspn(line, ":"); if ((p > 0) && (p < 16)) { irq++; } } fclose(fp); free(line); return irq; } //usage:#define mpstat_trivial_usage //usage: "[-A] [-I SUM|CPU|ALL|SCPU] [-u] [-P num|ALL] [INTERVAL [COUNT]]" //usage:#define mpstat_full_usage "\n\n" //usage: "Per-processor statistics\n" //usage: "\nOptions:" //usage: "\n -A Same as -I ALL -u -P ALL" //usage: "\n -I SUM|CPU|ALL|SCPU Report interrupt statistics" //usage: "\n -P num|ALL Processor to monitor" //usage: "\n -u Report CPU utilization" int mpstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int mpstat_main(int UNUSED_PARAM argc, char **argv) { struct utsname name; char *opt_irq_fmt; char *opt_set_cpu; int i, opt; enum { OPT_ALL = 1 << 0, /* -A */ OPT_INTS = 1 << 1, /* -I */ OPT_SETCPU = 1 << 2, /* -P */ OPT_UTIL = 1 << 3, /* -u */ }; /* Dont buffer data if redirected to a pipe */ setbuf(stdout, NULL); INIT_G(); G.interval = -1; /* Get number of processors */ G.cpu_nr = get_cpu_nr(); /* Get number of clock ticks per sec */ G.hz = get_hz(); /* Calculate number of interrupts per processor */ G.irqcpu_nr = get_irqcpu_nr(PROCFS_INTERRUPTS, NR_IRQS) + NR_IRQCPU_PREALLOC; /* Calculate number of soft interrupts per processor */ G.softirqcpu_nr = get_irqcpu_nr(PROCFS_SOFTIRQS, NR_IRQS) + NR_IRQCPU_PREALLOC; /* Allocate space for structures. + 1 for global structure. */ alloc_struct(G.cpu_nr + 1); /* Parse and process arguments */ opt = getopt32(argv, "AI:P:u", &opt_irq_fmt, &opt_set_cpu); argv += optind; if (*argv) { /* Get interval */ G.interval = xatoi_u(*argv); G.count = -1; argv++; if (*argv) { /* Get count value */ if (G.interval == 0) bb_show_usage(); G.count = xatoi_u(*argv); //if (*++argv) // bb_show_usage(); } } if (G.interval < 0) G.interval = 0; if (opt & OPT_ALL) { G.p_option = 1; G.options |= D_CPU + D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS; /* Select every CPU */ memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len); } if (opt & OPT_INTS) { if (strcmp(opt_irq_fmt, "ALL") == 0) G.options |= D_IRQ_SUM + D_IRQ_CPU + D_SOFTIRQS; else if (strcmp(opt_irq_fmt, "CPU") == 0) G.options |= D_IRQ_CPU; else if (strcmp(opt_irq_fmt, "SUM") == 0) G.options |= D_IRQ_SUM; else if (strcmp(opt_irq_fmt, "SCPU") == 0) G.options |= D_SOFTIRQS; else bb_show_usage(); } if ((opt & OPT_UTIL) /* -u? */ || G.options == 0 /* nothing? (use default then) */ ) { G.options |= D_CPU; } if (opt & OPT_SETCPU) { char *t; G.p_option = 1; for (t = strtok(opt_set_cpu, ","); t; t = strtok(NULL, ",")) { if (strcmp(t, "ALL") == 0) { /* Select every CPU */ memset(G.cpu_bitmap, 0xff, G.cpu_bitmap_len); } else { /* Get CPU number */ unsigned n = xatoi_u(t); if (n >= G.cpu_nr) bb_error_msg_and_die("not that many processors"); n++; G.cpu_bitmap[n >> 3] |= 1 << (n & 7); } } } if (!G.p_option) /* Display global stats */ G.cpu_bitmap[0] = 1; /* Get time */ get_localtime(&G.timestamp[0]); /* Get system name, release number and hostname */ uname(&name); /* Display header */ print_header(&G.timestamp[0], name.sysname, name.release, name.nodename, name.machine, G.cpu_nr); /* The main loop */ main_loop(); if (ENABLE_FEATURE_CLEAN_UP) { /* Clean up */ for (i = 0; i < 3; i++) { free(G.st_cpu[i]); free(G.st_irq[i]); free(G.st_irqcpu[i]); free(G.st_softirqcpu[i]); } free(G.cpu_bitmap); free(&G); } return EXIT_SUCCESS; }
--- mpstat.cOLD +++ mpstat.c @@ -38,9 +38,18 @@ #define PROCFS_SOFTIRQS "/proc/softirqs" #define PROCFS_UPTIME "/proc/uptime" + +#if 1 typedef unsigned long long data_t; +typedef long long idata_t; #define FMT_DATA "ll" +#else +typedef unsigned long data_t; +typedef long idata_t; +#define FMT_DATA "l" +#endif + struct stats_irqcpu { unsigned interrupt; char irq_name[MAX_IRQ_LEN]; @@ -64,6 +73,7 @@ data_t irq_nr; }; + /* Globals. Try to sort by size. */ struct globals { int interval; @@ -78,8 +88,8 @@ smallint header_done; smallint avg_header_done; unsigned char *cpu_bitmap; /* Bit 0: global, bit 1: 1st proc... */ - unsigned long long uptime[3]; - unsigned long long uptime0[3]; + data_t uptime[3]; + data_t uptime0[3]; struct stats_cpu *st_cpu[3]; struct stats_irq *st_irq[3]; struct stats_irqcpu *st_irqcpu[3]; @@ -100,6 +110,12 @@ }; +/* Does str start with "cpu"? */ +static int starts_with_cpu(const char *str) +{ + return !((str[0] - 'c') | (str[1] - 'p') | (str[2] - 'u')); +} + /* Is option on? */ static ALWAYS_INLINE int display_opt(int opt) { @@ -110,31 +126,35 @@ * Handle overflow conditions properly for counters which can be * unsigned long long or unsigned long, depending on the kernel version. */ -#define SP_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * 100) -#define S_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * G.hz) - -static double ll_sp_value(unsigned long long val1, unsigned long long val2, - unsigned long long itv) +static double percent_value(data_t prev, data_t curr, data_t itv) { - if ((val2 < val1) && (val1 <= 0xffffffff)) - /* Counter's type was unsigned long and has overflown */ - return ((double)((val2 - val1) & 0xffffffff)) / itv * 100; - return SP_VALUE(val1, val2, itv); -} + data_t v = curr - prev; -static double ll_s_value(unsigned long long val1, unsigned long long val2, - unsigned long long itv) + if (sizeof(data_t) > 32 + && (idata_t)v < 0 /* counter overflow? */ + && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */ + ) { + /* double shift defeats "warning: left shift count >= width of type" */ + v += ((data_t)1 << 16) << 16; + } + return (double)v / itv * 100; +} +static double hz_value(data_t prev, data_t curr, data_t itv) { - if ((val2 < val1) && (val1 <= 0xffffffff)) - /* Counter's type was unsigned long and has overflown */ - return ((double)((val2 - val1) & 0xffffffff)) / itv * G.hz; - return S_VALUE(val1, val2, itv); + data_t v = curr - prev; + + if (sizeof(data_t) > 32 + && (idata_t)v < 0 /* counter overflow? */ + && prev <= 0xffffffff /* kernel uses 32bit value for the counter? */ + ) { + v += ((data_t)1 << 16) << 16; + } + return (double)v / itv * G.hz; } -static ALWAYS_INLINE unsigned long long jiffies_diff(unsigned long long old, - unsigned long long new) +static ALWAYS_INLINE data_t jiffies_diff(data_t old, data_t new) { - unsigned long long diff = new - old; + data_t diff = new - old; return (diff == 0) ? 1 : diff; } @@ -143,52 +163,38 @@ return G.cpu_bitmap[cpu >> 3] & (1 << (cpu & 7)); } -static unsigned long long get_per_cpu_interval(struct stats_cpu *scc, - struct stats_cpu *scp) -{ - return ((scc->cpu_user + scc->cpu_nice + - scc->cpu_system + scc->cpu_iowait + - scc->cpu_idle + scc->cpu_steal + - scc->cpu_irq + scc->cpu_softirq) - - (scp->cpu_user + scp->cpu_nice + - scp->cpu_system + scp->cpu_iowait + - scp->cpu_idle + scp->cpu_steal + - scp->cpu_irq + scp->cpu_softirq)); -} - static void write_irqcpu_stats(struct stats_irqcpu *per_cpu_stats[], int total_irqs, - unsigned long long itv, int prev, int current, + data_t itv, + int prev, int current, const char *prev_str, const char *current_str) { int j; int offset, cpu; - struct stats_irqcpu *p, *q, *p0, *q0; + struct stats_irqcpu *p0, *q0; /* Check if number of IRQs has changed */ if (G.interval != 0) { - j = 0; - do { - p0 = per_cpu_stats[current] + j; + for (j = 0; j <= total_irqs; j++) { + p0 = &per_cpu_stats[current][j]; if (p0->irq_name[0] != '\0') { - q0 = per_cpu_stats[prev] + j; + q0 = &per_cpu_stats[prev][j]; if (strcmp(p0->irq_name, q0->irq_name) != 0) { /* Strings are different */ break; } } - j++; - } while (j <= total_irqs); + } } /* Print header */ printf("\n%-11s CPU", prev_str); for (j = 0; j < total_irqs; j++) { - p0 = per_cpu_stats[current] + j; + p0 = &per_cpu_stats[current][j]; if (p0->irq_name[0] != '\0') printf(" %8s/s", p0->irq_name); } - printf("\n"); + bb_putchar('\n'); for (cpu = 1; cpu <= G.cpu_nr; cpu++) { /* Check if we want stats about this CPU */ @@ -196,18 +202,18 @@ continue; } - printf("%-11s %3d", current_str, cpu - 1); + printf("%-11s %4u", current_str, cpu - 1); for (j = 0; j < total_irqs; j++) { /* IRQ field set only for proc 0 */ - p0 = per_cpu_stats[current] + j; + p0 = &per_cpu_stats[current][j]; /* - * An empty string for irq name means, that - * interrupt is no longer used + * An empty string for irq name means that + * interrupt is no longer used. */ if (p0->irq_name[0] != '\0') { - q0 = per_cpu_stats[prev] + j; + q0 = &per_cpu_stats[prev][j]; offset = j; /* @@ -217,54 +223,63 @@ if (strcmp(p0->irq_name, q0->irq_name) != 0 && G.interval != 0 ) { - if (j) { + if (j) offset = j - 1; - } - q0 = per_cpu_stats[prev] + offset; + q0 = &per_cpu_stats[prev][offset]; if (strcmp(p0->irq_name, q0->irq_name) != 0 && (j + 1 < total_irqs) ) { offset = j + 1; + q0 = &per_cpu_stats[prev][offset]; } - q0 = per_cpu_stats[prev] + offset; } if (strcmp(p0->irq_name, q0->irq_name) == 0 || G.interval == 0 ) { - p = per_cpu_stats[current] + (cpu - 1) * total_irqs + j; - q = per_cpu_stats[prev] + (cpu - 1) * total_irqs + offset; + struct stats_irqcpu *p, *q; + p = &per_cpu_stats[current][(cpu - 1) * total_irqs + j]; + q = &per_cpu_stats[prev][(cpu - 1) * total_irqs + offset]; printf(" %10.2f", - S_VALUE(q->interrupt, p->interrupt, itv)); + (double)(p->interrupt - q->interrupt) / itv * G.hz); } else { printf(" N/A"); } } } - printf("\n"); + bb_putchar('\n'); } } -static void print_stats_cpu_struct(struct stats_cpu *a, - struct stats_cpu *b, - unsigned long long itv) +static data_t get_per_cpu_interval(struct stats_cpu *scc, struct stats_cpu *scp) { - if (!a || !b) - return; + return ((scc->cpu_user + scc->cpu_nice + + scc->cpu_system + scc->cpu_iowait + + scc->cpu_idle + scc->cpu_steal + + scc->cpu_irq + scc->cpu_softirq) - + (scp->cpu_user + scp->cpu_nice + + scp->cpu_system + scp->cpu_iowait + + scp->cpu_idle + scp->cpu_steal + + scp->cpu_irq + scp->cpu_softirq)); +} - printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f\n", - ll_sp_value(a->cpu_user - a->cpu_guest, - b->cpu_user - b->cpu_guest, itv), - ll_sp_value(a->cpu_nice, b->cpu_nice, itv), - ll_sp_value(a->cpu_system, b->cpu_system, itv), - ll_sp_value(a->cpu_iowait, b->cpu_iowait, itv), - ll_sp_value(a->cpu_irq, b->cpu_irq, itv), - ll_sp_value(a->cpu_softirq, b->cpu_softirq, itv), - ll_sp_value(a->cpu_steal, b->cpu_steal, itv), - ll_sp_value(a->cpu_guest, b->cpu_guest, itv), - (b->cpu_idle < a->cpu_idle) ? 0.0 : - ll_sp_value(a->cpu_idle, b->cpu_idle, itv) - ); +static void print_stats_cpu_struct(struct stats_cpu *p, struct stats_cpu *c, + data_t itv) +{ + printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", + percent_value(p->cpu_user - p->cpu_guest, + /**/ c->cpu_user - c->cpu_guest, itv), + percent_value(p->cpu_nice , c->cpu_nice , itv), + percent_value(p->cpu_system , c->cpu_system , itv), + percent_value(p->cpu_iowait , c->cpu_iowait , itv), + percent_value(p->cpu_irq , c->cpu_irq , itv), + percent_value(p->cpu_softirq, c->cpu_softirq, itv), + percent_value(p->cpu_steal , c->cpu_steal , itv), + percent_value(p->cpu_guest , c->cpu_guest , itv), +/// Why this one is different? + (c->cpu_idle < p->cpu_idle) ? 0.0 : + percent_value(p->cpu_idle, c->cpu_idle, itv) + ); } static void write_stats_core(int prev, int current, @@ -272,84 +287,80 @@ const char *current_str) { struct stats_cpu *scc, *scp; - unsigned long long itv, pc_itv, g_itv; + data_t itv, g_itv; int cpu; /* Compute time interval */ - g_itv = jiffies_diff(G.uptime[prev], G.uptime[current]); + itv = g_itv = jiffies_diff(G.uptime[prev], G.uptime[current]); /* Reduce interval to one CPU */ if (G.cpu_nr > 1) itv = jiffies_diff(G.uptime0[prev], G.uptime0[current]); - else - itv = g_itv; /* Print CPU stats */ if (display_opt(D_CPU)) { /* This is done exactly once */ if (!G.header_done) { - printf("\n%-11s CPU %%usr %%nice %%sys %%iowait %%irq " - "%%soft %%steal %%guest %%idle\n", prev_str); + printf("\n%-11s CPU %%usr %%nice %%sys %%iowait %%irq %%soft %%steal %%guest %%idle\n", + prev_str + ); G.header_done = 1; } - /* Should we print global stats? */ - if (G.cpu_bitmap[0] & 1) { - printf("%-11s all", current_str); - print_stats_cpu_struct(G.st_cpu[prev], G.st_cpu[current], g_itv); - } + for (cpu = 0; cpu <= G.cpu_nr; cpu++) { + data_t pc_itv; - for (cpu = 1; cpu <= G.cpu_nr; cpu++) { - scc = G.st_cpu[current] + cpu; - scp = G.st_cpu[prev] + cpu; - /* Print stats about this particular CPU? */ if (!is_cpu_in_bitmap(cpu)) continue; - printf("%-11s %4d", current_str, cpu - 1); + scc = &G.st_cpu[current][cpu]; + scp = &G.st_cpu[prev][cpu]; + pc_itv = g_itv; - /* - * If the CPU is offline, then it isn't in /proc/stat, - * sum of all values is 0. - * NB: Guest time is in user mode. - */ - if ((scc->cpu_user + scc->cpu_nice + scc->cpu_system + - scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal + - scc->cpu_irq + scc->cpu_softirq) == 0 - ) { + printf((cpu ? "%-11s %4u" : "%-11s all"), current_str, cpu - 1); + if (cpu) { + double idle; /* - * Set current struct fields to values from prev. - * iterations. Then their values won't jump from - * zero, when the CPU comes back online + * If the CPU is offline, then it isn't in /proc/stat, + * so all values are 0. + * NB: Guest time is in user mode. +///maybe "Guest time is in user TIME"? */ - *scc = *scp; - printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f" - " %6.2f %6.2f %6.2f\n", - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); - continue; + if ((scc->cpu_user | scc->cpu_nice | scc->cpu_system | + scc->cpu_iowait | scc->cpu_idle | scc->cpu_steal | + scc->cpu_irq | scc->cpu_softirq) == 0 + ) { + /* + * Set current struct fields to values from prev. + * iteration. Then their values won't jump from + * zero, when the CPU comes back online. + */ + *scc = *scp; + idle = 0.0; + goto print_zeros; + } + /* Compute interval again for current proc */ + pc_itv = get_per_cpu_interval(scc, scp); + if (pc_itv == 0) { + /* + * If the CPU is tickless then there is no change in CPU values + * but the sum of values is not zero. + */ + idle = 100.0; + print_zeros: + printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, idle); + continue; + } } - - /* Compute interval again for current proc */ - pc_itv = get_per_cpu_interval(scc, scp); - if (!pc_itv) { - /* - * If the CPU is tickless then there is no change in CPU values - * but the sum of values is not zero. - */ - printf(" %6.2f %6.2f %6.2f %6.2f %6.2f %6.2f" - " %6.2f %6.2f %6.2f\n", - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0); - } else { - print_stats_cpu_struct(scp, scc, pc_itv); - } + print_stats_cpu_struct(scp, scc, pc_itv); } } /* Print total number of IRQs per CPU */ if (display_opt(D_IRQ_SUM)) { - struct stats_irq *sic, *sip; /* Print average header, this is done exactly once */ if (!G.avg_header_done) { @@ -357,40 +368,44 @@ G.avg_header_done = 1; } - if (G.cpu_bitmap[0] & 1) - printf("%-11s all %9.2f\n", current_str, - ll_s_value(G.st_irq[prev]->irq_nr, G.st_irq[current]->irq_nr, itv)); + for (cpu = 0; cpu <= G.cpu_nr; cpu++) { + data_t pc_itv; - for (cpu = 1; cpu <= G.cpu_nr; cpu++) { /* Print stats about this CPU? */ if (!is_cpu_in_bitmap(cpu)) continue; - printf("%-11s %4d", current_str, cpu - 1); - - sic = G.st_irq[current] + cpu; - sip = G.st_irq[prev] + cpu; - - scc = G.st_cpu[current] + cpu; - scp = G.st_cpu[prev] + cpu; - - /* Compute interval again for current proc */ - pc_itv = get_per_cpu_interval(scc, scp); - if (!pc_itv) - printf(" %9.2f\n", 0.0); - else - printf(" %9.2f\n", - ll_s_value(sip->irq_nr, sic->irq_nr, itv)); + pc_itv = itv; + printf((cpu ? "%-11s %4u" : "%-11s all"), current_str, cpu - 1); + if (cpu) { + scc = &G.st_cpu[current][cpu]; + scp = &G.st_cpu[prev][cpu]; + /* Compute interval again for current proc */ + pc_itv = get_per_cpu_interval(scc, scp); + if (pc_itv == 0) { + printf(" %9.2f\n", 0.0); + continue; + } + } + printf(" %9.2f\n", hz_value(G.st_irq[prev][cpu].irq_nr, G.st_irq[current][cpu].irq_nr, pc_itv)); } } - if (display_opt(D_IRQ_CPU)) - write_irqcpu_stats(G.st_irqcpu, G.irqcpu_nr, itv, prev, - current, prev_str, current_str); + if (display_opt(D_IRQ_CPU)) { + write_irqcpu_stats(G.st_irqcpu, G.irqcpu_nr, + itv, + prev, current, + prev_str, current_str + ); + } - if (display_opt(D_SOFTIRQS)) + if (display_opt(D_SOFTIRQS)) { write_irqcpu_stats(G.st_softirqcpu, G.softirqcpu_nr, - itv, prev, current, prev_str, current_str); + itv, + prev, current, + prev_str, current_str + ); + } } /* @@ -398,29 +413,24 @@ */ static void write_stats(int current) { - char current_time[2][16]; + char prev_time[16]; + char curr_time[16]; - /* Get previous timestamp */ - strftime(current_time[!current], 16, "%X", &G.timestamp[!current]); + strftime(prev_time, sizeof(prev_time), "%X", &G.timestamp[!current]); + strftime(curr_time, sizeof(curr_time), "%X", &G.timestamp[current]); - /* Get current timestamp */ - strftime(current_time[current], 16, "%X", &G.timestamp[current]); - - write_stats_core(!current, current, current_time[!current], current_time[current]); + write_stats_core(!current, current, prev_time, curr_time); } static void write_stats_avg(int current) { - static const char buf[] = "Average:"; - write_stats_core(2, current, buf, buf); + write_stats_core(2, current, "Average:", "Average:"); } /* * Read CPU statistics */ -static void get_cpu_statistics(struct stats_cpu *cpu, - unsigned long long *up, - unsigned long long *up0) +static void get_cpu_statistics(struct stats_cpu *cpu, data_t *up, data_t *up0) { FILE *fp; char buf[1024]; @@ -432,7 +442,7 @@ unsigned cpu_number; struct stats_cpu *cp; - if ((buf[0] - 'c') | (buf[1] - 'p') | (buf[2] - 'u')) + if (!starts_with_cpu(buf)) continue; /* not "cpu" */ if (buf[3] == ' ') { /* "cpu " */ @@ -514,16 +524,16 @@ FILE *fp; struct stats_irq *irq_i; struct stats_irqcpu *ic; - char *buf = NULL; - unsigned long irq = 0; + char *buf; + unsigned buflen; unsigned cpu; + unsigned irq; int cpu_index[G.cpu_nr]; - int iindex = 0; + int iindex; int len, digit; - char *cp, *next; - for (cpu = 0; cpu < G.cpu_nr; cpu++) { - irq_i = G.st_irq[current] + cpu + 1; + for (cpu = 1; cpu <= G.cpu_nr; cpu++) { + irq_i = &G.st_irq[current][cpu]; irq_i->irq_nr = 0; } @@ -531,42 +541,47 @@ if (!fp) return; - buf = xmalloc(INTERRUPTS_LINE + 11 * G.cpu_nr); + buflen = INTERRUPTS_LINE + 11 * G.cpu_nr; + buf = xmalloc(buflen); /* Parse header and determine, which CPUs are online */ - while (fgets(buf, INTERRUPTS_LINE + 11 * G.cpu_nr, fp)) { + iindex = 0; + while (fgets(buf, buflen, fp)) { + char *cp, *next; next = buf; - while (((cp = strstr(next, "CPU")) != NULL) - && (iindex < G.cpu_nr) + while ((cp = strstr(next, "CPU")) != NULL + && iindex < G.cpu_nr ) { - cpu = strtol(cp + 3, &next, 10); + cpu = strtoul(cp + 3, &next, 10); cpu_index[iindex++] = cpu; } if (iindex) /* We found header */ break; } - while (fgets(buf, INTERRUPTS_LINE + 11 * G.cpu_nr, fp) + irq = 0; + while (fgets(buf, buflen, fp) && irq < irqs_per_cpu ) { + char *cp; /* Skip over "<irq>:" */ cp = strchr(buf, ':'); if (!cp) continue; - cp++; - ic = per_cpu_stats[current] + irq; - len = strcspn(buf, ":"); - if (len >= MAX_IRQ_LEN) { - len = MAX_IRQ_LEN - 1; + ic = &per_cpu_stats[current][irq]; + len = cp - buf; + if (len > sizeof(ic->irq_name)) { + len = sizeof(ic->irq_name); } - strncpy(ic->irq_name, buf, len); - ic->irq_name[len] = '\0'; + safe_strncpy(ic->irq_name, buf, len); digit = isdigit(buf[len - 1]); + cp++; for (cpu = 0; cpu < iindex; cpu++) { - ic = per_cpu_stats[current] + cpu_index[cpu] * irqs_per_cpu + irq; - irq_i = G.st_irq[current] + cpu_index[cpu] + 1; + char *next; + ic = &per_cpu_stats[current][cpu_index[cpu] * irqs_per_cpu + irq]; + irq_i = &G.st_irq[current][cpu_index[cpu] + 1]; ic->interrupt = strtoul(cp, &next, 10); if (digit) { /* Do not count non-numerical IRQs */ @@ -581,13 +596,13 @@ while (irq < irqs_per_cpu) { /* Number of interrupts per CPU has changed */ - ic = per_cpu_stats[current] + irq; - ic->irq_name[0] = 0; /* False interrupt */ + ic = &per_cpu_stats[current][irq]; + ic->irq_name[0] = '\0'; /* False interrupt */ irq++; } } -static void get_uptime(unsigned long long *uptime) +static void get_uptime(data_t *uptime) { FILE *fp; char buf[32]; /* 32 is enough for unsigned long.unsigned long */ @@ -598,7 +613,7 @@ return; if (fgets(buf, 32, fp)) { if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) { - *uptime = (unsigned long long)uptime_sec *G.hz + decimal * G.hz / 100; + *uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100; } } @@ -767,7 +782,7 @@ /* Get current file entry */ while ((d = readdir(dir)) != NULL) { - if (strncmp(d->d_name, "cpu", 3) == 0 && isdigit(d->d_name[3])) { + if (starts_with_cpu(d->d_name) && isdigit(d->d_name[3])) { snprintf(line, MAX_PF_NAME, "%s/%s", SYSFS_DEVCPU, d->d_name); line[MAX_PF_NAME - 1] = '\0'; @@ -792,14 +807,21 @@ static int get_proc_cpu_nr(void) { FILE *fp; - char line[16]; - int num_proc, proc_nr = -1; + char line[256]; + int proc_nr = -1; fp = xfopen_for_read(PROCFS_STAT); - while (fgets(line, 16, fp)) { - if (strncmp(line, "cpu ", 4) != 0 && strncmp(line, "cpu", 3) == 0) { - sscanf(line + 3, "%d", &num_proc); - if (num_proc > proc_nr) { + while (fgets(line, sizeof(line), fp)) { + if (!starts_with_cpu(line)) { + if (proc_nr >= 0) + break; /* we are past "cpuN..." lines */ + continue; + } + if (line[3] != ' ') { /* "cpuN" */ + int num_proc; + if (sscanf(line + 3, "%u", &num_proc) == 1 + && num_proc > proc_nr + ) { proc_nr = num_proc; } }
_______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox