mpstat: fix/improve handling of interrupt names
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 30 Jul 2010 10:45:14 +0000 (12:45 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 30 Jul 2010 10:45:14 +0000 (12:45 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
procps/mpstat.c

index b0d187ea691e76d1664c02fbad8e09c8cf1c183b..a1f3937c9f56a6511812412568fc8f31aa0d2eac 100644 (file)
 /* Maximum number of interrupts */
 #define NR_IRQS            256
 #define NR_IRQCPU_PREALLOC 3
-#define MAX_IRQ_LEN        16
+#define MAX_IRQNAME_LEN    16
 #define MAX_PF_NAME        512
+/* sysstat 9.0.6 uses width 8, but newer code which also prints /proc/softirqs
+ * data needs more: "interrupts" in /proc/softirqs have longer names,
+ * most are up to 8 chars, one (BLOCK_IOPOLL) is even longer.
+ * We are printing headers in the " IRQNAME/s" form, experimentally
+ * anything smaller than 10 chars looks ugly for /proc/softirqs stats.
+ */
+#define INTRATE_SCRWIDTH   10
+#define INTRATE_SCRWIDTH_STR "10"
 
 /* System files */
 #define SYSFS_DEVCPU      "/sys/devices/system/cpu"
@@ -54,7 +62,7 @@ typedef long idata_t;
 
 struct stats_irqcpu {
        unsigned interrupt;
-       char irq_name[MAX_IRQ_LEN];
+       char irq_name[MAX_IRQNAME_LEN];
 };
 
 struct stats_cpu {
@@ -195,10 +203,23 @@ static void write_irqcpu_stats(struct stats_irqcpu *per_cpu_stats[],
 
        /* 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);
+       {
+               /* A bit complex code to "buy back" space if one header is too wide.
+                * Here's how it looks like. BLOCK_IOPOLL eating too much,
+                * and latter headers use smaller width to compensate:
+                * ...BLOCK/s BLOCK_IOPOLL/s TASKLET/s SCHED/s HRTIMER/s  RCU/s
+                * ...   2.32      0.00      0.01     17.58      0.14    141.96
+                */
+               int expected_len = 0;
+               int printed_len = 0;
+               for (j = 0; j < total_irqs; j++) {
+                       p0 = &per_cpu_stats[current][j];
+                       if (p0->irq_name[0] != '\0') {
+                               int n = (INTRATE_SCRWIDTH-3) - (printed_len - expected_len);
+                               printed_len += printf(" %*s/s", n > 0 ? n : 0, skip_whitespace(p0->irq_name));
+                               expected_len += INTRATE_SCRWIDTH;
+                       }
+               }
        }
        bb_putchar('\n');
 
@@ -247,7 +268,7 @@ static void write_irqcpu_stats(struct stats_irqcpu *per_cpu_stats[],
                                        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",
+                                       printf("%"INTRATE_SCRWIDTH_STR".2f",
                                                (double)(p->interrupt - q->interrupt) / itv * G.hz);
                                } else {
                                        printf("        N/A");
@@ -574,10 +595,11 @@ static void get_irqs_from_interrupts(const char *fname,
 
                ic = &per_cpu_stats[current][irq];
                len = cp - buf;
-               if (len > sizeof(ic->irq_name)) {
-                       len = sizeof(ic->irq_name);
+               if (len >= sizeof(ic->irq_name)) {
+                       len = sizeof(ic->irq_name) - 1;
                }
-               safe_strncpy(ic->irq_name, buf, len);
+               safe_strncpy(ic->irq_name, buf, len + 1);
+               //bb_error_msg("%s: irq%d:'%s' buf:'%s'", fname, irq, ic->irq_name, buf);
                digit = isdigit(buf[len - 1]);
                cp++;