a2be1b
From 4fcd56bf582ee2ef5b205625ca3d1bfed90364e9 Mon Sep 17 00:00:00 2001
a2be1b
From: Jaromir Capik <jcapik@redhat.com>
a2be1b
Date: Tue, 4 Feb 2014 19:10:42 +0100
a2be1b
Subject: [PATCH] vmstat: Support for timestamps with '-t' & fix for '-wd'
a2be1b
a2be1b
From now the vmstat can append a timestamp to each line in the
a2be1b
VMSTAT and DISKSTAT mode. You can achieve that with the '-t'
a2be1b
switch.
a2be1b
The '-w' switch now works in the DISKSTAT mode too.
a2be1b
---
a2be1b
 vmstat.8 |   3 +
a2be1b
 vmstat.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
a2be1b
 2 files changed, 167 insertions(+), 33 deletions(-)
a2be1b
a2be1b
diff --git a/vmstat.8 b/vmstat.8
a2be1b
index ef6cbe9..420d9f3 100644
a2be1b
--- a/vmstat.8
a2be1b
+++ b/vmstat.8
a2be1b
@@ -74,6 +74,9 @@ or 1048576
a2be1b
 bytes.  Note this does not change the swap (si/so) or block (bi/bo)
a2be1b
 fields.
a2be1b
 .TP
a2be1b
+\fB\-t\fR, \fB\-\-timestamp\fR
a2be1b
+Append timestamp to each line
a2be1b
+.TP
a2be1b
 \fB\-w\fR, \fB\-\-wide\fR
a2be1b
 Wide output mode (useful for systems with higher amount of memory,
a2be1b
 where the default output mode suffers from unwanted column breakage).
a2be1b
diff --git a/vmstat.c b/vmstat.c
a2be1b
index c01351d..a84d2d2 100644
a2be1b
--- a/vmstat.c
a2be1b
+++ b/vmstat.c
a2be1b
@@ -43,6 +43,7 @@
a2be1b
 #include <sys/types.h>
a2be1b
 #include <termios.h>
a2be1b
 #include <unistd.h>
a2be1b
+#include <time.h>
a2be1b
 
a2be1b
 #include "c.h"
a2be1b
 #include "fileutils.h"
a2be1b
@@ -75,6 +76,9 @@ static int a_option;
a2be1b
 /* "-w" means "wide output" */
a2be1b
 static int w_option;
a2be1b
 
a2be1b
+/* "-t" means "show timestamp" */
a2be1b
+static int t_option;
a2be1b
+
a2be1b
 static unsigned sleep_time = 1;
a2be1b
 static int infinite_updates = 0;
a2be1b
 static unsigned long num_updates;
a2be1b
@@ -100,6 +104,7 @@ static void __attribute__ ((__noreturn__))
a2be1b
 	fputs(_(" -p, --partition <dev>  partition specific statistics\n"), out);
a2be1b
 	fputs(_(" -S, --unit <char>      define display unit\n"), out);
a2be1b
 	fputs(_(" -w, --wide             wide output\n"), out);
a2be1b
+	fputs(_(" -t, --timestamp        show timestamp\n"), out);
a2be1b
 	fputs(USAGE_SEPARATOR, out);
a2be1b
 	fputs(USAGE_HELP, out);
a2be1b
 	fputs(USAGE_VERSION, out);
a2be1b
@@ -180,21 +185,33 @@ static int format_1000(unsigned long long val64, char *restrict dst)
a2be1b
 
a2be1b
 static void new_header(void)
a2be1b
 {
a2be1b
+	struct tm *tm_ptr;
a2be1b
+	time_t the_time;
a2be1b
+	char timebuf[32];
a2be1b
+
a2be1b
 	/* Translation Hint: Translating folloging header & fields
a2be1b
 	 * that follow (marked with max x chars) might not work,
a2be1b
 	 * unless manual page is translated as well.  */
a2be1b
-
a2be1b
-	const char header[] =
a2be1b
-	    "procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----\n";
a2be1b
-	const char wide_header[] =
a2be1b
-	    "procs -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------\n";
a2be1b
+	const char *header =
a2be1b
+	    _("procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----");
a2be1b
+	const char *wide_header =
a2be1b
+	    _("procs -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------");
a2be1b
+	const char *timestamp_header = _(" -----timestamp-----");
a2be1b
 
a2be1b
 	const char format[] =
a2be1b
-	    "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s\n";
a2be1b
+	    "%2s %2s %6s %6s %6s %6s %4s %4s %5s %5s %4s %4s %2s %2s %2s %2s %2s";
a2be1b
 	const char wide_format[] =
a2be1b
-	    "%2s %2s %12s %12s %12s %12s %4s %4s %5s %5s %4s %4s %3s %3s %3s %3s %3s\n";
a2be1b
+	    "%2s %2s %12s %12s %12s %12s %4s %4s %5s %5s %4s %4s %3s %3s %3s %3s %3s";
a2be1b
+
a2be1b
+
a2be1b
+	printf(w_option ? wide_header : header);
a2be1b
+
a2be1b
+	if (t_option) {
a2be1b
+		printf(timestamp_header);
a2be1b
+	}
a2be1b
+
a2be1b
+	printf("\n");
a2be1b
 
a2be1b
-	printf(w_option ? _(wide_header) : _(header));
a2be1b
 	printf(
a2be1b
 	    w_option ? wide_format : format,
a2be1b
 	    /* Translation Hint: max 2 chars */
a2be1b
@@ -235,6 +252,19 @@ static void new_header(void)
a2be1b
 	     _("wa"),
a2be1b
 	    /* Translation Hint: max 2 chars */
a2be1b
 	     _("st"));
a2be1b
+
a2be1b
+	if (t_option) {
a2be1b
+		(void) time( &the_time );
a2be1b
+		tm_ptr = localtime( &the_time );
a2be1b
+		if (strftime(timebuf, sizeof(timebuf), "%Z", tm_ptr)) {
a2be1b
+			timebuf[strlen(timestamp_header) - 1] = '\0';
a2be1b
+		} else {
a2be1b
+			timebuf[0] = '\0';
a2be1b
+		}
a2be1b
+		printf(" %*s", (int)(strlen(timestamp_header) - 1), timebuf);
a2be1b
+	}
a2be1b
+
a2be1b
+	printf("\n");
a2be1b
 }
a2be1b
 
a2be1b
 static unsigned long unitConvert(unsigned long size)
a2be1b
@@ -247,9 +277,9 @@ static unsigned long unitConvert(unsigned long size)
a2be1b
 static void new_format(void)
a2be1b
 {
a2be1b
 	const char format[] =
a2be1b
-	    "%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u\n";
a2be1b
+	    "%2u %2u %6lu %6lu %6lu %6lu %4u %4u %5u %5u %4u %4u %2u %2u %2u %2u %2u";
a2be1b
 	const char wide_format[] =
a2be1b
-	    "%2u %2u %12lu %12lu %12lu %12lu %4u %4u %5u %5u %4u %4u %3u %3u %3u %3u %3u\n";
a2be1b
+	    "%2u %2u %12lu %12lu %12lu %12lu %4u %4u %5u %5u %4u %4u %3u %3u %3u %3u %3u";
a2be1b
 
a2be1b
 	unsigned int tog = 0;	/* toggle switch for cleaner code */
a2be1b
 	unsigned int i;
a2be1b
@@ -263,6 +293,9 @@ static void new_format(void)
a2be1b
 	unsigned int sleep_half;
a2be1b
 	unsigned long kb_per_page = sysconf(_SC_PAGESIZE) / 1024ul;
a2be1b
 	int debt = 0;		/* handle idle ticks running backwards */
a2be1b
+	struct tm *tm_ptr;
a2be1b
+	time_t the_time;
a2be1b
+	char timebuf[32];
a2be1b
 
a2be1b
 	sleep_half = (sleep_time / 2);
a2be1b
 	new_header();
a2be1b
@@ -272,6 +305,12 @@ static void new_format(void)
a2be1b
 		cpu_zzz, pgpgin, pgpgout, pswpin, pswpout, intr, ctxt, &running,
a2be1b
 		&blocked, &dummy_1, &dummy_2);
a2be1b
 
a2be1b
+	if (t_option) {
a2be1b
+		(void) time( &the_time );
a2be1b
+		tm_ptr = localtime( &the_time );
a2be1b
+		strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_ptr);
a2be1b
+	}
a2be1b
+
a2be1b
 	duse = *cpu_use + *cpu_nic;
a2be1b
 	dsys = *cpu_sys + *cpu_xxx + *cpu_yyy;
a2be1b
 	didl = *cpu_idl;
a2be1b
@@ -298,6 +337,12 @@ static void new_format(void)
a2be1b
 	       (unsigned)( (100*dstl			+ divo2) / Div )
a2be1b
 	);
a2be1b
 
a2be1b
+	if (t_option) {
a2be1b
+		printf(" %s", timebuf);
a2be1b
+	}
a2be1b
+
a2be1b
+	printf("\n");
a2be1b
+
a2be1b
 	/* main loop */
a2be1b
 	for (i = 1; infinite_updates || i < num_updates; i++) {
a2be1b
 		sleep(sleep_time);
a2be1b
@@ -313,6 +358,12 @@ static void new_format(void)
a2be1b
 			pgpgout + tog, pswpin + tog, pswpout + tog, intr + tog,
a2be1b
 			ctxt + tog, &running, &blocked, &dummy_1, &dummy_2);
a2be1b
 
a2be1b
+		if (t_option) {
a2be1b
+			(void) time( &the_time );
a2be1b
+			tm_ptr = localtime( &the_time );
a2be1b
+			strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_ptr);
a2be1b
+		}
a2be1b
+
a2be1b
 		duse =
a2be1b
 		    cpu_use[tog] - cpu_use[!tog] + cpu_nic[tog] - cpu_nic[!tog];
a2be1b
 		dsys =
a2be1b
@@ -364,6 +415,12 @@ static void new_format(void)
a2be1b
 		       /* st */
a2be1b
 		       (unsigned)( (100*dstl+divo2)/Div )
a2be1b
 		);
a2be1b
+
a2be1b
+		if (t_option) {
a2be1b
+			printf(" %s", timebuf);
a2be1b
+		}
a2be1b
+
a2be1b
+		printf("\n");
a2be1b
 	}
a2be1b
 }
a2be1b
 
a2be1b
@@ -453,11 +510,33 @@ static int diskpartition_format(const char *partition_name)
a2be1b
 
a2be1b
 static void diskheader(void)
a2be1b
 {
a2be1b
+	struct tm *tm_ptr;
a2be1b
+	time_t the_time;
a2be1b
+	char timebuf[32];
a2be1b
+
a2be1b
 	/* Translation Hint: Translating folloging header & fields
a2be1b
 	 * that follow (marked with max x chars) might not work,
a2be1b
 	 * unless manual page is translated as well.  */
a2be1b
-	printf(_("disk- ------------reads------------ ------------writes----------- -----IO------\n"));
a2be1b
-	printf("%5s %6s %6s %7s %7s %6s %6s %7s %7s %6s %6s\n",
a2be1b
+	const char *header =
a2be1b
+	    _("disk- ------------reads------------ ------------writes----------- -----IO------");
a2be1b
+	const char *wide_header =
a2be1b
+	    _("disk- -------------------reads------------------- -------------------writes------------------ ------IO-------");
a2be1b
+	const char *timestamp_header = _(" -----timestamp-----");
a2be1b
+
a2be1b
+	const char format[] =
a2be1b
+	    "%5s %6s %6s %7s %7s %6s %6s %7s %7s %6s %6s";
a2be1b
+	const char wide_format[] =
a2be1b
+	    "%5s %9s %9s %11s %11s %9s %9s %11s %11s %7s %7s";
a2be1b
+
a2be1b
+	printf(w_option ? wide_header : header);
a2be1b
+
a2be1b
+	if (t_option) {
a2be1b
+		printf(timestamp_header);
a2be1b
+	}
a2be1b
+
a2be1b
+	printf("\n");
a2be1b
+
a2be1b
+	printf(w_option ? wide_format : format,
a2be1b
 	       " ",
a2be1b
 	       /* Translation Hint: max 6 chars */
a2be1b
 	       _("total"),
a2be1b
@@ -479,25 +558,53 @@ static void diskheader(void)
a2be1b
 	       _("cur"),
a2be1b
 	       /* Translation Hint: max 6 chars */
a2be1b
 	       _("sec"));
a2be1b
+
a2be1b
+	if (t_option) {
a2be1b
+		(void) time( &the_time );
a2be1b
+		tm_ptr = localtime( &the_time );
a2be1b
+		if (strftime(timebuf, sizeof(timebuf), "%Z", tm_ptr)) {
a2be1b
+			timebuf[strlen(timestamp_header) - 1] = '\0';
a2be1b
+		} else {
a2be1b
+			timebuf[0] = '\0';
a2be1b
+		}
a2be1b
+		printf(" %*s", (int)(strlen(timestamp_header) - 1), timebuf);
a2be1b
+	}
a2be1b
+
a2be1b
+	printf("\n");
a2be1b
 }
a2be1b
 
a2be1b
 static void diskformat(void)
a2be1b
 {
a2be1b
+	const char format[] =
a2be1b
+	    "%-5s %6u %6u %7llu %7u %6u %6u %7llu %7u %6u %6u";
a2be1b
+	const char wide_format[] =
a2be1b
+	    "%-5s %9u %9u %11llu %11u %9u %9u %11llu %11u %7u %7u";
a2be1b
+
a2be1b
 	FILE *fDiskstat;
a2be1b
 	struct disk_stat *disks;
a2be1b
 	struct partition_stat *partitions;
a2be1b
 	unsigned long ndisks, i, j, k;
a2be1b
-	const char format[] = "%-5s %6u %6u %7llu %7u %6u %6u %7llu %7u %6u %6u\n";
a2be1b
+	struct tm *tm_ptr;
a2be1b
+	time_t the_time;
a2be1b
+	char timebuf[32];
a2be1b
+
a2be1b
 
a2be1b
 	if ((fDiskstat = fopen("/proc/diskstats", "rb"))) {
a2be1b
 		fclose(fDiskstat);
a2be1b
 		ndisks = getdiskstat(&disks, &partitions);
a2be1b
+
a2be1b
+		if (t_option) {
a2be1b
+			(void) time( &the_time );
a2be1b
+			tm_ptr = localtime( &the_time );
a2be1b
+			strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_ptr);
a2be1b
+		}
a2be1b
+
a2be1b
 		if (!moreheaders)
a2be1b
 			diskheader();
a2be1b
 		for (k = 0; k < ndisks; k++) {
a2be1b
 			if (moreheaders && ((k % height) == 0))
a2be1b
 				diskheader();
a2be1b
-			printf(format,
a2be1b
+			printf(w_option ? wide_format : format,
a2be1b
 			       disks[k].disk_name,
a2be1b
 			       disks[k].reads,
a2be1b
 			       disks[k].merged_reads,
a2be1b
@@ -510,31 +617,51 @@ static void diskformat(void)
a2be1b
 			       disks[k].inprogress_IO ? disks[k].inprogress_IO / 1000 : 0,
a2be1b
 			       disks[k].milli_spent_IO ? disks[k].
a2be1b
 			       milli_spent_IO / 1000 : 0);
a2be1b
+
a2be1b
+			if (t_option) {
a2be1b
+				printf(" %s", timebuf);
a2be1b
+			}
a2be1b
+
a2be1b
+			printf("\n");
a2be1b
 			fflush(stdout);
a2be1b
 		}
a2be1b
 		free(disks);
a2be1b
 		free(partitions);
a2be1b
+
a2be1b
 		for (j = 1; infinite_updates || j < num_updates; j++) {
a2be1b
 			sleep(sleep_time);
a2be1b
 			ndisks = getdiskstat(&disks, &partitions);
a2be1b
-		for (i = 0; i < ndisks; i++, k++) {
a2be1b
-			if (moreheaders && ((k % height) == 0))
a2be1b
-				diskheader();
a2be1b
-			printf(format,
a2be1b
-			       disks[i].disk_name,
a2be1b
-			       disks[i].reads,
a2be1b
-			       disks[i].merged_reads,
a2be1b
-			       disks[i].reads_sectors,
a2be1b
-			       disks[i].milli_reading,
a2be1b
-			       disks[i].writes,
a2be1b
-			       disks[i].merged_writes,
a2be1b
-			       disks[i].written_sectors,
a2be1b
-			       disks[i].milli_writing,
a2be1b
-			       disks[i].inprogress_IO ? disks[i].inprogress_IO / 1000 : 0,
a2be1b
-			       disks[i].milli_spent_IO ? disks[i].
a2be1b
-			       milli_spent_IO / 1000 : 0);
a2be1b
-			fflush(stdout);
a2be1b
-		}
a2be1b
+
a2be1b
+			if (t_option) {
a2be1b
+				(void) time( &the_time );
a2be1b
+				tm_ptr = localtime( &the_time );
a2be1b
+				strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm_ptr);
a2be1b
+			}
a2be1b
+
a2be1b
+			for (i = 0; i < ndisks; i++, k++) {
a2be1b
+				if (moreheaders && ((k % height) == 0))
a2be1b
+					diskheader();
a2be1b
+				printf(w_option ? wide_format : format,
a2be1b
+				       disks[i].disk_name,
a2be1b
+				       disks[i].reads,
a2be1b
+				       disks[i].merged_reads,
a2be1b
+				       disks[i].reads_sectors,
a2be1b
+				       disks[i].milli_reading,
a2be1b
+				       disks[i].writes,
a2be1b
+				       disks[i].merged_writes,
a2be1b
+				       disks[i].written_sectors,
a2be1b
+				       disks[i].milli_writing,
a2be1b
+				       disks[i].inprogress_IO ? disks[i].inprogress_IO / 1000 : 0,
a2be1b
+				       disks[i].milli_spent_IO ? disks[i].
a2be1b
+				       milli_spent_IO / 1000 : 0);
a2be1b
+
a2be1b
+				if (t_option) {
a2be1b
+					printf(" %s", timebuf);
a2be1b
+				}
a2be1b
+
a2be1b
+				printf("\n");
a2be1b
+				fflush(stdout);
a2be1b
+			}
a2be1b
 			free(disks);
a2be1b
 			free(partitions);
a2be1b
 		}
a2be1b
@@ -740,6 +867,7 @@ int main(int argc, char *argv[])
a2be1b
 		{"partition", required_argument, NULL, 'p'},
a2be1b
 		{"unit", required_argument, NULL, 'S'},
a2be1b
 		{"wide", no_argument, NULL, 'w'},
a2be1b
+		{"timestamp", no_argument, NULL, 't'},
a2be1b
 		{"help", no_argument, NULL, 'h'},
a2be1b
 		{"version", no_argument, NULL, 'V'},
a2be1b
 		{NULL, 0, NULL, 0}
a2be1b
@@ -754,7 +882,7 @@ int main(int argc, char *argv[])
a2be1b
 	atexit(close_stdout);
a2be1b
 
a2be1b
 	while ((c =
a2be1b
-		getopt_long(argc, argv, "afmnsdDp:S:whV", longopts,
a2be1b
+		getopt_long(argc, argv, "afmnsdDp:S:wthV", longopts,
a2be1b
 			    NULL)) != EOF)
a2be1b
 		switch (c) {
a2be1b
 		case 'V':
a2be1b
@@ -820,6 +948,9 @@ int main(int argc, char *argv[])
a2be1b
 		case 'w':
a2be1b
 			w_option = 1;
a2be1b
 			break;
a2be1b
+		case 't':
a2be1b
+			t_option = 1;
a2be1b
+			break;
a2be1b
 		default:
a2be1b
 			/* no other aguments defined yet. */
a2be1b
 			usage(stderr);
a2be1b
-- 
a2be1b
1.8.4.2
a2be1b