From: Jens Axboe Date: Mon, 24 Sep 2012 06:51:24 +0000 (+0200) Subject: Merge branch 'master' into gfio X-Git-Tag: fio-2.1~57^2~61 X-Git-Url: https://git.kernel.dk/?p=fio.git;a=commitdiff_plain;h=d79db1222039e906dd49ae290daa59701f4e2385 Merge branch 'master' into gfio Conflicts: Makefile backend.c client.c fio.h init.c io_ddir.h options.c server.h stat.c stat.h Signed-off-by: Jens Axboe --- d79db1222039e906dd49ae290daa59701f4e2385 diff --cc Makefile index ea8c851e,ccfa802b..d851640a --- a/Makefile +++ b/Makefile @@@ -18,7 -15,7 +18,7 @@@ SOURCE := gettime.c ioengines.c init.c lib/num2str.c lib/ieee754.c $(wildcard crc/*.c) engines/cpu.c \ engines/mmap.c engines/sync.c engines/null.c engines/net.c \ memalign.c server.c client.c iolog.c backend.c libfio.c flow.c \ - cconv.c lib/prio_tree.c - json.c ++ cconv.c lib/prio_tree.c json.c ifeq ($(UNAME), Linux) SOURCE += diskutil.c fifo.c blktrace.c helpers.c cgroup.c trim.c \ diff --cc backend.c index f20857a0,ce0a0098..a0ac424c --- a/backend.c +++ b/backend.c @@@ -57,10 -57,8 +57,10 @@@ static struct flist_head *cgroup_list static char *cgroup_mnt; static int exit_value; static volatile int fio_abort; +static unsigned int nr_process = 0; +static unsigned int nr_thread = 0; - struct io_log *agg_io_log[2]; + struct io_log *agg_io_log[DDIR_RWDIR_CNT]; int groupid = 0; unsigned int thread_number = 0; @@@ -1156,14 -1153,14 +1166,16 @@@ static void *thread_main(void *data } update_rusage_stat(td); - td->ts.runtime[0] = (td->ts.runtime[0] + 999) / 1000; - td->ts.runtime[1] = (td->ts.runtime[1] + 999) / 1000; + td->ts.runtime[DDIR_READ] = (td->ts.runtime[DDIR_READ] + 999) / 1000; + td->ts.runtime[DDIR_WRITE] = (td->ts.runtime[DDIR_WRITE] + 999) / 1000; + td->ts.runtime[DDIR_TRIM] = (td->ts.runtime[DDIR_TRIM] + 999) / 1000; td->ts.total_run_time = mtime_since_now(&td->epoch); - td->ts.io_bytes[0] = td->io_bytes[0]; - td->ts.io_bytes[1] = td->io_bytes[1]; + td->ts.io_bytes[DDIR_READ] = td->io_bytes[DDIR_READ]; + td->ts.io_bytes[DDIR_WRITE] = td->io_bytes[DDIR_WRITE]; + td->ts.io_bytes[DDIR_TRIM] = td->io_bytes[DDIR_TRIM]; + fio_unpin_memory(td); + fio_mutex_down(writeout_mutex); if (td->bw_log) { if (td->o.bw_log_file) { @@@ -1381,15 -1381,7 +1393,15 @@@ static void run_threads(void set_sig_handlers(); + nr_thread = nr_process = 0; + for_each_td(td, i) { + if (td->o.use_thread) + nr_thread++; + else + nr_process++; + } + - if (!terse_output) { + if (output_format == FIO_OUTPUT_NORMAL) { log_info("Starting "); if (nr_thread) log_info("%d thread%s", nr_thread, @@@ -1668,8 -1669,9 +1680,9 @@@ int fio_backend(void return 0; if (write_bw_log) { - setup_log(&agg_io_log[DDIR_READ], 0); - setup_log(&agg_io_log[DDIR_WRITE], 0); - setup_log(&agg_io_log[DDIR_TRIM], 0); + setup_log(&agg_io_log[DDIR_READ], 0, IO_LOG_TYPE_BW); + setup_log(&agg_io_log[DDIR_WRITE], 0, IO_LOG_TYPE_BW); ++ setup_log(&agg_io_log[DDIR_TRIM], 0, IO_LOG_TYPE_BW); } startup_mutex = fio_mutex_init(FIO_MUTEX_LOCKED); diff --cc eta.c index f491feaa,075ce8c8..600b046c --- a/eta.c +++ b/eta.c @@@ -286,17 -296,24 +296,24 @@@ int calc_thread_status(struct jobs_eta || td->runstate == TD_PRE_READING) { je->nr_running++; if (td_read(td)) { - je->t_rate += td->o.rate[DDIR_READ]; - je->t_iops += td->o.rate_iops[DDIR_READ]; - je->m_rate += td->o.ratemin[DDIR_READ]; - je->m_iops += td->o.rate_iops_min[DDIR_READ]; + je->t_rate[0] += td->o.rate[DDIR_READ]; + je->t_iops[0] += td->o.rate_iops[DDIR_READ]; + je->m_rate[0] += td->o.ratemin[DDIR_READ]; + je->m_iops[0] += td->o.rate_iops_min[DDIR_READ]; } if (td_write(td)) { - je->t_rate += td->o.rate[DDIR_WRITE]; - je->t_iops += td->o.rate_iops[DDIR_WRITE]; - je->m_rate += td->o.ratemin[DDIR_WRITE]; - je->m_iops += td->o.rate_iops_min[DDIR_WRITE]; + je->t_rate[1] += td->o.rate[DDIR_WRITE]; + je->t_iops[1] += td->o.rate_iops[DDIR_WRITE]; + je->m_rate[1] += td->o.ratemin[DDIR_WRITE]; + je->m_iops[1] += td->o.rate_iops_min[DDIR_WRITE]; } + if (td_trim(td)) { - je->t_rate += td->o.rate[DDIR_TRIM]; - je->t_iops += td->o.rate_iops[DDIR_TRIM]; - je->m_rate += td->o.ratemin[DDIR_TRIM]; - je->m_iops += td->o.rate_iops_min[DDIR_TRIM]; ++ je->t_rate[2] += td->o.rate[DDIR_TRIM]; ++ je->t_iops[2] += td->o.rate_iops[DDIR_TRIM]; ++ je->m_rate[2] += td->o.ratemin[DDIR_TRIM]; ++ je->m_iops[2] += td->o.rate_iops_min[DDIR_TRIM]; + } + je->files_open += td->nr_open_files; } else if (td->runstate == TD_RAMP) { je->nr_running++; @@@ -390,17 -409,15 +409,18 @@@ void display_thread_status(struct jobs_ p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); free(tr); free(mr); - } else if (je->m_iops || je->t_iops) - p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); + } else if (je->m_iops[0] || je->m_iops[1] || je->t_iops[0] || je->t_iops[1]) { + p += sprintf(p, ", CR=%d/%d IOPS", + je->t_iops[0] + je->t_iops[1], + je->m_iops[0] + je->m_iops[1]); + } if (je->eta_sec != INT_MAX && je->nr_running) { char perc_str[32]; - char *iops_str[2]; - char *rate_str[2]; + char *iops_str[DDIR_RWDIR_CNT]; + char *rate_str[DDIR_RWDIR_CNT]; size_t left; int l; + int ddir; if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) strcpy(perc_str, "-.-% done"); diff --cc fio.h index 95d9d77c,b2bbe93f..4b3c63b7 --- a/fio.h +++ b/fio.h @@@ -310,10 -523,12 +310,10 @@@ enum extern int exitall_on_terminate; extern unsigned int thread_number; -extern unsigned int nr_process, nr_thread; extern int shm_id; extern int groupid; - extern int terse_output; + extern int output_format; extern int temp_stall_ts; -extern unsigned long long mlock_size; extern uintptr_t page_mask, page_size; extern int read_only; extern int eta_print; @@@ -551,6 -761,10 +553,12 @@@ static inline void td_io_u_free_notify( extern const char *fio_get_arch_string(int); extern const char *fio_get_os_string(int); +#define ARRAY_SIZE(x) (sizeof((x)) / (sizeof((x)[0]))) + + enum { + FIO_OUTPUT_TERSE = 0, + FIO_OUTPUT_JSON, + FIO_OUTPUT_NORMAL, + }; + #endif diff --cc gclient.c index 78a5c361,00000000..f317dc70 mode 100644,000000..100644 --- a/gclient.c +++ b/gclient.c @@@ -1,1387 -1,0 +1,1387 @@@ +#include +#include + +#include +#include +#include + +#include "fio.h" +#include "gfio.h" +#include "ghelpers.h" +#include "goptions.h" +#include "gerror.h" +#include "graph.h" +#include "gclient.h" +#include "printing.h" + +static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, + struct group_run_stats *rs); + +static gboolean results_window_delete(GtkWidget *w, gpointer data) +{ + struct gui_entry *ge = (struct gui_entry *) data; + + gtk_widget_destroy(w); + ge->results_window = NULL; + ge->results_notebook = NULL; + return TRUE; +} + +static void results_close(GtkWidget *w, gpointer *data) +{ + struct gui_entry *ge = (struct gui_entry *) data; + + gtk_widget_destroy(ge->results_window); +} + +static void results_print(GtkWidget *w, gpointer *data) +{ + struct gui_entry *ge = (struct gui_entry *) data; + + gfio_print_results(ge); +} + +static GtkActionEntry results_menu_items[] = { + { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL}, + { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL}, + { "PrintFile", GTK_STOCK_PRINT, "Print", "P", NULL, G_CALLBACK(results_print) }, + { "CloseFile", GTK_STOCK_CLOSE, "Close", "W", NULL, G_CALLBACK(results_close) }, +}; +static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]); + +static const gchar *results_ui_string = " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +"; + +static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge) +{ + GtkActionGroup *action_group; + GtkWidget *widget; + GError *error = 0; + + ge->results_uimanager = gtk_ui_manager_new(); + + action_group = gtk_action_group_new("ResultsMenu"); + gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge); + + gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0); + gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error); + + gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager)); + + widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu"); + return widget; +} + +static GtkWidget *get_results_window(struct gui_entry *ge) +{ + GtkWidget *win, *notebook, *vbox; + + if (ge->results_window) + return ge->results_notebook; + + win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(win), "Results"); + gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768); + g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge); + g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(win), vbox); + + ge->results_menu = get_results_menubar(win, ge); + gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0); + + notebook = gtk_notebook_new(); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1); + gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook)); + gtk_container_add(GTK_CONTAINER(vbox), notebook); + + ge->results_window = win; + ge->results_notebook = notebook; + return ge->results_notebook; +} + +static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload; + struct gfio_client *gc = client->client_data; + struct gui_entry *ge = gc->ge; + struct gui *ui = ge->ui; + GtkTreeIter iter; + struct tm *tm; + time_t sec; + char tmp[64], timebuf[80]; + + sec = p->log_sec; + tm = localtime(&sec); + strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm); + sprintf(timebuf, "%s.%03ld", tmp, (long) p->log_usec / 1000); + + gdk_threads_enter(); + + gtk_list_store_append(ui->log_model, &iter); + gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1); + gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1); + gtk_list_store_set(ui->log_model, &iter, 2, log_get_level(p->level), -1); + gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1); + + if (p->level == FIO_LOG_ERR) + gfio_view_log(ui); + + gdk_threads_leave(); +} + +static void disk_util_destroy(GtkWidget *w, gpointer data) +{ + struct gui_entry *ge = (struct gui_entry *) data; + + ge->disk_util_vbox = NULL; + gtk_widget_destroy(w); +} + +static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge) +{ + GtkWidget *vbox, *box, *scroll, *res_notebook; + + if (ge->disk_util_vbox) + return ge->disk_util_vbox; + + scroll = get_scrolled_window(5); + vbox = gtk_vbox_new(FALSE, 3); + box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5); + + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox); + res_notebook = get_results_window(ge); + + gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization")); + ge->disk_util_vbox = box; + g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge); + + return ge->disk_util_vbox; +} + +static int __gfio_disk_util_show(GtkWidget *res_notebook, + struct gfio_client *gc, struct cmd_du_pdu *p) +{ + GtkWidget *box, *frame, *entry, *vbox, *util_vbox; + struct gui_entry *ge = gc->ge; + double util; + char tmp[16]; + + util_vbox = gfio_disk_util_get_vbox(ge); + + vbox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(util_vbox), vbox); + + frame = gtk_frame_new((char *) p->dus.name); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2); + + box = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), box); + + frame = gtk_frame_new("Read"); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); + vbox = gtk_hbox_new(TRUE, 3); + gtk_container_add(GTK_CONTAINER(frame), vbox); + entry = new_info_entry_in_frame(vbox, "IOs"); + entry_set_int_value(entry, p->dus.ios[0]); + entry = new_info_entry_in_frame(vbox, "Merges"); + entry_set_int_value(entry, p->dus.merges[0]); + entry = new_info_entry_in_frame(vbox, "Sectors"); + entry_set_int_value(entry, p->dus.sectors[0]); + entry = new_info_entry_in_frame(vbox, "Ticks"); + entry_set_int_value(entry, p->dus.ticks[0]); + + frame = gtk_frame_new("Write"); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); + vbox = gtk_hbox_new(TRUE, 3); + gtk_container_add(GTK_CONTAINER(frame), vbox); + entry = new_info_entry_in_frame(vbox, "IOs"); + entry_set_int_value(entry, p->dus.ios[1]); + entry = new_info_entry_in_frame(vbox, "Merges"); + entry_set_int_value(entry, p->dus.merges[1]); + entry = new_info_entry_in_frame(vbox, "Sectors"); + entry_set_int_value(entry, p->dus.sectors[1]); + entry = new_info_entry_in_frame(vbox, "Ticks"); + entry_set_int_value(entry, p->dus.ticks[1]); + + frame = gtk_frame_new("Shared"); + gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2); + vbox = gtk_hbox_new(TRUE, 3); + gtk_container_add(GTK_CONTAINER(frame), vbox); + entry = new_info_entry_in_frame(vbox, "IO ticks"); + entry_set_int_value(entry, p->dus.io_ticks); + entry = new_info_entry_in_frame(vbox, "Time in queue"); + entry_set_int_value(entry, p->dus.time_in_queue); + + util = 0.0; + if (p->dus.msec) + util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec; + if (util > 100.0) + util = 100.0; + + sprintf(tmp, "%3.2f%%", util); + entry = new_info_entry_in_frame(vbox, "Disk utilization"); + gtk_entry_set_text(GTK_ENTRY(entry), tmp); + + gtk_widget_show_all(ge->results_window); + return 0; +} + +static int gfio_disk_util_show(struct gfio_client *gc) +{ + struct gui_entry *ge = gc->ge; + GtkWidget *res_notebook; + int i; + + if (!gc->nr_du) + return 1; + + res_notebook = get_results_window(ge); + + for (i = 0; i < gc->nr_du; i++) { + struct cmd_du_pdu *p = &gc->du[i]; + + __gfio_disk_util_show(res_notebook, gc, p); + } + + gtk_widget_show_all(ge->results_window); + return 0; +} + +static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload; + struct gfio_client *gc = client->client_data; + struct gui_entry *ge = gc->ge; + unsigned int nr = gc->nr_du; + + gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu)); + memcpy(&gc->du[nr], p, sizeof(*p)); + gc->nr_du++; + + gdk_threads_enter(); + if (ge->results_window) + __gfio_disk_util_show(ge->results_notebook, gc, p); + else + gfio_disk_util_show(gc); + gdk_threads_leave(); +} + +extern int sum_stat_clients; +extern struct thread_stat client_ts; +extern struct group_run_stats client_gs; + +static int sum_stat_nr; + +static void gfio_thread_status_op(struct fio_client *client, + struct fio_net_cmd *cmd) +{ + struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload; + + gfio_display_ts(client, &p->ts, &p->rs); + + if (sum_stat_clients == 1) + return; + + sum_thread_stats(&client_ts, &p->ts, sum_stat_nr); + sum_group_stats(&client_gs, &p->rs); + + client_ts.members++; + client_ts.thread_number = p->ts.thread_number; + client_ts.groupid = p->ts.groupid; + + if (++sum_stat_nr == sum_stat_clients) { + strcpy(client_ts.name, "All clients"); + gfio_display_ts(client, &client_ts, &client_gs); + } +} + +static void gfio_group_stats_op(struct fio_client *client, + struct fio_net_cmd *cmd) +{ + /* We're ignoring group stats for now */ +} + +static void gfio_update_thread_status(struct gui_entry *ge, + char *status_message, double perc) +{ + static char message[100]; + const char *m = message; + + strncpy(message, status_message, sizeof(message) - 1); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0); + gtk_widget_queue_draw(ge->ui->window); +} + +static void gfio_update_thread_status_all(struct gui *ui, char *status_message, + double perc) +{ + static char message[100]; + const char *m = message; + + strncpy(message, status_message, sizeof(message) - 1); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0); + gtk_widget_queue_draw(ui->window); +} + +/* + * Client specific ETA + */ +static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je) +{ + struct gfio_client *gc = client->client_data; + struct gui_entry *ge = gc->ge; + static int eta_good; + char eta_str[128]; + char output[256]; + char tmp[32]; + double perc = 0.0; + int i2p = 0; + + gdk_threads_enter(); + + eta_str[0] = '\0'; + output[0] = '\0'; + + if (je->eta_sec != INT_MAX && je->elapsed_sec) { + perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); + eta_to_str(eta_str, je->eta_sec); + } + + sprintf(tmp, "%u", je->nr_running); + gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp); + sprintf(tmp, "%u", je->files_open); + gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp); + +#if 0 + if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { + if (je->m_rate || je->t_rate) { + char *tr, *mr; + + mr = num2str(je->m_rate, 4, 0, i2p); + tr = num2str(je->t_rate, 4, 0, i2p); + gtk_entry_set_text(GTK_ENTRY(ge->eta); + p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); + free(tr); + free(mr); + } else if (je->m_iops || je->t_iops) + p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); + + gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---"); + gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---"); + gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---"); + gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---"); +#endif + + if (je->eta_sec != INT_MAX && je->nr_running) { + char *iops_str[2]; + char *rate_str[2]; + + if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) + strcpy(output, "-.-% done"); + else { + eta_good = 1; + perc *= 100.0; + sprintf(output, "%3.1f%% done", perc); + } + + rate_str[0] = num2str(je->rate[0], 5, 10, i2p); + rate_str[1] = num2str(je->rate[1], 5, 10, i2p); + + iops_str[0] = num2str(je->iops[0], 4, 1, 0); + iops_str[1] = num2str(je->iops[1], 4, 1, 0); + + gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]); + gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]); + gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]); + gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]); + + graph_add_xy_data(ge->graphs.iops_graph, ge->graphs.read_iops, je->elapsed_sec, je->iops[0], iops_str[0]); + graph_add_xy_data(ge->graphs.iops_graph, ge->graphs.write_iops, je->elapsed_sec, je->iops[1], iops_str[1]); + graph_add_xy_data(ge->graphs.bandwidth_graph, ge->graphs.read_bw, je->elapsed_sec, je->rate[0], rate_str[0]); + graph_add_xy_data(ge->graphs.bandwidth_graph, ge->graphs.write_bw, je->elapsed_sec, je->rate[1], rate_str[1]); + + free(rate_str[0]); + free(rate_str[1]); + free(iops_str[0]); + free(iops_str[1]); + } + + if (eta_str[0]) { + char *dst = output + strlen(output); + + sprintf(dst, " - %s", eta_str); + } + + gfio_update_thread_status(ge, output, perc); + gdk_threads_leave(); +} + +/* + * Update ETA in main window for all clients + */ +static void gfio_update_all_eta(struct jobs_eta *je) +{ + struct gui *ui = &main_ui; + static int eta_good; + char eta_str[128]; + char output[256]; + double perc = 0.0; + int i2p = 0; + + gdk_threads_enter(); + + eta_str[0] = '\0'; + output[0] = '\0'; + + if (je->eta_sec != INT_MAX && je->elapsed_sec) { + perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec); + eta_to_str(eta_str, je->eta_sec); + } + +#if 0 + if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) { + if (je->m_rate || je->t_rate) { + char *tr, *mr; + + mr = num2str(je->m_rate, 4, 0, i2p); + tr = num2str(je->t_rate, 4, 0, i2p); + gtk_entry_set_text(GTK_ENTRY(ui->eta); + p += sprintf(p, ", CR=%s/%s KB/s", tr, mr); + free(tr); + free(mr); + } else if (je->m_iops || je->t_iops) + p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops); + + gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---"); + gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---"); + gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---"); + gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---"); +#endif + + entry_set_int_value(ui->eta.jobs, je->nr_running); + + if (je->eta_sec != INT_MAX && je->nr_running) { + char *iops_str[2]; + char *rate_str[2]; + + if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running) + strcpy(output, "-.-% done"); + else { + eta_good = 1; + perc *= 100.0; + sprintf(output, "%3.1f%% done", perc); + } + + rate_str[0] = num2str(je->rate[0], 5, 10, i2p); + rate_str[1] = num2str(je->rate[1], 5, 10, i2p); + + iops_str[0] = num2str(je->iops[0], 4, 1, 0); + iops_str[1] = num2str(je->iops[1], 4, 1, 0); + + gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]); + gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]); + gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]); + gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]); + + graph_add_xy_data(ui->graphs.iops_graph, ui->graphs.read_iops, je->elapsed_sec, je->iops[0], iops_str[0]); + graph_add_xy_data(ui->graphs.iops_graph, ui->graphs.write_iops, je->elapsed_sec, je->iops[1], iops_str[1]); + graph_add_xy_data(ui->graphs.bandwidth_graph, ui->graphs.read_bw, je->elapsed_sec, je->rate[0], rate_str[0]); + graph_add_xy_data(ui->graphs.bandwidth_graph, ui->graphs.write_bw, je->elapsed_sec, je->rate[1], rate_str[1]); + + free(rate_str[0]); + free(rate_str[1]); + free(iops_str[0]); + free(iops_str[1]); + } + + if (eta_str[0]) { + char *dst = output + strlen(output); + + sprintf(dst, " - %s", eta_str); + } + + gfio_update_thread_status_all(ui, output, perc); + gdk_threads_leave(); +} + +static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload; + struct gfio_client *gc = client->client_data; + struct gui_entry *ge = gc->ge; + const char *os, *arch; + + os = fio_get_os_string(probe->os); + if (!os) + os = "unknown"; + + arch = fio_get_arch_string(probe->arch); + if (!arch) + os = "unknown"; + + if (!client->name) + client->name = strdup((char *) probe->hostname); + + gc->client_cpus = le32_to_cpu(probe->cpus); + gc->client_flags = le64_to_cpu(probe->flags); + + gdk_threads_enter(); + + gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname); + gtk_label_set_text(GTK_LABEL(ge->probe.os), os); + gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch); + gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), (char *) probe->fio_version); + + gfio_set_state(ge, GE_STATE_CONNECTED); + + gdk_threads_leave(); +} + +static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct gfio_client *gc = client->client_data; + + gdk_threads_enter(); + gfio_set_state(gc->ge, GE_STATE_NEW); + gdk_threads_leave(); +} + +static struct thread_options *gfio_client_add_job(struct gfio_client *gc, + struct thread_options_pack *top) +{ + struct gfio_client_options *gco; + + gco = calloc(1, sizeof(*gco)); + convert_thread_options_to_cpu(&gco->o, top); + INIT_FLIST_HEAD(&gco->list); + flist_add_tail(&gco->list, &gc->o_list); + gc->o_list_nr = 1; + return &gco->o; +} + +static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload; + struct gfio_client *gc = client->client_data; + struct gui_entry *ge = gc->ge; + struct thread_options *o; + char *c1, *c2, *c3, *c4; + char tmp[80]; + + p->thread_number = le32_to_cpu(p->thread_number); + p->groupid = le32_to_cpu(p->groupid); + o = gfio_client_add_job(gc, &p->top); + + gdk_threads_enter(); + + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(ge->eta.names), (gchar *) o->name); + gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0); + + sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir)); + multitext_add_entry(&ge->eta.iotype, tmp); + + c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]); + c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]); + c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]); + c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]); + sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4); + free(c1); + free(c2); + free(c3); + free(c4); + multitext_add_entry(&ge->eta.bs, tmp); + + multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine); + + sprintf(tmp, "%u", o->iodepth); + multitext_add_entry(&ge->eta.iodepth, tmp); + + multitext_set_entry(&ge->eta.iotype, 0); + multitext_set_entry(&ge->eta.bs, 0); + multitext_set_entry(&ge->eta.ioengine, 0); + multitext_set_entry(&ge->eta.iodepth, 0); + + gfio_set_state(ge, GE_STATE_JOB_SENT); + + gdk_threads_leave(); +} + +static void gfio_update_job_op(struct fio_client *client, + struct fio_net_cmd *cmd) +{ + uint32_t *pdu_error = (uint32_t *) cmd->payload; + struct gfio_client *gc = client->client_data; + + gc->update_job_status = le32_to_cpu(*pdu_error); + gc->update_job_done = 1; +} + +static void gfio_client_timed_out(struct fio_client *client) +{ + struct gfio_client *gc = client->client_data; + char buf[256]; + + gdk_threads_enter(); + + gfio_set_state(gc->ge, GE_STATE_NEW); + clear_ge_ui_info(gc->ge); + + sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname); + gfio_report_info(gc->ge->ui, "Network timeout", buf); + + gdk_threads_leave(); +} + +static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct gfio_client *gc = client->client_data; + + gdk_threads_enter(); + + gfio_set_state(gc->ge, GE_STATE_JOB_DONE); + + if (gc->err_entry) + entry_set_int_value(gc->err_entry, client->error); + + gdk_threads_leave(); +} + +static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct gfio_client *gc = client->client_data; + + gdk_threads_enter(); + gfio_set_state(gc->ge, GE_STATE_JOB_STARTED); + gdk_threads_leave(); +} + +static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd) +{ + struct gfio_client *gc = client->client_data; + + gdk_threads_enter(); + gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING); + gdk_threads_leave(); +} + +static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu) +{ + printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples); + free(pdu); +} + +static void gfio_add_total_depths_tree(GtkListStore *model, + struct thread_stat *ts, unsigned int len) +{ + double io_u_dist[FIO_IO_U_MAP_NR]; + GtkTreeIter iter; + /* Bits 1-6, and 8 */ + const int add_mask = 0x17e; + int i, j; + - stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); ++ stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist); + + gtk_list_store_append(model, &iter); + + gtk_list_store_set(model, &iter, 0, "Total", -1); + + for (i = 1, j = 0; i < len; i++) { + char fbuf[32]; + + if (!(add_mask & (1UL << (i - 1)))) + sprintf(fbuf, "0.0%%"); + else { + sprintf(fbuf, "%3.1f%%", io_u_dist[j]); + j++; + } + + gtk_list_store_set(model, &iter, i, fbuf, -1); + } + +} + +static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts, + struct group_run_stats *rs) +{ + unsigned int nr = gc->nr_results; + + gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results)); + memcpy(&gc->results[nr].ts, ts, sizeof(*ts)); + memcpy(&gc->results[nr].gs, rs, sizeof(*rs)); + gc->nr_results++; +} + +static void gfio_add_sc_depths_tree(GtkListStore *model, + struct thread_stat *ts, unsigned int len, + int submit) +{ + double io_u_dist[FIO_IO_U_MAP_NR]; + GtkTreeIter iter; + /* Bits 0, and 3-8 */ + const int add_mask = 0x1f9; + int i, j; + + if (submit) + stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist); + else + stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist); + + gtk_list_store_append(model, &iter); + + gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1); + + for (i = 1, j = 0; i < len; i++) { + char fbuf[32]; + + if (!(add_mask & (1UL << (i - 1)))) + sprintf(fbuf, "0.0%%"); + else { + sprintf(fbuf, "%3.1f%%", io_u_dist[j]); + j++; + } + + gtk_list_store_set(model, &iter, i, fbuf, -1); + } + +} + +static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts) +{ + GtkWidget *frame, *box, *tree_view = NULL; + GtkTreeSelection *selection; + GtkListStore *model; + int i; + const char *labels[] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" }; + const int nr_labels = ARRAY_SIZE(labels); + GType types[nr_labels]; + + frame = gtk_frame_new("IO depths"); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), box); + + for (i = 0; i < nr_labels; i++) + types[i] = G_TYPE_STRING; + + model = gtk_list_store_newv(nr_labels, types); + + tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_widget_set_can_focus(tree_view, FALSE); + + g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, + "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); + gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); + + for (i = 0; i < nr_labels; i++) + tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); + + gfio_add_total_depths_tree(model, ts, nr_labels); + gfio_add_sc_depths_tree(model, ts, nr_labels, 1); + gfio_add_sc_depths_tree(model, ts, nr_labels, 0); + + gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, TRUE, 3); +} + +static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts) +{ + GtkWidget *box, *frame, *entry; + double usr_cpu, sys_cpu; + unsigned long runtime; + char tmp[32]; + + runtime = ts->total_run_time; + if (runtime) { + double runt = (double) runtime; + + usr_cpu = (double) ts->usr_time * 100 / runt; + sys_cpu = (double) ts->sys_time * 100 / runt; + } else { + usr_cpu = 0; + sys_cpu = 0; + } + + frame = gtk_frame_new("OS resources"); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), box); + + entry = new_info_entry_in_frame(box, "User CPU"); + sprintf(tmp, "%3.2f%%", usr_cpu); + gtk_entry_set_text(GTK_ENTRY(entry), tmp); + entry = new_info_entry_in_frame(box, "System CPU"); + sprintf(tmp, "%3.2f%%", sys_cpu); + gtk_entry_set_text(GTK_ENTRY(entry), tmp); + entry = new_info_entry_in_frame(box, "Context switches"); + entry_set_int_value(entry, ts->ctx); + entry = new_info_entry_in_frame(box, "Major faults"); + entry_set_int_value(entry, ts->majf); + entry = new_info_entry_in_frame(box, "Minor faults"); + entry_set_int_value(entry, ts->minf); +} + +static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels, + int num) +{ + GtkWidget *tree_view; + GtkTreeSelection *selection; + GtkListStore *model; + GtkTreeIter iter; + GType *types; + int i; + + types = malloc(num * sizeof(GType)); + + for (i = 0; i < num; i++) + types[i] = G_TYPE_STRING; + + model = gtk_list_store_newv(num, types); + free(types); + types = NULL; + + tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_widget_set_can_focus(tree_view, FALSE); + + g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, + "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); + gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); + + for (i = 0; i < num; i++) + tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE); + + gtk_list_store_append(model, &iter); + + for (i = 0; i < num; i++) { + char fbuf[32]; + + if (lat[i] <= 0.0) + sprintf(fbuf, "0.00"); + else + sprintf(fbuf, "%3.2f%%", lat[i]); + + gtk_list_store_set(model, &iter, i, fbuf, -1); + } + + return tree_view; +} + +static struct graph *setup_lat_bucket_graph(const char *title, double *lat, + const char **labels, + unsigned int len, + double xdim, double ydim) +{ + struct graph *g; + int i; + + g = graph_new(xdim, ydim, gfio_graph_font); + graph_title(g, title); + graph_x_title(g, "Buckets"); + graph_y_title(g, "Percent"); + + for (i = 0; i < len; i++) { + graph_label_t l; + + l = graph_add_label(g, labels[i]); + graph_add_data(g, l, lat[i]); + } + + return g; +} + +static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p) +{ + struct graph *g = p; + cairo_t *cr; + + cr = gdk_cairo_create(gtk_widget_get_window(w)); +#if 0 + if (graph_has_tooltips(g)) { + g_object_set(w, "has-tooltip", TRUE, NULL); + g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g); + } +#endif + cairo_set_source_rgb(cr, 0, 0, 0); + bar_graph_draw(g, cr); + cairo_destroy(cr); + + return FALSE; +} + +static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event, + gpointer data) +{ + guint width = gtk_widget_get_allocated_width(w); + guint height = gtk_widget_get_allocated_height(w); + struct graph *g = data; + + graph_set_size(g, width, height); + graph_set_size(g, width, height); + graph_set_position(g, 0, 0); + return TRUE; +} + +static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox, + struct thread_stat *ts) +{ + double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR]; + const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u", + "250u", "500u", "750u", "1m", "2m", + "4m", "10m", "20m", "50m", "100m", + "250m", "500m", "750m", "1s", "2s", ">= 2s" }; + int start, end, i; + const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR; + GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area; + struct gui_entry *ge = gc->ge; + + stat_calc_lat_u(ts, io_u_lat); + stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]); + + /* + * Found out which first bucket has entries, and which last bucket + */ + start = end = -1U; + for (i = 0; i < total; i++) { + if (io_u_lat[i] == 0.00) + continue; + + if (start == -1U) + start = i; + end = i; + } + + /* + * No entries... + */ + if (start == -1U) + return; + + tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1); + ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0); + + frame = gtk_frame_new("Latency buckets"); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + completion_vbox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), completion_vbox); + hbox = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(completion_vbox), hbox); + + drawing_area = gtk_drawing_area_new(); + gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300); + gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &gfio_color_white); + gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area); + g_signal_connect(G_OBJECT(drawing_area), GFIO_DRAW_EVENT, G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph); + g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph); + + gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, TRUE, 3); +} + +static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min, + unsigned long max, double mean, double dev) +{ + const char *base = "(usec)"; + GtkWidget *hbox, *label, *frame; + char *minp, *maxp; + char tmp[64]; + + if (!usec_to_msec(&min, &max, &mean, &dev)) + base = "(msec)"; + + minp = num2str(min, 6, 1, 0); + maxp = num2str(max, 6, 1, 0); + + sprintf(tmp, "%s %s", name, base); + frame = gtk_frame_new(tmp); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), hbox); + + label = new_info_label_in_frame(hbox, "Minimum"); + gtk_label_set_text(GTK_LABEL(label), minp); + label = new_info_label_in_frame(hbox, "Maximum"); + gtk_label_set_text(GTK_LABEL(label), maxp); + label = new_info_label_in_frame(hbox, "Average"); + sprintf(tmp, "%5.02f", mean); + gtk_label_set_text(GTK_LABEL(label), tmp); + label = new_info_label_in_frame(hbox, "Standard deviation"); + sprintf(tmp, "%5.02f", dev); + gtk_label_set_text(GTK_LABEL(label), tmp); + + free(minp); + free(maxp); +} + +static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals, + fio_fp64_t *plist, + unsigned int len, + const char *base, + unsigned int scale) +{ + GType types[FIO_IO_U_LIST_MAX_LEN]; + GtkWidget *tree_view; + GtkTreeSelection *selection; + GtkListStore *model; + GtkTreeIter iter; + int i; + + for (i = 0; i < len; i++) + types[i] = G_TYPE_INT; + + model = gtk_list_store_newv(len, types); + + tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_widget_set_can_focus(tree_view, FALSE); + + g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, + "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); + gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); + + for (i = 0; i < len; i++) { + char fbuf[8]; + + sprintf(fbuf, "%2.2f%%", plist[i].u.f); + tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE); + } + + gtk_list_store_append(model, &iter); + + for (i = 0; i < len; i++) { + if (scale) + ovals[i] = (ovals[i] + 999) / 1000; + gtk_list_store_set(model, &iter, i, ovals[i], -1); + } + + return tree_view; +} + +static struct graph *setup_clat_graph(char *title, unsigned int *ovals, + fio_fp64_t *plist, + unsigned int len, + double xdim, double ydim) +{ + struct graph *g; + int i; + + g = graph_new(xdim, ydim, gfio_graph_font); + graph_title(g, title); + graph_x_title(g, "Percentile"); + graph_y_title(g, "Time"); + + for (i = 0; i < len; i++) { + graph_label_t l; + char fbuf[8]; + + sprintf(fbuf, "%2.2f%%", plist[i].u.f); + l = graph_add_label(g, fbuf); + graph_add_data(g, l, (double) ovals[i]); + } + + return g; +} + +static void gfio_show_clat_percentiles(struct gfio_client *gc, + GtkWidget *vbox, struct thread_stat *ts, + int ddir) +{ + unsigned int *io_u_plat = ts->io_u_plat[ddir]; + unsigned long nr = ts->clat_stat[ddir].samples; + fio_fp64_t *plist = ts->percentile_list; + unsigned int *ovals, len, minv, maxv, scale_down; + const char *base; + GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox; + struct gui_entry *ge = gc->ge; + char tmp[64]; + + len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv); + if (!len) + goto out; + + /* + * We default to usecs, but if the value range is such that we + * should scale down to msecs, do that. + */ + if (minv > 2000 && maxv > 99999) { + scale_down = 1; + base = "msec"; + } else { + scale_down = 0; + base = "usec"; + } + + sprintf(tmp, "Completion percentiles (%s)", base); + tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down); + ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0); + + frame = gtk_frame_new(tmp); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5); + + completion_vbox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), completion_vbox); + hbox = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(completion_vbox), hbox); + drawing_area = gtk_drawing_area_new(); + gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300); + gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &gfio_color_white); + gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area); + g_signal_connect(G_OBJECT(drawing_area), GFIO_DRAW_EVENT, G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph); + g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph); + + gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, TRUE, 3); +out: + if (ovals) + free(ovals); +} + +#define GFIO_CLAT 1 +#define GFIO_SLAT 2 +#define GFIO_LAT 4 + +static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox, + struct group_run_stats *rs, + struct thread_stat *ts, int ddir) +{ + const char *ddir_label[2] = { "Read", "Write" }; + GtkWidget *frame, *label, *box, *vbox, *main_vbox; + unsigned long min[3], max[3], runt; + unsigned long long bw, iops; + unsigned int flags = 0; + double mean[3], dev[3]; + char *io_p, *bw_p, *iops_p; + int i2p; + + if (!ts->runtime[ddir]) + return; + + i2p = is_power_of_2(rs->kb_base); + runt = ts->runtime[ddir]; + + bw = (1000 * ts->io_bytes[ddir]) / runt; + io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p); + bw_p = num2str(bw, 6, 1, i2p); + + iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt; + iops_p = num2str(iops, 6, 1, 0); + + box = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3); + + frame = gtk_frame_new(ddir_label[ddir]); + gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 5); + + main_vbox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), main_vbox); + + box = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3); + + label = new_info_label_in_frame(box, "IO"); + gtk_label_set_text(GTK_LABEL(label), io_p); + label = new_info_label_in_frame(box, "Bandwidth"); + gtk_label_set_text(GTK_LABEL(label), bw_p); + label = new_info_label_in_frame(box, "IOPS"); + gtk_label_set_text(GTK_LABEL(label), iops_p); + label = new_info_label_in_frame(box, "Runtime (msec)"); + label_set_int_value(label, ts->runtime[ddir]); + + if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) { + double p_of_agg = 100.0; + const char *bw_str = "KB"; + char tmp[32]; + + if (rs->agg[ddir]) { + p_of_agg = mean[0] * 100 / (double) rs->agg[ddir]; + if (p_of_agg > 100.0) + p_of_agg = 100.0; + } + + if (mean[0] > 999999.9) { + min[0] /= 1000.0; + max[0] /= 1000.0; + mean[0] /= 1000.0; + dev[0] /= 1000.0; + bw_str = "MB"; + } + + sprintf(tmp, "Bandwidth (%s)", bw_str); + frame = gtk_frame_new(tmp); + gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); + + box = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), box); + + label = new_info_label_in_frame(box, "Minimum"); + label_set_int_value(label, min[0]); + label = new_info_label_in_frame(box, "Maximum"); + label_set_int_value(label, max[0]); + label = new_info_label_in_frame(box, "Percentage of jobs"); + sprintf(tmp, "%3.2f%%", p_of_agg); + gtk_label_set_text(GTK_LABEL(label), tmp); + label = new_info_label_in_frame(box, "Average"); + sprintf(tmp, "%5.02f", mean[0]); + gtk_label_set_text(GTK_LABEL(label), tmp); + label = new_info_label_in_frame(box, "Standard deviation"); + sprintf(tmp, "%5.02f", dev[0]); + gtk_label_set_text(GTK_LABEL(label), tmp); + } + + if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) + flags |= GFIO_SLAT; + if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1])) + flags |= GFIO_CLAT; + if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2])) + flags |= GFIO_LAT; + + if (flags) { + frame = gtk_frame_new("Latency"); + gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5); + + vbox = gtk_vbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), vbox); + + if (flags & GFIO_SLAT) + gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]); + if (flags & GFIO_CLAT) + gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]); + if (flags & GFIO_LAT) + gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]); + } + + if (ts->clat_percentiles) + gfio_show_clat_percentiles(gc, main_vbox, ts, ddir); + + free(io_p); + free(bw_p); + free(iops_p); +} + +static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc, + struct thread_stat *ts, + struct group_run_stats *rs) +{ + GtkWidget *box, *vbox, *entry, *scroll; + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_container_set_border_width(GTK_CONTAINER(scroll), 5); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + vbox = gtk_vbox_new(FALSE, 3); + + box = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5); + + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox); + + gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name)); + + entry = new_info_entry_in_frame(box, "Name"); + gtk_entry_set_text(GTK_ENTRY(entry), ts->name); + if (strlen(ts->description)) { + entry = new_info_entry_in_frame(box, "Description"); + gtk_entry_set_text(GTK_ENTRY(entry), ts->description); + } + entry = new_info_entry_in_frame(box, "Group ID"); + entry_set_int_value(entry, ts->groupid); + entry = new_info_entry_in_frame(box, "Jobs"); + entry_set_int_value(entry, ts->members); + gc->err_entry = entry = new_info_entry_in_frame(box, "Error"); + entry_set_int_value(entry, ts->error); + entry = new_info_entry_in_frame(box, "PID"); + entry_set_int_value(entry, ts->pid); + + if (ts->io_bytes[DDIR_READ]) + gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ); + if (ts->io_bytes[DDIR_WRITE]) + gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE); + + gfio_show_latency_buckets(gc, vbox, ts); + gfio_show_cpu_usage(vbox, ts); + gfio_show_io_depths(vbox, ts); +} + +void gfio_display_end_results(struct gfio_client *gc) +{ + struct gui_entry *ge = gc->ge; + GtkWidget *res_notebook; + int i; + + res_notebook = get_results_window(ge); + + for (i = 0; i < gc->nr_results; i++) { + struct end_results *e = &gc->results[i]; + + __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs); + } + + if (gfio_disk_util_show(gc)) + gtk_widget_show_all(ge->results_window); +} + +static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts, + struct group_run_stats *rs) +{ + struct gfio_client *gc = client->client_data; + struct gui_entry *ge = gc->ge; + + gfio_add_end_results(gc, ts, rs); + + gdk_threads_enter(); + if (ge->results_window) + __gfio_display_end_results(ge->results_notebook, gc, ts, rs); + else + gfio_display_end_results(gc); + gdk_threads_leave(); +} + +static void gfio_client_removed(struct fio_client *client) +{ + struct gfio_client *gc = client->client_data; + + assert(gc->client == client); + fio_put_client(gc->client); + gc->client = NULL; +} + +struct client_ops gfio_client_ops = { + .text = gfio_text_op, + .disk_util = gfio_disk_util_op, + .thread_status = gfio_thread_status_op, + .group_stats = gfio_group_stats_op, + .jobs_eta = gfio_update_client_eta, + .eta = gfio_update_all_eta, + .probe = gfio_probe_op, + .quit = gfio_quit_op, + .add_job = gfio_add_job_op, + .update_job = gfio_update_job_op, + .timed_out = gfio_client_timed_out, + .stop = gfio_client_stop, + .start = gfio_client_start, + .job_start = gfio_client_job_start, + .iolog = gfio_client_iolog, + .removed = gfio_client_removed, + .eta_msec = FIO_CLIENT_DEF_ETA_MSEC, + .stay_connected = 1, + .client_type = FIO_CLIENT_TYPE_GUI, +}; diff --cc init.c index ae96c6c6,da1f472d..6604a18f --- a/init.c +++ b/init.c @@@ -36,8 -36,9 +36,8 @@@ static struct thread_data def_thread struct thread_data *threads = NULL; int exitall_on_terminate = 0; - int terse_output = 0; + int output_format = FIO_OUTPUT_NORMAL; int eta_print; -unsigned long long mlock_size = 0; FILE *f_out = NULL; FILE *f_err = NULL; char **job_sections = NULL; @@@ -853,24 -875,28 +877,26 @@@ static int add_job(struct thread_data * if (!td->o.name) td->o.name = strdup(jobname); - if (!terse_output) { + if (output_format == FIO_OUTPUT_NORMAL) { if (!job_add_num) { - if (!strcmp(td->io_ops->name, "cpuio")) { - log_info("%s: ioengine=cpu, cpuload=%u," - " cpucycle=%u\n", td->o.name, - td->o.cpuload, - td->o.cpucycle); - } else { + if (is_backend && !recursed) + fio_server_send_add_job(td); + + if (!(td->io_ops->flags & FIO_NOIO)) { - char *c1, *c2, *c3, *c4; + char *c1, *c2, *c3, *c4, *c5, *c6; - c1 = to_kmg(td->o.min_bs[DDIR_READ]); - c2 = to_kmg(td->o.max_bs[DDIR_READ]); - c3 = to_kmg(td->o.min_bs[DDIR_WRITE]); - c4 = to_kmg(td->o.max_bs[DDIR_WRITE]); - c5 = to_kmg(td->o.min_bs[DDIR_TRIM]); - c6 = to_kmg(td->o.max_bs[DDIR_TRIM]); + c1 = fio_uint_to_kmg(td->o.min_bs[DDIR_READ]); + c2 = fio_uint_to_kmg(td->o.max_bs[DDIR_READ]); + c3 = fio_uint_to_kmg(td->o.min_bs[DDIR_WRITE]); + c4 = fio_uint_to_kmg(td->o.max_bs[DDIR_WRITE]); ++ c5 = fio_uint_to_kmg(td->o.min_bs[DDIR_TRIM]); ++ c6 = fio_uint_to_kmg(td->o.max_bs[DDIR_TRIM]); - log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s," + log_info("%s: (g=%d): rw=%s, bs=%s-%s/%s-%s/%s-%s," " ioengine=%s, iodepth=%u\n", td->o.name, td->groupid, - ddir_str[td->o.td_ddir], + ddir_str(td->o.td_ddir), - c1, c2, c3, c4, + c1, c2, c3, c4, c5, c6, td->io_ops->name, td->o.iodepth); diff --cc io_ddir.h index 908101aa,df5abbbc..f28f7555 --- a/io_ddir.h +++ b/io_ddir.h @@@ -36,15 -40,12 +40,21 @@@ static inline int ddir_sync(enum fio_dd static inline int ddir_rw(enum fio_ddir ddir) { - return ddir == DDIR_READ || ddir == DDIR_WRITE; + return ddir == DDIR_READ || ddir == DDIR_WRITE || ddir == DDIR_TRIM; } +static inline const char *ddir_str(enum fio_ddir ddir) +{ + const char *ddir_str[] = { NULL, "read", "write", "rw", NULL, - "randread", "randwrite", "randrw" }; ++ "randread", "randwrite", "randrw", ++ "trim", NULL, NULL, NULL, "randtrim" }; + + return ddir_str[ddir]; +} + + #define ddir_trim(ddir) ((ddir) == DDIR_TRIM) + + #define ddir_rw_sum(arr) \ + ((arr)[DDIR_READ] + (arr)[DDIR_WRITE] + (arr)[DDIR_TRIM]) + #endif diff --cc options.c index 2201f597,d1cf7e8a..03944563 --- a/options.c +++ b/options.c @@@ -175,21 -176,38 +175,38 @@@ static int str_bssplit_cb(void *data, c odir = strchr(str, ','); if (odir) { - ret = bssplit_ddir(&td->o, DDIR_WRITE, odir + 1); + ddir = strchr(odir + 1, ','); + if (ddir) { - ret = bssplit_ddir(td, DDIR_TRIM, ddir + 1); ++ ret = bssplit_ddir(&td->o, DDIR_TRIM, ddir + 1); + if (!ret) + *ddir = '\0'; + } else { + char *op; + + op = strdup(odir + 1); - ret = bssplit_ddir(td, DDIR_TRIM, op); ++ ret = bssplit_ddir(&td->o, DDIR_TRIM, op); + + free(op); + } - if (!ret) - ret = bssplit_ddir(td, DDIR_WRITE, odir + 1); ++ if (!ret) ++ ret = bssplit_ddir(&td->o, DDIR_WRITE, odir + 1); if (!ret) { *odir = '\0'; - ret = bssplit_ddir(td, DDIR_READ, str); + ret = bssplit_ddir(&td->o, DDIR_READ, str); } } else { char *op; op = strdup(str); - ret = bssplit_ddir(td, DDIR_WRITE, op); ++ ret = bssplit_ddir(&td->o, DDIR_WRITE, op); + free(op); + if (!ret) { + op = strdup(str); - ret = bssplit_ddir(td, DDIR_TRIM, op); ++ ret = bssplit_ddir(&td->o, DDIR_TRIM, op); + free(op); + } - ret = bssplit_ddir(td, DDIR_READ, str); + ret = bssplit_ddir(&td->o, DDIR_READ, str); - if (!ret) - ret = bssplit_ddir(&td->o, DDIR_WRITE, op); - - free(op); } free(p); @@@ -2113,48 -1839,37 +2157,52 @@@ struct fio_option fio_options[FIO_MAX_O }, { .name = "rate", + .lname = "I/O rate", .type = FIO_OPT_INT, - .off1 = td_var_offset(rate[0]), - .off2 = td_var_offset(rate[1]), + .off1 = td_var_offset(rate[DDIR_READ]), + .off2 = td_var_offset(rate[DDIR_WRITE]), + .off3 = td_var_offset(rate[DDIR_TRIM]), .help = "Set bandwidth rate", + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_RATE, }, { .name = "ratemin", + .lname = "I/O min rate", .type = FIO_OPT_INT, - .off1 = td_var_offset(ratemin[0]), - .off2 = td_var_offset(ratemin[1]), + .off1 = td_var_offset(ratemin[DDIR_READ]), + .off2 = td_var_offset(ratemin[DDIR_WRITE]), + .off3 = td_var_offset(ratemin[DDIR_TRIM]), .help = "Job must meet this rate or it will be shutdown", .parent = "rate", + .hide = 1, + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_RATE, }, { .name = "rate_iops", + .lname = "I/O rate IOPS", .type = FIO_OPT_INT, - .off1 = td_var_offset(rate_iops[0]), - .off2 = td_var_offset(rate_iops[1]), + .off1 = td_var_offset(rate_iops[DDIR_READ]), + .off2 = td_var_offset(rate_iops[DDIR_WRITE]), + .off3 = td_var_offset(rate_iops[DDIR_TRIM]), .help = "Limit IO used to this number of IO operations/sec", + .hide = 1, + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_RATE, }, { .name = "rate_iops_min", + .lname = "I/O min rate IOPS", .type = FIO_OPT_INT, - .off1 = td_var_offset(rate_iops_min[0]), - .off2 = td_var_offset(rate_iops_min[1]), + .off1 = td_var_offset(rate_iops_min[DDIR_READ]), + .off2 = td_var_offset(rate_iops_min[DDIR_WRITE]), + .off3 = td_var_offset(rate_iops_min[DDIR_TRIM]), .help = "Job must meet this rate or it will be shut down", .parent = "rate_iops", + .hide = 1, + .category = FIO_OPT_C_IO, + .group = FIO_OPT_G_RATE, }, { .name = "ratecycle", diff --cc server.h index a838126f,9bf8907f..938f20a0 --- a/server.h +++ b/server.h @@@ -157,6 -117,14 +157,12 @@@ struct group_run_stats extern void fio_server_send_ts(struct thread_stat *, struct group_run_stats *); extern void fio_server_send_gs(struct group_run_stats *); extern void fio_server_send_du(void); + extern void fio_server_idle_loop(void); + + extern int fio_clients_connect(void); + extern int fio_clients_send_ini(const char *); -extern int fio_handle_clients(void); -extern int fio_client_add(const char *, void **); + extern void fio_client_add_cmd_option(void *, const char *); + extern void fio_client_add_ini_file(void *, const char *); extern int fio_recv_data(int sk, void *p, unsigned int len); extern int fio_send_data(int sk, const void *p, unsigned int len); diff --cc stat.c index ef447b16,26659526..edeae877 --- a/stat.c +++ b/stat.c @@@ -306,7 -314,7 +307,7 @@@ void stat_calc_dist(unsigned int *map, static void stat_calc_lat(struct thread_stat *ts, double *dst, unsigned int *src, int nr) { -- unsigned long total = ts_total_io_u(ts); ++ unsigned long total = ddir_rw_sum(ts->total_io_u); int i; /* @@@ -514,8 -554,12 +515,10 @@@ void show_thread_status(struct thread_s show_ddir_status(rs, ts, DDIR_READ); if (ts->io_bytes[DDIR_WRITE]) show_ddir_status(rs, ts, DDIR_WRITE); + if (ts->io_bytes[DDIR_TRIM]) + show_ddir_status(rs, ts, DDIR_TRIM); - stat_calc_lat_u(ts, io_u_lat_u); - stat_calc_lat_m(ts, io_u_lat_m); - show_latencies(io_u_lat_u, io_u_lat_m); + show_latencies(ts); runtime = ts->total_run_time; if (runtime) { @@@ -531,7 -575,7 +534,7 @@@ log_info(" cpu : usr=%3.2f%%, sys=%3.2f%%, ctx=%lu, majf=%lu," " minf=%lu\n", usr_cpu, sys_cpu, ts->ctx, ts->majf, ts->minf); -- stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); ++ stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist); log_info(" IO depths : 1=%3.1f%%, 2=%3.1f%%, 4=%3.1f%%, 8=%3.1f%%," " 16=%3.1f%%, 32=%3.1f%%, >=64=%3.1f%%\n", io_u_dist[0], io_u_dist[1], io_u_dist[2], @@@ -635,8 -679,111 +638,111 @@@ static void show_ddir_status_terse(stru log_info(";%lu;%lu;%f%%;%f;%f", 0UL, 0UL, 0.0, 0.0, 0.0); } + static void add_ddir_status_json(struct thread_stat *ts, + struct group_run_stats *rs, int ddir, struct json_object *parent) + { + unsigned long min, max; + unsigned long long bw, iops; + unsigned int *ovals = NULL; + double mean, dev; + unsigned int len, minv, maxv; + int i; + const char *ddirname[] = {"read", "write", "trim"}; + struct json_object *dir_object, *tmp_object, *percentile_object; + char buf[120]; + double p_of_agg = 100.0; + + assert(ddir_rw(ddir)); + + dir_object = json_create_object(); + json_object_add_value_object(parent, ddirname[ddir], dir_object); + + iops = bw = 0; + if (ts->runtime[ddir]) { + uint64_t runt = ts->runtime[ddir]; + + bw = ((1000 * ts->io_bytes[ddir]) / runt) / 1024; + iops = (1000 * (uint64_t) ts->total_io_u[ddir]) / runt; + } + + json_object_add_value_int(dir_object, "io_bytes", ts->io_bytes[ddir] >> 10); + json_object_add_value_int(dir_object, "bw", bw); + json_object_add_value_int(dir_object, "iops", iops); + json_object_add_value_int(dir_object, "runtime", ts->runtime[ddir]); + + if (!calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev)) { + min = max = 0; + mean = dev = 0.0; + } + tmp_object = json_create_object(); + json_object_add_value_object(dir_object, "slat", tmp_object); + json_object_add_value_int(tmp_object, "min", min); + json_object_add_value_int(tmp_object, "max", max); + json_object_add_value_float(tmp_object, "mean", mean); + json_object_add_value_float(tmp_object, "stddev", dev); + + if (!calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev)) { + min = max = 0; + mean = dev = 0.0; + } + tmp_object = json_create_object(); + json_object_add_value_object(dir_object, "clat", tmp_object); + json_object_add_value_int(tmp_object, "min", min); + json_object_add_value_int(tmp_object, "max", max); + json_object_add_value_float(tmp_object, "mean", mean); + json_object_add_value_float(tmp_object, "stddev", dev); + + if (ts->clat_percentiles) { + len = calc_clat_percentiles(ts->io_u_plat[ddir], + ts->clat_stat[ddir].samples, + ts->percentile_list, &ovals, &maxv, + &minv); + } else + len = 0; + + percentile_object = json_create_object(); + json_object_add_value_object(tmp_object, "percentile", percentile_object); + for (i = 0; i < FIO_IO_U_LIST_MAX_LEN; i++) { + if (i >= len) { + json_object_add_value_int(percentile_object, "0.00", 0); + continue; + } + snprintf(buf, sizeof(buf) - 1, "%2.2f", ts->percentile_list[i].u.f); + json_object_add_value_int(percentile_object, (const char *)buf, ovals[i]); + } + + if (!calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev)) { + min = max = 0; + mean = dev = 0.0; + } + tmp_object = json_create_object(); + json_object_add_value_object(dir_object, "lat", tmp_object); + json_object_add_value_int(tmp_object, "min", min); + json_object_add_value_int(tmp_object, "max", max); + json_object_add_value_float(tmp_object, "mean", mean); + json_object_add_value_float(tmp_object, "stddev", dev); + if (ovals) + free(ovals); + + if (!calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) { + if (rs->agg[ddir]) { + p_of_agg = mean * 100 / (double) rs->agg[ddir]; + if (p_of_agg > 100.0) + p_of_agg = 100.0; + } + } else { + min = max = 0; + p_of_agg = mean = dev = 0.0; + } + json_object_add_value_int(dir_object, "bw_min", min); + json_object_add_value_int(dir_object, "bw_max", max); + json_object_add_value_float(dir_object, "bw_agg", mean); + json_object_add_value_float(dir_object, "bw_mean", mean); + json_object_add_value_float(dir_object, "bw_dev", dev); + } + static void show_thread_status_terse_v2(struct thread_stat *ts, - struct group_run_stats *rs) + struct group_run_stats *rs) { double io_u_dist[FIO_IO_U_MAP_NR]; double io_u_lat_u[FIO_IO_U_LAT_U_NR]; @@@ -666,7 -815,7 +774,7 @@@ ts->minf); /* Calc % distribution of IO depths, usecond, msecond latency */ -- stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); ++ stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist); stat_calc_lat_u(ts, io_u_lat_u); stat_calc_lat_m(ts, io_u_lat_m); @@@ -727,7 -877,7 +836,7 @@@ static void show_thread_status_terse_v3 ts->minf); /* Calc % distribution of IO depths, usecond, msecond latency */ -- stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); ++ stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist); stat_calc_lat_u(ts, io_u_lat_u); stat_calc_lat_m(ts, io_u_lat_m); @@@ -757,6 -907,90 +866,90 @@@ log_info("\n"); } + static struct json_object *show_thread_status_json(struct thread_stat *ts, + struct group_run_stats *rs) + { + struct json_object *root, *tmp; + double io_u_dist[FIO_IO_U_MAP_NR]; + double io_u_lat_u[FIO_IO_U_LAT_U_NR]; + double io_u_lat_m[FIO_IO_U_LAT_M_NR]; + double usr_cpu, sys_cpu; + int i; + + root = json_create_object(); + json_object_add_value_string(root, "jobname", ts->name); + json_object_add_value_int(root, "groupid", ts->groupid); + json_object_add_value_int(root, "error", ts->error); + + add_ddir_status_json(ts, rs, DDIR_READ, root); + add_ddir_status_json(ts, rs, DDIR_WRITE, root); + add_ddir_status_json(ts, rs, DDIR_TRIM, root); + + /* CPU Usage */ + if (ts->total_run_time) { + double runt = (double) ts->total_run_time; + + usr_cpu = (double) ts->usr_time * 100 / runt; + sys_cpu = (double) ts->sys_time * 100 / runt; + } else { + usr_cpu = 0; + sys_cpu = 0; + } + json_object_add_value_float(root, "usr_cpu", usr_cpu); + json_object_add_value_float(root, "sys_cpu", sys_cpu); + json_object_add_value_int(root, "ctx", ts->ctx); + json_object_add_value_int(root, "majf", ts->majf); + json_object_add_value_int(root, "minf", ts->minf); + + + /* Calc % distribution of IO depths, usecond, msecond latency */ - stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist); ++ stat_calc_dist(ts->io_u_map, ddir_rw_sum(ts->total_io_u), io_u_dist); + stat_calc_lat_u(ts, io_u_lat_u); + stat_calc_lat_m(ts, io_u_lat_m); + + tmp = json_create_object(); + json_object_add_value_object(root, "iodepth_level", tmp); + /* Only show fixed 7 I/O depth levels*/ + for (i = 0; i < 7; i++) { + char name[20]; + if (i < 6) + snprintf(name, 19, "%d", 1 << i); + else + snprintf(name, 19, ">=%d", 1 << i); + json_object_add_value_float(tmp, (const char *)name, io_u_dist[i]); + } + + tmp = json_create_object(); + json_object_add_value_object(root, "latency_us", tmp); + /* Microsecond latency */ + for (i = 0; i < FIO_IO_U_LAT_U_NR; i++) { + const char *ranges[] = { "2", "4", "10", "20", "50", "100", + "250", "500", "750", "1000", }; + json_object_add_value_float(tmp, ranges[i], io_u_lat_u[i]); + } + /* Millisecond latency */ + tmp = json_create_object(); + json_object_add_value_object(root, "latency_ms", tmp); + for (i = 0; i < FIO_IO_U_LAT_M_NR; i++) { + const char *ranges[] = { "2", "4", "10", "20", "50", "100", + "250", "500", "750", "1000", "2000", + ">=2000", }; + json_object_add_value_float(tmp, ranges[i], io_u_lat_m[i]); + } + + /* Additional output if continue_on_error set - default off*/ + if (ts->continue_on_error) { + json_object_add_value_int(root, "total_err", ts->total_err_count); + json_object_add_value_int(root, "total_err", ts->first_error); + } + + /* Additional output if description is set */ + if (strlen(ts->description)) + json_object_add_value_string(root, "desc", ts->description); + + return root; + } + static void show_thread_status_terse(struct thread_stat *ts, struct group_run_stats *rs) { diff --cc stat.h index d7184aab,b9e04481..8a1536e8 --- a/stat.h +++ b/stat.h @@@ -1,13 -1,11 +1,13 @@@ #ifndef FIO_STAT_H #define FIO_STAT_H +#include "iolog.h" + struct group_run_stats { - uint64_t max_run[2], min_run[2]; - uint64_t max_bw[2], min_bw[2]; - uint64_t io_kb[2]; - uint64_t agg[2]; + uint64_t max_run[DDIR_RWDIR_CNT], min_run[DDIR_RWDIR_CNT]; + uint64_t max_bw[DDIR_RWDIR_CNT], min_bw[DDIR_RWDIR_CNT]; + uint64_t io_kb[DDIR_RWDIR_CNT]; + uint64_t agg[DDIR_RWDIR_CNT]; uint32_t kb_base; uint32_t groupid; }; @@@ -177,10 -174,10 +177,10 @@@ struct jobs_eta uint32_t nr_ramp; uint32_t nr_pending; uint32_t files_open; - uint32_t m_rate[2], t_rate[2]; - uint32_t m_iops[2], t_iops[2]; - uint32_t rate[2]; - uint32_t iops[2]; - uint32_t m_rate, t_rate; - uint32_t m_iops, t_iops; ++ uint32_t m_rate[DDIR_RWDIR_CNT], t_rate[DDIR_RWDIR_CNT]; ++ uint32_t m_iops[DDIR_RWDIR_CNT], t_iops[DDIR_RWDIR_CNT]; + uint32_t rate[DDIR_RWDIR_CNT]; + uint32_t iops[DDIR_RWDIR_CNT]; uint64_t elapsed_sec; uint64_t eta_sec; uint32_t is_pow2; @@@ -202,27 -199,5 +202,25 @@@ extern void sum_thread_stats(struct thr extern void sum_group_stats(struct group_run_stats *dst, struct group_run_stats *src); extern void init_thread_stat(struct thread_stat *ts); extern void init_group_run_stat(struct group_run_stats *gs); +extern void eta_to_str(char *str, unsigned long eta_sec); +extern int calc_lat(struct io_stat *is, unsigned long *min, unsigned long *max, double *mean, double *dev); +extern unsigned int calc_clat_percentiles(unsigned int *io_u_plat, unsigned long nr, fio_fp64_t *plist, unsigned int **output, unsigned int *maxv, unsigned int *minv); +extern void stat_calc_lat_m(struct thread_stat *ts, double *io_u_lat); +extern void stat_calc_lat_u(struct thread_stat *ts, double *io_u_lat); +extern void stat_calc_dist(unsigned int *map, unsigned long total, double *io_u_dist); + - #define ts_total_io_u(ts) ((ts)->total_io_u[0] + (ts)->total_io_u[1]) - +static inline int usec_to_msec(unsigned long *min, unsigned long *max, + double *mean, double *dev) +{ + if (*min > 1000 && *max > 1000 && *mean > 1000.0 && *dev > 1000.0) { + *min /= 1000; + *max /= 1000; + *mean /= 1000.0; + *dev /= 1000.0; + return 0; + } + + return 1; +} #endif diff --cc thread_options.h index a78684cf,00000000..323dacd9 mode 100644,000000..100644 --- a/thread_options.h +++ b/thread_options.h @@@ -1,412 -1,0 +1,412 @@@ +#ifndef FIO_THREAD_OPTIONS_H +#define FIO_THREAD_OPTIONS_H + +#include "arch/arch.h" +#include "os/os.h" +#include "stat.h" +#include "gettime.h" + +/* + * What type of allocation to use for io buffers + */ +enum fio_memtype { + MEM_MALLOC = 0, /* ordinary malloc */ + MEM_SHM, /* use shared memory segments */ + MEM_SHMHUGE, /* use shared memory segments with huge pages */ + MEM_MMAP, /* use anonynomous mmap */ + MEM_MMAPHUGE, /* memory mapped huge file */ +}; + +/* + * What type of errors to continue on when continue_on_error is used + */ +enum error_type { + ERROR_TYPE_NONE = 0, + ERROR_TYPE_READ = 1 << 0, + ERROR_TYPE_WRITE = 1 << 1, + ERROR_TYPE_VERIFY = 1 << 2, + ERROR_TYPE_ANY = 0xffff, +}; + +#define BSSPLIT_MAX 64 + +struct bssplit { + uint32_t bs; + uint32_t perc; +}; + +struct thread_options { + int pad; + char *description; + char *name; + char *directory; + char *filename; + char *opendir; + char *ioengine; + char *mmapfile; + enum td_ddir td_ddir; + unsigned int rw_seq; + unsigned int kb_base; + unsigned int ddir_seq_nr; + long ddir_seq_add; + unsigned int iodepth; + unsigned int iodepth_low; + unsigned int iodepth_batch; + unsigned int iodepth_batch_complete; + + unsigned long long size; + unsigned int size_percent; + unsigned int fill_device; + unsigned long long file_size_low; + unsigned long long file_size_high; + unsigned long long start_offset; + - unsigned int bs[2]; - unsigned int ba[2]; - unsigned int min_bs[2]; - unsigned int max_bs[2]; - struct bssplit *bssplit[2]; - unsigned int bssplit_nr[2]; ++ unsigned int bs[DDIR_RWDIR_CNT]; ++ unsigned int ba[DDIR_RWDIR_CNT]; ++ unsigned int min_bs[DDIR_RWDIR_CNT]; ++ unsigned int max_bs[DDIR_RWDIR_CNT]; ++ struct bssplit *bssplit[DDIR_RWDIR_CNT]; ++ unsigned int bssplit_nr[DDIR_RWDIR_CNT]; + + unsigned int nr_files; + unsigned int open_files; + enum file_lock_mode file_lock_mode; + unsigned int lockfile_batch; + + unsigned int odirect; + unsigned int invalidate_cache; + unsigned int create_serialize; + unsigned int create_fsync; + unsigned int create_on_open; + unsigned int create_only; + unsigned int end_fsync; + unsigned int pre_read; + unsigned int sync_io; + unsigned int verify; + unsigned int do_verify; + unsigned int verifysort; + unsigned int verify_interval; + unsigned int verify_offset; + char verify_pattern[MAX_PATTERN_SIZE]; + unsigned int verify_pattern_bytes; + unsigned int verify_fatal; + unsigned int verify_dump; + unsigned int verify_async; + unsigned long long verify_backlog; + unsigned int verify_batch; + unsigned int use_thread; + unsigned int unlink; + unsigned int do_disk_util; + unsigned int override_sync; + unsigned int rand_repeatable; + unsigned int use_os_rand; + unsigned int log_avg_msec; + unsigned int norandommap; + unsigned int softrandommap; + unsigned int bs_unaligned; + unsigned int fsync_on_close; + + unsigned int hugepage_size; + unsigned int rw_min_bs; + unsigned int thinktime; + unsigned int thinktime_spin; + unsigned int thinktime_blocks; + unsigned int fsync_blocks; + unsigned int fdatasync_blocks; + unsigned int barrier_blocks; + unsigned long long start_delay; + unsigned long long timeout; + unsigned long long ramp_time; + unsigned int overwrite; + unsigned int bw_avg_time; + unsigned int iops_avg_time; + unsigned int loops; + unsigned long long zone_range; + unsigned long long zone_size; + unsigned long long zone_skip; + unsigned long long lockmem; + enum fio_memtype mem_type; + unsigned int mem_align; + + unsigned int stonewall; + unsigned int new_group; + unsigned int numjobs; + os_cpu_mask_t cpumask; + unsigned int cpumask_set; + os_cpu_mask_t verify_cpumask; + unsigned int verify_cpumask_set; + unsigned int iolog; + unsigned int rwmixcycle; + unsigned int rwmix[2]; + unsigned int nice; + unsigned int ioprio; + unsigned int ioprio_class; + unsigned int file_service_type; + unsigned int group_reporting; + unsigned int fadvise_hint; + enum fio_fallocate_mode fallocate_mode; + unsigned int zero_buffers; + unsigned int refill_buffers; + unsigned int scramble_buffers; + unsigned int compress_percentage; + unsigned int compress_chunk; + unsigned int time_based; + unsigned int disable_lat; + unsigned int disable_clat; + unsigned int disable_slat; + unsigned int disable_bw; + unsigned int gtod_reduce; + unsigned int gtod_cpu; + unsigned int gtod_offload; + enum fio_cs clocksource; + unsigned int no_stall; + unsigned int trim_percentage; + unsigned int trim_batch; + unsigned int trim_zero; + unsigned long long trim_backlog; + unsigned int clat_percentiles; + unsigned int overwrite_plist; + fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN]; + + char *read_iolog_file; + char *write_iolog_file; + char *bw_log_file; + char *lat_log_file; + char *iops_log_file; + char *replay_redirect; + + /* + * Pre-run and post-run shell + */ + char *exec_prerun; + char *exec_postrun; + - unsigned int rate[2]; - unsigned int ratemin[2]; ++ unsigned int rate[DDIR_RWDIR_CNT]; ++ unsigned int ratemin[DDIR_RWDIR_CNT]; + unsigned int ratecycle; - unsigned int rate_iops[2]; - unsigned int rate_iops_min[2]; ++ unsigned int rate_iops[DDIR_RWDIR_CNT]; ++ unsigned int rate_iops_min[DDIR_RWDIR_CNT]; + + char *ioscheduler; + + /* + * I/O Error handling + */ + enum error_type continue_on_error; + + /* + * Benchmark profile type + */ + char *profile; + + /* + * blkio cgroup support + */ + char *cgroup; + unsigned int cgroup_weight; + unsigned int cgroup_nodelete; + + unsigned int uid; + unsigned int gid; + + int flow_id; + int flow; + int flow_watermark; + unsigned int flow_sleep; + + unsigned long long offset_increment; + + unsigned int sync_file_range; +}; + +#define FIO_TOP_STR_MAX 256 + +struct thread_options_pack { + uint8_t description[FIO_TOP_STR_MAX]; + uint8_t name[FIO_TOP_STR_MAX]; + uint8_t directory[FIO_TOP_STR_MAX]; + uint8_t filename[FIO_TOP_STR_MAX]; + uint8_t opendir[FIO_TOP_STR_MAX]; + uint8_t ioengine[FIO_TOP_STR_MAX]; + uint8_t mmapfile[FIO_TOP_STR_MAX]; + uint32_t td_ddir; + uint32_t rw_seq; + uint32_t kb_base; + uint32_t ddir_seq_nr; + uint64_t ddir_seq_add; + uint32_t iodepth; + uint32_t iodepth_low; + uint32_t iodepth_batch; + uint32_t iodepth_batch_complete; + + uint64_t size; + uint32_t size_percent; + uint32_t fill_device; + uint64_t file_size_low; + uint64_t file_size_high; + uint64_t start_offset; + + uint32_t bs[2]; + uint32_t ba[2]; + uint32_t min_bs[2]; + uint32_t max_bs[2]; + struct bssplit bssplit[2][BSSPLIT_MAX]; + uint32_t bssplit_nr[2]; + + uint32_t nr_files; + uint32_t open_files; + uint32_t file_lock_mode; + uint32_t lockfile_batch; + + uint32_t odirect; + uint32_t invalidate_cache; + uint32_t create_serialize; + uint32_t create_fsync; + uint32_t create_on_open; + uint32_t create_only; + uint32_t end_fsync; + uint32_t pre_read; + uint32_t sync_io; + uint32_t verify; + uint32_t do_verify; + uint32_t verifysort; + uint32_t verify_interval; + uint32_t verify_offset; + uint8_t verify_pattern[MAX_PATTERN_SIZE]; + uint32_t verify_pattern_bytes; + uint32_t verify_fatal; + uint32_t verify_dump; + uint32_t verify_async; + uint64_t verify_backlog; + uint32_t verify_batch; + uint32_t use_thread; + uint32_t unlink; + uint32_t do_disk_util; + uint32_t override_sync; + uint32_t rand_repeatable; + uint32_t use_os_rand; + uint32_t log_avg_msec; + uint32_t norandommap; + uint32_t softrandommap; + uint32_t bs_unaligned; + uint32_t fsync_on_close; + + uint32_t hugepage_size; + uint32_t rw_min_bs; + uint32_t thinktime; + uint32_t thinktime_spin; + uint32_t thinktime_blocks; + uint32_t fsync_blocks; + uint32_t fdatasync_blocks; + uint32_t barrier_blocks; + uint64_t start_delay; + uint64_t timeout; + uint64_t ramp_time; + uint32_t overwrite; + uint32_t bw_avg_time; + uint32_t iops_avg_time; + uint32_t loops; + uint64_t zone_range; + uint64_t zone_size; + uint64_t zone_skip; + uint64_t lockmem; + uint32_t mem_type; + uint32_t mem_align; + + uint32_t stonewall; + uint32_t new_group; + uint32_t numjobs; + uint8_t cpumask[FIO_TOP_STR_MAX]; + uint32_t cpumask_set; + uint8_t verify_cpumask[FIO_TOP_STR_MAX]; + uint32_t verify_cpumask_set; + uint32_t iolog; + uint32_t rwmixcycle; + uint32_t rwmix[2]; + uint32_t nice; + uint32_t ioprio; + uint32_t ioprio_class; + uint32_t file_service_type; + uint32_t group_reporting; + uint32_t fadvise_hint; + uint32_t fallocate_mode; + uint32_t zero_buffers; + uint32_t refill_buffers; + uint32_t scramble_buffers; + unsigned int compress_percentage; + unsigned int compress_chunk; + uint32_t time_based; + uint32_t disable_lat; + uint32_t disable_clat; + uint32_t disable_slat; + uint32_t disable_bw; + uint32_t gtod_reduce; + uint32_t gtod_cpu; + uint32_t gtod_offload; + uint32_t clocksource; + uint32_t no_stall; + uint32_t trim_percentage; + uint32_t trim_batch; + uint32_t trim_zero; + uint64_t trim_backlog; + uint32_t clat_percentiles; + uint32_t overwrite_plist; + fio_fp64_t percentile_list[FIO_IO_U_LIST_MAX_LEN]; + + uint8_t read_iolog_file[FIO_TOP_STR_MAX]; + uint8_t write_iolog_file[FIO_TOP_STR_MAX]; + uint8_t bw_log_file[FIO_TOP_STR_MAX]; + uint8_t lat_log_file[FIO_TOP_STR_MAX]; + uint8_t iops_log_file[FIO_TOP_STR_MAX]; + uint8_t replay_redirect[FIO_TOP_STR_MAX]; + + /* + * Pre-run and post-run shell + */ + uint8_t exec_prerun[FIO_TOP_STR_MAX]; + uint8_t exec_postrun[FIO_TOP_STR_MAX]; + + uint32_t rate[2]; + uint32_t ratemin[2]; + uint32_t ratecycle; + uint32_t rate_iops[2]; + uint32_t rate_iops_min[2]; + + uint8_t ioscheduler[FIO_TOP_STR_MAX]; + + /* + * I/O Error handling + */ + uint32_t continue_on_error; + + /* + * Benchmark profile type + */ + uint8_t profile[FIO_TOP_STR_MAX]; + + /* + * blkio cgroup support + */ + uint8_t cgroup[FIO_TOP_STR_MAX]; + uint32_t cgroup_weight; + uint32_t cgroup_nodelete; + + uint32_t uid; + uint32_t gid; + + int32_t flow_id; + int32_t flow; + int32_t flow_watermark; + uint32_t flow_sleep; + + uint64_t offset_increment; + + uint32_t sync_file_range; +} __attribute__((packed)); + +extern void convert_thread_options_to_cpu(struct thread_options *o, struct thread_options_pack *top); +extern void convert_thread_options_to_net(struct thread_options_pack *top, struct thread_options *); +extern int fio_test_cconv(struct thread_options *); +extern void options_default_fill(struct thread_options *o); + +#endif