Handle percentile lists with higher precision that 2 digits

We cap the output at %2.2f right now, that's not always enough.
Make the parser check and store the precision required to
output the list correctly.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/fio.h b/fio.h
index 370ddaa..43f4854 100644
--- a/fio.h
+++ b/fio.h
@@ -248,6 +248,7 @@
 	unsigned int trim_zero;
 	unsigned long long trim_backlog;
 	unsigned int clat_percentiles;
+	unsigned int percentile_precision;	/* digits after decimal for percentiles */
 	fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 	char *read_iolog_file;
diff --git a/init.c b/init.c
index 52665f0..60ba299 100644
--- a/init.c
+++ b/init.c
@@ -867,6 +867,7 @@
 	td->mutex = fio_mutex_init(FIO_MUTEX_LOCKED);
 
 	td->ts.clat_percentiles = td->o.clat_percentiles;
+	td->ts.percentile_precision = td->o.percentile_precision;
 	memcpy(td->ts.percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
 
 	for (i = 0; i < DDIR_RWDIR_CNT; i++) {
diff --git a/options.c b/options.c
index 4522fe4..42a2ea0 100644
--- a/options.c
+++ b/options.c
@@ -2434,6 +2434,7 @@
 		.name	= "percentile_list",
 		.type	= FIO_OPT_FLOAT_LIST,
 		.off1	= td_var_offset(percentile_list),
+		.off2	= td_var_offset(percentile_precision),
 		.help	= "Specify a custom list of percentiles to report",
 		.def    = "1:5:10:20:30:40:50:60:70:80:90:95:99:99.5:99.9:99.95:99.99",
 		.maxlen	= FIO_IO_U_LIST_MAX_LEN,
diff --git a/parse.c b/parse.c
index 4ce29c1..15aeb0a 100644
--- a/parse.c
+++ b/parse.c
@@ -369,10 +369,11 @@
 	long ul1, ul2;
 	double uf;
 	char **cp = NULL;
+	char *cp2;
 	int ret = 0, is_time = 0;
 	const struct value_pair *vp;
 	struct value_pair posval[PARSE_MAX_VP];
-	int i, all_skipped = 1;
+	int i, len, all_skipped = 1;
 
 	dprint(FD_PARSE, "__handle_option=%s, type=%d, ptr=%s\n", o->name,
 							o->type, ptr);
@@ -502,6 +503,19 @@
 		break;
 	}
 	case FIO_OPT_FLOAT_LIST: {
+		if (first) {
+			/*
+			** Initialize precision to 0 and zero out list
+			** in case specified list is shorter than default
+			*/
+			ul2 = 0;
+			ilp = td_var(data, o->off2);
+			*ilp = ul2;
+
+			flp = td_var(data, o->off1);
+			for(i = 0; i < o->maxlen; i++)
+				flp[i].u.f = 0.0;
+		}
 		if (curr >= o->maxlen) {
 			log_err("the list exceeding max length %d\n",
 					o->maxlen);
@@ -525,6 +539,23 @@
 		flp = td_var(data, o->off1);
 		flp[curr].u.f = uf;
 
+		/*
+		** Calculate precision for output by counting
+		** number of digits after period. Find first
+		** period in entire remaining list each time
+		*/
+		cp2 = strchr(ptr, '.');
+		if (cp2 != NULL) {
+			len = 0;
+
+			while (*++cp2 != '\0' && *cp2 >= '0' && *cp2 <= '9')
+				len++;
+
+			ilp = td_var(data, o->off2);
+			if (len > *ilp)
+				*ilp = len;
+		}
+
 		break;
 	}
 	case FIO_OPT_STR_STORE: {
diff --git a/stat.c b/stat.c
index fb5ff64..c523f5c 100644
--- a/stat.c
+++ b/stat.c
@@ -182,11 +182,12 @@
  * Find and display the p-th percentile of clat
  */
 static void show_clat_percentiles(unsigned int *io_u_plat, unsigned long nr,
-				  fio_fp64_t *plist)
+				  fio_fp64_t *plist, uint64_t precision)
 {
 	unsigned int len, j = 0, minv, maxv;
 	unsigned int *ovals;
 	int is_last, scale_down;
+	char buf1[32], buf2[32];
 
 	len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
 	if (!len)
@@ -204,8 +205,10 @@
 		log_info("    clat percentiles (usec):\n     |");
 	}
 
+	snprintf(buf1, sizeof(buf1), " %%1.%luf", precision);
+	snprintf(buf2, sizeof(buf1), "%%1.%luf", precision);
 	for (j = 0; j < len; j++) {
-		char fbuf[8];
+		char fbuf[16];
 
 		/* for formatting */
 		if (j != 0 && (j % 4) == 0)
@@ -215,9 +218,9 @@
 		is_last = (j == len - 1);
 
 		if (plist[j].u.f < 10.0)
-			sprintf(fbuf, " %2.2f", plist[j].u.f);
+			snprintf(fbuf, sizeof(fbuf), buf1, plist[j].u.f);
 		else
-			sprintf(fbuf, "%2.2f", plist[j].u.f);
+			snprintf(fbuf, sizeof(fbuf), buf2, plist[j].u.f);
 
 		if (scale_down)
 			ovals[j] = (ovals[j] + 999) / 1000;
@@ -440,7 +443,8 @@
 	if (ts->clat_percentiles) {
 		show_clat_percentiles(ts->io_u_plat[ddir],
 					ts->clat_stat[ddir].samples,
-					ts->percentile_list);
+					ts->percentile_list,
+					ts->percentile_precision);
 	}
 	if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
 		double p_of_agg = 100.0;
@@ -655,7 +659,7 @@
 			log_info(";0%%=0");
 			continue;
 		}
-		log_info(";%2.2f%%=%u", ts->percentile_list[i].u.f, ovals[i]);
+		log_info(";%f%%=%u", ts->percentile_list[i].u.f, ovals[i]);
 	}
 
 	if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
@@ -753,7 +757,7 @@
 			json_object_add_value_int(percentile_object, "0.00", 0);
 			continue;
 		}
-		snprintf(buf, sizeof(buf), "%2.2f", ts->percentile_list[i].u.f);
+		snprintf(buf, sizeof(buf), "%f", ts->percentile_list[i].u.f);
 		json_object_add_value_int(percentile_object, (const char *)buf, ovals[i]);
 	}
 
@@ -1210,6 +1214,7 @@
 		ts = &threadstats[j];
 
 		ts->clat_percentiles = td->o.clat_percentiles;
+		ts->percentile_precision = td->o.percentile_precision;
 		memcpy(ts->percentile_list, td->o.percentile_list, sizeof(td->o.percentile_list));
 
 		idx++;
diff --git a/stat.h b/stat.h
index 97186c1..98ae4c8 100644
--- a/stat.h
+++ b/stat.h
@@ -144,6 +144,7 @@
 	 * IO depth and latency stats
 	 */
 	uint64_t clat_percentiles;
+	uint64_t percentile_precision;
 	fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN];
 
 	uint32_t io_u_map[FIO_IO_U_MAP_NR];