+ show_lat_u(io_u_lat_u, out);
+ show_lat_m(io_u_lat_m, out);
+}
+
+static int block_state_category(int block_state)
+{
+ switch (block_state) {
+ case BLOCK_STATE_UNINIT:
+ return 0;
+ case BLOCK_STATE_TRIMMED:
+ case BLOCK_STATE_WRITTEN:
+ return 1;
+ case BLOCK_STATE_WRITE_FAILURE:
+ case BLOCK_STATE_TRIM_FAILURE:
+ return 2;
+ default:
+ /* Silence compile warning on some BSDs and have a return */
+ assert(0);
+ return -1;
+ }
+}
+
+static int compare_block_infos(const void *bs1, const void *bs2)
+{
+ uint32_t block1 = *(uint32_t *)bs1;
+ uint32_t block2 = *(uint32_t *)bs2;
+ int state1 = BLOCK_INFO_STATE(block1);
+ int state2 = BLOCK_INFO_STATE(block2);
+ int bscat1 = block_state_category(state1);
+ int bscat2 = block_state_category(state2);
+ int cycles1 = BLOCK_INFO_TRIMS(block1);
+ int cycles2 = BLOCK_INFO_TRIMS(block2);
+
+ if (bscat1 < bscat2)
+ return -1;
+ if (bscat1 > bscat2)
+ return 1;
+
+ if (cycles1 < cycles2)
+ return -1;
+ if (cycles1 > cycles2)
+ return 1;
+
+ if (state1 < state2)
+ return -1;
+ if (state1 > state2)
+ return 1;
+
+ assert(block1 == block2);
+ return 0;
+}
+
+static int calc_block_percentiles(int nr_block_infos, uint32_t *block_infos,
+ fio_fp64_t *plist, unsigned int **percentiles,
+ unsigned int *types)
+{
+ int len = 0;
+ int i, nr_uninit;
+
+ qsort(block_infos, nr_block_infos, sizeof(uint32_t), compare_block_infos);
+
+ while (len < FIO_IO_U_LIST_MAX_LEN && plist[len].u.f != 0.0)
+ len++;
+
+ if (!len)
+ return 0;
+
+ /*
+ * Sort the percentile list. Note that it may already be sorted if
+ * we are using the default values, but since it's a short list this
+ * isn't a worry. Also note that this does not work for NaN values.
+ */
+ if (len > 1)
+ qsort((void *)plist, len, sizeof(plist[0]), double_cmp);
+
+ nr_uninit = 0;
+ /* Start only after the uninit entries end */
+ for (nr_uninit = 0;
+ nr_uninit < nr_block_infos
+ && BLOCK_INFO_STATE(block_infos[nr_uninit]) == BLOCK_STATE_UNINIT;
+ nr_uninit ++)
+ ;
+
+ if (nr_uninit == nr_block_infos)
+ return 0;
+
+ *percentiles = calloc(len, sizeof(**percentiles));
+
+ for (i = 0; i < len; i++) {
+ int idx = (plist[i].u.f * (nr_block_infos - nr_uninit) / 100)
+ + nr_uninit;
+ (*percentiles)[i] = BLOCK_INFO_TRIMS(block_infos[idx]);
+ }
+
+ memset(types, 0, sizeof(*types) * BLOCK_STATE_COUNT);
+ for (i = 0; i < nr_block_infos; i++)
+ types[BLOCK_INFO_STATE(block_infos[i])]++;
+
+ return len;
+}
+
+static const char *block_state_names[] = {
+ [BLOCK_STATE_UNINIT] = "unwritten",
+ [BLOCK_STATE_TRIMMED] = "trimmed",
+ [BLOCK_STATE_WRITTEN] = "written",
+ [BLOCK_STATE_TRIM_FAILURE] = "trim failure",
+ [BLOCK_STATE_WRITE_FAILURE] = "write failure",
+};
+
+static void show_block_infos(int nr_block_infos, uint32_t *block_infos,
+ fio_fp64_t *plist, struct buf_output *out)
+{
+ int len, pos, i;
+ unsigned int *percentiles = NULL;
+ unsigned int block_state_counts[BLOCK_STATE_COUNT];
+
+ len = calc_block_percentiles(nr_block_infos, block_infos, plist,
+ &percentiles, block_state_counts);
+
+ log_buf(out, " block lifetime percentiles :\n |");
+ pos = 0;
+ for (i = 0; i < len; i++) {
+ uint32_t block_info = percentiles[i];
+#define LINE_LENGTH 75
+ char str[LINE_LENGTH];
+ int strln = snprintf(str, LINE_LENGTH, " %3.2fth=%u%c",
+ plist[i].u.f, block_info,
+ i == len - 1 ? '\n' : ',');
+ assert(strln < LINE_LENGTH);
+ if (pos + strln > LINE_LENGTH) {
+ pos = 0;
+ log_buf(out, "\n |");
+ }
+ log_buf(out, "%s", str);
+ pos += strln;
+#undef LINE_LENGTH
+ }
+ if (percentiles)
+ free(percentiles);
+
+ log_buf(out, " states :");
+ for (i = 0; i < BLOCK_STATE_COUNT; i++)
+ log_buf(out, " %s=%u%c",
+ block_state_names[i], block_state_counts[i],
+ i == BLOCK_STATE_COUNT - 1 ? '\n' : ',');
+}
+
+static void show_ss_normal(struct thread_stat *ts, struct buf_output *out)
+{
+ char *p1, *p2;
+ unsigned long long bw_mean, iops_mean;
+ const int i2p = is_power_of_2(ts->kb_base);
+
+ if (!ts->ss_dur)
+ return;
+
+ bw_mean = steadystate_bw_mean(ts);
+ iops_mean = steadystate_iops_mean(ts);
+
+ p1 = num2str(bw_mean / ts->kb_base, 6, ts->kb_base, i2p, ts->unit_base);
+ p2 = num2str(iops_mean, 6, 1, 0, 0);
+
+ log_buf(out, " steadystate : attained=%s, bw=%s/s, iops=%s, %s%s=%.3f%s\n",
+ ts->ss_state & __FIO_SS_ATTAINED ? "yes" : "no",
+ p1, p2,
+ ts->ss_state & __FIO_SS_IOPS ? "iops" : "bw",
+ ts->ss_state & __FIO_SS_SLOPE ? " slope": " mean dev",
+ ts->ss_criterion.u.f,
+ ts->ss_state & __FIO_SS_PCT ? "%" : "");
+
+ free(p1);
+ free(p2);