[PATCH] Add option for terse parseable output
diff --git a/README b/README
index c951504..d6ff109 100644
--- a/README
+++ b/README
@@ -48,6 +48,7 @@
 	-w Generate per-job bandwidth logs
 	-f <file> Read <file> for job descriptions
 	-o <file> Log output to file
+	-m Minimal (terse) output
 	-h Print help info
 	-v Print version information and exit
 
@@ -303,6 +304,31 @@
 		busy constantly, 50% would be a disk idling half of the time.
 
 
+Terse output
+------------
+
+For scripted usage where you typically want to generate tables or graphs
+of the results, fio can output the results in a comma seperated format.
+The format is one long line of values, such as:
+
+client1,0,0,936,331,2894,0,0,0.000000,0.000000,1,170,22.115385,34.290410,16,714,84.252874%,366.500000,566.417819,3496,1237,2894,0,0,0.000000,0.000000,0,246,6.671625,21.436952,0,2534,55.465300%,1406.600000,2008.044216,0.000000%,0.431928%,1109
+
+Split up, the format is as follows:
+
+	jobname, groupid, error
+	READ status:
+		KiB IO, bandwidth (KiB/sec), runtime (msec)
+		Submission latency: min, max, mean, deviation
+		Completion latency: min, max, mean, deviation
+		Bw: min, max, aggreate percentage of total, mean, deviation
+	WRITE status:
+		KiB IO, bandwidth (KiB/sec), runtime (msec)
+		Submission latency: min, max, mean, deviation
+		Completion latency: min, max, mean, deviation
+		Bw: min, max, aggreate percentage of total, mean, deviation
+	CPU usage: user, system, context switches
+
+
 Author
 ------
 
diff --git a/fio.c b/fio.c
index b53a58e..445600f 100644
--- a/fio.c
+++ b/fio.c
@@ -1576,7 +1576,7 @@
 	char eta_str[32];
 	double perc = 0.0;
 
-	if (temp_stall_ts)
+	if (temp_stall_ts || terse_output)
 		return;
 
 	eta_secs = malloc(thread_number * sizeof(int));
@@ -1717,8 +1717,10 @@
 
 	mlocked_mem = fio_pin_memory();
 
-	printf("Starting %d thread%s\n", thread_number, thread_number > 1 ? "s" : "");
-	fflush(stdout);
+	if (!terse_output) {
+		printf("Starting %d thread%s\n", thread_number, thread_number > 1 ? "s" : "");
+		fflush(stdout);
+	}
 
 	signal(SIGINT, sig_handler);
 	signal(SIGALRM, sig_handler);
diff --git a/fio.h b/fio.h
index cc68603..39d9587 100644
--- a/fio.h
+++ b/fio.h
@@ -324,6 +324,7 @@
 extern int thread_number;
 extern int shm_id;
 extern int groupid;
+extern int terse_output;
 extern FILE *f_out;
 extern FILE *f_err;
 
diff --git a/init.c b/init.c
index bbc9aad..40fcf1c 100644
--- a/init.c
+++ b/init.c
@@ -54,6 +54,7 @@
 int write_lat_log = 0;
 int write_bw_log = 0;
 int exitall_on_terminate = 0;
+int terse_output = 0;
 unsigned long long mlock_size = 0;
 FILE *f_out = NULL;
 FILE *f_err = NULL;
@@ -173,10 +174,12 @@
 
 	ddir = td->ddir + (!td->sequential << 1) + (td->iomix << 2);
 
-	if (!job_add_num)
-		fprintf(f_out, "%s: (g=%d): rw=%s, odir=%d, bs=%d-%d, rate=%d, ioengine=%s, iodepth=%d\n", td->name, td->groupid, ddir_str[ddir], td->odirect, td->min_bs, td->max_bs, td->rate, td->io_engine_name, td->iodepth);
-	else if (job_add_num == 1)
-		fprintf(f_out, "...\n");
+	if (!terse_output) {
+		if (!job_add_num)
+			fprintf(f_out, "%s: (g=%d): rw=%s, odir=%d, bs=%d-%d, rate=%d, ioengine=%s, iodepth=%d\n", td->name, td->groupid, ddir_str[ddir], td->odirect, td->min_bs, td->max_bs, td->rate, td->io_engine_name, td->iodepth);
+		else if (job_add_num == 1)
+			fprintf(f_out, "...\n");
+	}
 
 	/*
 	 * recurse add identical jobs, clear numjobs and stonewall options
@@ -965,6 +968,7 @@
 	printf("\t-t Runtime in seconds\n");
 	printf("\t-l Generate per-job latency logs\n");
 	printf("\t-w Generate per-job bandwidth logs\n");
+	printf("\t-m Minimal (terse) output\n");
 	printf("\t-f Job file (Required)\n");
 	printf("\t-v Print version info and exit\n");
 }
@@ -973,7 +977,7 @@
 {
 	int c, idx = 1, ini_idx = 0;
 
-	while ((c = getopt(argc, argv, "t:o:f:lwvh")) != EOF) {
+	while ((c = getopt(argc, argv, "t:o:f:lwvhm")) != EOF) {
 		switch (c) {
 			case 't':
 				def_timeout = atoi(optarg);
@@ -1002,6 +1006,10 @@
 				f_err = f_out;
 				idx++;
 				break;
+			case 'm':
+				terse_output = 1;
+				idx++;
+				break;
 			case 'h':
 				usage(argv[0]);
 				exit(0);
diff --git a/stat.c b/stat.c
index f3aa232..4cc3b7e 100644
--- a/stat.c
+++ b/stat.c
@@ -369,6 +369,63 @@
 	fprintf(f_out, "  cpu          : usr=%3.2f%%, sys=%3.2f%%, ctx=%lu\n", usr_cpu, sys_cpu, td->ctx);
 }
 
+static void show_ddir_status_terse(struct thread_data *td,
+				   struct group_run_stats *rs, int ddir)
+{
+	unsigned long min, max;
+	unsigned long long bw;
+	double mean, dev;
+
+	bw = 0;
+	if (td->runtime[ddir])
+		bw = td->io_bytes[ddir] / td->runtime[ddir];
+
+	fprintf(f_out, ",%llu,%llu,%lu", td->io_bytes[ddir] >> 10, bw, td->runtime[ddir]);
+
+	if (calc_lat(&td->slat_stat[ddir], &min, &max, &mean, &dev))
+		fprintf(f_out, ",%lu,%lu,%f,%f", min, max, mean, dev);
+	else
+		fprintf(f_out, ",%lu,%lu,%f,%f", 0UL, 0UL, 0.0, 0.0);
+
+	if (calc_lat(&td->clat_stat[ddir], &min, &max, &mean, &dev))
+		fprintf(f_out, ",%lu,%lu,%f,%f", min, max, mean, dev);
+	else
+		fprintf(f_out, ",%lu,%lu,%f,%f", 0UL, 0UL, 0.0, 0.0);
+
+	if (calc_lat(&td->bw_stat[ddir], &min, &max, &mean, &dev)) {
+		double p_of_agg;
+
+		p_of_agg = mean * 100 / (double) rs->agg[ddir];
+		fprintf(f_out, ",%lu,%lu,%f%%,%f,%f", min, max, p_of_agg, mean, dev);
+	} else
+		fprintf(f_out, ",%lu,%lu,%f%%,%f,%f", 0UL, 0UL, 0.0, 0.0, 0.0);
+		
+}
+
+
+static void show_thread_status_terse(struct thread_data *td,
+				     struct group_run_stats *rs)
+{
+	double usr_cpu, sys_cpu;
+
+	fprintf(f_out, "%s,%d,%d",td->name, td->groupid, td->error);
+
+	show_ddir_status_terse(td, rs, 0);
+	show_ddir_status_terse(td, rs, 1);
+
+	if (td->runtime[0] + td->runtime[1]) {
+		double runt = td->runtime[0] + td->runtime[1];
+
+		usr_cpu = (double) td->usr_time * 100 / runt;
+		sys_cpu = (double) td->sys_time * 100 / runt;
+	} else {
+		usr_cpu = 0;
+		sys_cpu = 0;
+	}
+
+	fprintf(f_out, ",%f%%,%f%%,%lu\n", usr_cpu, sys_cpu, td->ctx);
+}
+
 void show_run_stats(void)
 {
 	struct group_run_stats *runstats, *rs;
@@ -437,19 +494,25 @@
 	/*
 	 * don't overwrite last signal output
 	 */
-	printf("\n");
+	if (!terse_output)
+		printf("\n");
 
 	for (i = 0; i < thread_number; i++) {
 		td = &threads[i];
 		rs = &runstats[td->groupid];
 
-		show_thread_status(td, rs);
+		if (terse_output)
+			show_thread_status_terse(td, rs);
+		else
+			show_thread_status(td, rs);
 	}
 
-	for (i = 0; i < groupid + 1; i++)
-		show_group_stats(&runstats[i], i);
+	if (!terse_output) {
+		for (i = 0; i < groupid + 1; i++)
+			show_group_stats(&runstats[i], i);
 
-	show_disk_util();
+		show_disk_util();
+	}
 }
 
 static inline void add_stat_sample(struct io_stat *is, unsigned long val)