Add support for other ways of triggering intermediate result outputs

Fio has support for using the USR1 signal to dump the current
results while continuing to run. Add a --status-interval parameter
to allow this to be configured to automatically happen every X
seconds.

There's also support for signaling fio through the file system. To
do that, simply touch /tmp/fio-dump-status. When fio sees this file,
it will unlink and dump the current results.

A small tweak is needed for the file approach to work in Windows.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/HOWTO b/HOWTO
index c7d0c9e..6445348 100644
--- a/HOWTO
+++ b/HOWTO
@@ -1600,6 +1600,9 @@
 
 It is also possible to get fio to dump the current output while it is
 running, without terminating the job. To do that, send fio the USR1 signal.
+You can also get regularly timed dumps by using the --status-interval
+parameter, or by creating a file in /tmp named fio-dump-status. If fio
+sees this file, it will unlink it and dump the current output status.
 
 
 7.0 Terse output
@@ -1721,7 +1724,7 @@
 
 
 9.0 CPU idleness profiling
-
+--------------------------
 In some cases, we want to understand CPU overhead in a test. For example,
 we test patches for the specific goodness of whether they reduce CPU usage.
 fio implements a balloon approach to create a thread per CPU that runs at
diff --git a/README b/README
index 3782570..15a0731 100644
--- a/README
+++ b/README
@@ -155,6 +155,7 @@
 	--eta=when		When ETA estimate should be printed
 				May be "always", "never" or "auto"
 	--eta-newline=time	Force a new line for every 'time' period passed
+	--status-interval=t	Force full status dump every 't' period passed
 	--section=name		Only run specified section in job file.
 				Multiple sections can be specified.
 	--alloc-size=kb		Set smalloc pool to this size in kb (def 1024)
diff --git a/backend.c b/backend.c
index f48b43d..4d4e8ef 100644
--- a/backend.c
+++ b/backend.c
@@ -1558,6 +1558,12 @@
 		fio_terminate_threads(TERMINATE_ALL);
 }
 
+static void do_usleep(unsigned int usecs)
+{
+	check_for_running_stats();
+	usleep(usecs);
+}
+
 /*
  * Main function for kicking off and reaping jobs, as needed.
  */
@@ -1736,7 +1742,7 @@
 			if (mtime_since_now(&this_start) > JOB_START_TIMEOUT)
 				break;
 
-			usleep(100000);
+			do_usleep(100000);
 
 			for (i = 0; i < this_jobs; i++) {
 				td = map[i];
@@ -1788,12 +1794,12 @@
 		reap_threads(&nr_running, &t_rate, &m_rate);
 
 		if (todo)
-			usleep(100000);
+			do_usleep(100000);
 	}
 
 	while (nr_running) {
 		reap_threads(&nr_running, &t_rate, &m_rate);
-		usleep(10000);
+		do_usleep(10000);
 	}
 
 	fio_idle_prof_stop();
diff --git a/fio.h b/fio.h
index 50a868a..ebf6309 100644
--- a/fio.h
+++ b/fio.h
@@ -362,6 +362,7 @@
 extern int is_backend;
 extern int nr_clients;
 extern int log_syslog;
+extern int status_interval;
 extern const char fio_version_string[];
 
 extern struct thread_data *threads;
diff --git a/init.c b/init.c
index 634dda2..ff9b6d4 100644
--- a/init.c
+++ b/init.c
@@ -58,6 +58,7 @@
 
 int write_bw_log = 0;
 int read_only = 0;
+int status_interval = 0;
 
 static int write_lat_log;
 
@@ -123,9 +124,9 @@
 		.val		= 'c' | FIO_CLIENT_FLAG,
 	},
 	{
-		.name		   = (char *) "enghelp",
+		.name		= (char *) "enghelp",
 		.has_arg	= optional_argument,
-		.val		    = 'i' | FIO_CLIENT_FLAG,
+		.val		= 'i' | FIO_CLIENT_FLAG,
 	},
 	{
 		.name		= (char *) "showcmd",
@@ -212,6 +213,11 @@
 		.val		= 'I',
 	},
 	{
+		.name		= (char *) "status-interval",
+		.has_arg	= required_argument,
+		.val		= 'L',
+	},
+	{
 		.name		= NULL,
 	},
 };
@@ -1361,6 +1367,8 @@
 	printf("            \t\tMay be \"always\", \"never\" or \"auto\"\n");
 	printf("  --eta-newline=time\tForce a new line for every 'time'");
 	printf(" period passed\n");
+	printf("  --status-interval=t\tForce full status dump every");
+	printf(" 't' period passed\n");
 	printf("  --readonly\t\tTurn on safety read-only checks, preventing"
 		" writes\n");
 	printf("  --section=name\tOnly run specified section in job file\n");
@@ -1789,6 +1797,18 @@
 			do_exit++;
 			exit_val = fio_monotonic_clocktest();
 			break;
+		case 'L': {
+			long long val;
+
+			if (check_str_time(optarg, &val)) {
+				log_err("fio: failed parsing time %s\n", optarg);
+				do_exit++;
+				exit_val = 1;
+				break;
+			}
+			status_interval = val * 1000;
+			break;
+			}
 		case '?':
 			log_err("%s: unrecognized option '%s'\n", argv[0],
 							argv[optind - 1]);
diff --git a/parse.c b/parse.c
index e8b628c..63c94e3 100644
--- a/parse.c
+++ b/parse.c
@@ -285,7 +285,7 @@
 	return str_to_decimal(p, val, 1, data);
 }
 
-static int check_str_time(const char *p, long long *val)
+int check_str_time(const char *p, long long *val)
 {
 	return str_to_decimal(p, val, 0, NULL);
 }
diff --git a/parse.h b/parse.h
index b9da7b9..cf15ce0 100644
--- a/parse.h
+++ b/parse.h
@@ -89,6 +89,7 @@
 extern void strip_blank_end(char *);
 extern int str_to_decimal(const char *, long long *, int, void *);
 extern int check_str_bytes(const char *p, long long *val, void *data);
+extern int check_str_time(const char *p, long long *val);
 extern int str_to_float(const char *str, double *val);
 
 /*
diff --git a/stat.c b/stat.c
index 3db0612..7ff7ad4 100644
--- a/stat.c
+++ b/stat.c
@@ -1420,6 +1420,40 @@
 	pthread_detach(thread);
 }
 
+static int status_interval_init;
+static struct timeval status_time;
+
+#define FIO_STATUS_FILE		"/tmp/fio-dump-status"
+
+static int check_status_file(void)
+{
+	struct stat sb;
+
+	if (stat(FIO_STATUS_FILE, &sb))
+		return 0;
+
+	unlink(FIO_STATUS_FILE);
+	return 1;
+}
+
+void check_for_running_stats(void)
+{
+	if (status_interval) {
+		if (!status_interval_init) {
+			fio_gettime(&status_time, NULL);
+			status_interval_init = 1;
+		} else if (mtime_since_now(&status_time) >= status_interval) {
+			show_running_run_stats();
+			fio_gettime(&status_time, NULL);
+			return;
+		}
+	}
+	if (check_status_file()) {
+		show_running_run_stats();
+		return;
+	}
+}
+
 static inline void add_stat_sample(struct io_stat *is, unsigned long data)
 {
 	double val = data;
diff --git a/stat.h b/stat.h
index 0125f57..b1bf5dc 100644
--- a/stat.h
+++ b/stat.h
@@ -205,6 +205,7 @@
 extern void display_thread_status(struct jobs_eta *je);
 extern void show_run_stats(void);
 extern void show_running_run_stats(void);
+extern void check_for_running_stats(void);
 extern void sum_thread_stats(struct thread_stat *dst, struct thread_stat *src, int nr);
 extern void sum_group_stats(struct group_run_stats *dst, struct group_run_stats *src);
 extern void init_thread_stat(struct thread_stat *ts);