Index: sysstat-10.2.1/pr_stats.c
===================================================================
--- sysstat-10.2.1.orig/pr_stats.c
+++ sysstat-10.2.1/pr_stats.c
@@ -45,6 +45,10 @@ extern unsigned long avg_count;
 /*
  ***************************************************************************
  * Display CPU statistics.
+ * NB: The stats are only calculated over the part of the time interval when
+ * the CPU was online. As a consequence, the sum (%user + %nice + ... + %idle)
+ * will always be 100% on the time interval even if the CPU has been offline
+ * most of the time.
  *
  * IN:
  * @a		Activity structure with statistics.
@@ -58,7 +62,13 @@ __print_funct_t print_cpu_stats(struct a
 				unsigned long long g_itv)
 {
 	int i;
+	unsigned long long tot_jiffies_c, tot_jiffies_p;
 	struct stats_cpu *scc, *scp;
+	struct stats_cpu *scc_all = (struct stats_cpu *) ((char *) a->buf[curr]);
+	struct stats_cpu *scp_all = (struct stats_cpu *) ((char *) a->buf[prev]);
+	unsigned char offline_cpu_bitmap[BITMAP_SIZE(NR_CPUS)] = {0};
+
+	g_itv = 0;
 
 	if (dis) {
 		if (DISPLAY_CPU_DEF(a->opt_flags)) {
@@ -73,7 +83,19 @@ __print_funct_t print_cpu_stats(struct a
 			       timestamp[!curr]);
 		}
 	}
-	
+
+	/*
+	 * Initial processing.
+	 * Compute CPU "all" as sum of all individual CPU. Done only on SMP machines (a->nr > 1).
+	 * 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 (a->nr > 1) {
+		memset(scc_all, 0, sizeof(struct stats_cpu));
+		memset(scp_all, 0, sizeof(struct stats_cpu));
+	}
+
 	for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
 		
 		/*
@@ -86,126 +108,199 @@ __print_funct_t print_cpu_stats(struct a
 		scp = (struct stats_cpu *) ((char *) a->buf[prev] + i * a->msize);
 
 		/*
-		 * Note: a->nr is in [1, NR_CPUS + 1].
+		 * Compute the total number of jiffies spent by current processor.
+		 * 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 omited 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) && !WANT_SINCE_BOOT(flags)) {
+			/*
+			 * 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".
+			 */
+			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.
+		 */
+		g_itv += 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;
+	}
+
+	/*
+	 * 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 < a->nr) && (i < a->bitmap->b_size + 1); i++) {
+		/*
+		 * Should current CPU (including CPU "all") be displayed?
+		 * Note: @nr[curr] is in [1, NR_CPUS + 1].
 		 * Bitmap size is provided for (NR_CPUS + 1) CPUs.
 		 * Anyway, NR_CPUS may vary between the version of sysstat
 		 * used by sadc to create a file, and the version of sysstat
 		 * used by sar to read it...
 		 */
+		if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))) ||
+		    offline_cpu_bitmap[i >> 3] & (1 << (i & 0x07)))
+			/* Don't display CPU */
+			continue;
+
+		scc = (struct stats_cpu *) ((char *) a->buf[curr] + i * a->msize);
+		scp = (struct stats_cpu *) ((char *) a->buf[prev] + i * a->msize);
 		
-		/* Should current CPU (including CPU "all") be displayed? */
-		if (a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))) {
-			
-			/* Yes: Display it */
-			printf("%-11s", timestamp[curr]);
+		printf("%-11s", timestamp[curr]);
+
+		if (i == 0) {
+			/* This is CPU "all" */
+			printf("     all");
 			
-			if (!i) {
-				/* This is CPU "all" */
-				printf("     all");
+			if (a->nr == 1) {
+				/*
+				 * This is a UP machine. In this case
+				 * interval has still not been calculated.
+				 */
+				g_itv = get_per_cpu_interval(scc, scp);
 			}
-			else {
-				printf("     %3d", i - 1);
+		}
+		else {
+			printf("     %3d", i - 1);
 
+			/* Recalculate interval for current proc */
+			g_itv = get_per_cpu_interval(scc, scp);
+			
+			if (!g_itv) {
 				/*
-				 * If the CPU is offline then it is omited from /proc/stat:
-				 * All the fields couldn't have been read and the sum of them is zero.
-				 * (Remember that guest/guest_nice times are already included in
-				 * user/nice modes.)
+				 * If the CPU is tickless then there is no change in CPU values
+				 * but the sum of values is not zero.
+				 * %user, %nice, %system, %iowait, %steal, ..., %idle
 				 */
-				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) {
-					/*
-					 * 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;
-					
-					/* %user, %nice, %system, %iowait, %steal, ..., %idle */
-					printf("    %6.2f    %6.2f    %6.2f"
-					       "    %6.2f    %6.2f    %6.2f",
-					       0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
-					
-					if (DISPLAY_CPU_ALL(a->opt_flags)) {
-						/*
-						 * Four additional fields to display:
-						 * %irq, %soft, %guest, %gnice.
-						 */
-						printf("    %6.2f    %6.2f    %6.2f    %6.2f",
-						       0.0, 0.0, 0.0, 0.0);
-					}
-					printf("\n");
-					continue;
-				}
+				printf("    %6.2f    %6.2f    %6.2f"
+				       "    %6.2f    %6.2f",
+				       0.0, 0.0, 0.0, 0.0, 0.0);
 				
-				/* Recalculate interval for current proc */
-				g_itv = get_per_cpu_interval(scc, scp);
-				
-				if (!g_itv) {
-					/*
-					 * If the CPU is tickless then there is no change in CPU values
-					 * but the sum of values is not zero.
-					 * %user, %nice, %system, %iowait, %steal, ..., %idle
-					 */
-					printf("    %6.2f    %6.2f    %6.2f"
-					       "    %6.2f    %6.2f",
-					       0.0, 0.0, 0.0, 0.0, 0.0);
-
-					if (DISPLAY_CPU_DEF(a->opt_flags)) {
-						printf("    %6.2f\n", 100.0);
-					}
-					/*
-					 * Four additional fields to display:
-					 * %irq, %soft, %guest, %gnice.
-					 */
-					else if (DISPLAY_CPU_ALL(a->opt_flags)) {
-						printf("    %6.2f    %6.2f    %6.2f    %6.2f\n",
-						       0.0, 0.0, 0.0, 100.0);
-					}
-					continue;
+				if (DISPLAY_CPU_DEF(a->opt_flags)) {
+					printf("    %6.2f\n", 100.0);
 				}
-			}
-			
-			if (DISPLAY_CPU_DEF(a->opt_flags)) {
-				printf("    %6.2f    %6.2f    %6.2f    %6.2f    %6.2f    %6.2f\n",
-				       ll_sp_value(scp->cpu_user, scc->cpu_user, g_itv),
-				       ll_sp_value(scp->cpu_nice, scc->cpu_nice, g_itv),
-				       ll_sp_value(scp->cpu_sys + scp->cpu_hardirq + scp->cpu_softirq,
-						   scc->cpu_sys + scc->cpu_hardirq + scc->cpu_softirq,
-						   g_itv),
-				       ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
-				       ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
-				       scc->cpu_idle < scp->cpu_idle ?
-				       0.0 :
-				       ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv));
-			}
-			else if (DISPLAY_CPU_ALL(a->opt_flags)) {
-				printf("    %6.2f    %6.2f    %6.2f    %6.2f    %6.2f    %6.2f"
-				       "    %6.2f    %6.2f    %6.2f    %6.2f\n",
-				       (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, g_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, g_itv),
-				       ll_sp_value(scp->cpu_sys, scc->cpu_sys, g_itv),
-				       ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
-				       ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
-				       ll_sp_value(scp->cpu_hardirq, scc->cpu_hardirq, g_itv),
-				       ll_sp_value(scp->cpu_softirq, scc->cpu_softirq, g_itv),
-				       ll_sp_value(scp->cpu_guest, scc->cpu_guest, g_itv),
-				       ll_sp_value(scp->cpu_guest_nice, scc->cpu_guest_nice, g_itv),
-				       scc->cpu_idle < scp->cpu_idle ?
-				       0.0 :
-				       ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv));
+				/*
+				 * Four additional fields to display:
+				 * %irq, %soft, %guest, %gnice.
+				 */
+				else if (DISPLAY_CPU_ALL(a->opt_flags)) {
+					printf("    %6.2f    %6.2f    %6.2f    %6.2f\n",
+					       0.0, 0.0, 0.0, 100.0);
+				}
+				continue;
 			}
 		}
+		
+		if (DISPLAY_CPU_DEF(a->opt_flags)) {
+			printf("    %6.2f    %6.2f    %6.2f    %6.2f    %6.2f    %6.2f\n",
+			       ll_sp_value(scp->cpu_user, scc->cpu_user, g_itv),
+			       ll_sp_value(scp->cpu_nice, scc->cpu_nice, g_itv),
+			       ll_sp_value(scp->cpu_sys + scp->cpu_hardirq + scp->cpu_softirq,
+					   scc->cpu_sys + scc->cpu_hardirq + scc->cpu_softirq,
+					   g_itv),
+			       ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
+			       ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
+			       scc->cpu_idle < scp->cpu_idle ?
+			       0.0 :
+			       ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv));
+		}
+		else if (DISPLAY_CPU_ALL(a->opt_flags)) {
+			printf("    %6.2f    %6.2f    %6.2f    %6.2f    %6.2f    %6.2f"
+			       "    %6.2f    %6.2f    %6.2f    %6.2f\n",
+			       (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, g_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, g_itv),
+			       ll_sp_value(scp->cpu_sys, scc->cpu_sys, g_itv),
+			       ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
+			       ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
+			       ll_sp_value(scp->cpu_hardirq, scc->cpu_hardirq, g_itv),
+			       ll_sp_value(scp->cpu_softirq, scc->cpu_softirq, g_itv),
+			       ll_sp_value(scp->cpu_guest, scc->cpu_guest, g_itv),
+			       ll_sp_value(scp->cpu_guest_nice, scc->cpu_guest_nice, g_itv),
+			       scc->cpu_idle < scp->cpu_idle ?
+			       0.0 :
+			       ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv));
+		}
 	}
 }
 
+
 /*
  ***************************************************************************
  * Display tasks creation and context switches statistics.
