+ 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_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);
+
+ 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_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
+{
+ GtkWidget *frame, *box, *tree_view;
+ GtkTreeSelection *selection;
+ GtkListStore *model;
+ GType types[FIO_IO_U_MAP_NR + 1];
+ int i;
+#define NR_LABELS 10
+ const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
+
+ 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, FALSE, 3);
+}
+
+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 GtkWidget *get_results_window(struct gui_entry *ge)
+{
+ GtkWidget *win, *notebook;
+
+ 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);
+
+ notebook = gtk_notebook_new();
+ gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
+ gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
+ gtk_container_add(GTK_CONTAINER(win), notebook);
+
+ ge->results_window = win;
+ ge->results_notebook = notebook;
+ return ge->results_notebook;
+}
+
+static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
+ struct group_run_stats *rs)
+{
+ GtkWidget *res_win, *box, *vbox, *entry, *scroll;
+ struct gfio_client *gc = client->client_data;
+
+ gdk_threads_enter();
+
+ res_win = get_results_window(gc->ge);
+
+ 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(res_win), scroll, gtk_label_new(ts->name));
+
+ gc->results_widget = vbox;
+
+ 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(vbox, rs, ts, DDIR_READ);
+ if (ts->io_bytes[DDIR_WRITE])
+ gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
+
+ gfio_show_latency_buckets(vbox, ts);
+ gfio_show_cpu_usage(vbox, ts);
+ gfio_show_io_depths(vbox, ts);
+
+ gtk_widget_show_all(gc->ge->results_window);
+ gdk_threads_leave();
+}
+
+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 gui *ui = &main_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, 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, p->level, -1);
+ gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
+
+ if (p->level == FIO_LOG_ERR)
+ view_log(NULL, (gpointer) ui);
+
+ gdk_threads_leave();
+}
+
+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;
+ GtkWidget *box, *frame, *entry, *vbox;
+ double util;
+ char tmp[16];
+
+ gdk_threads_enter();
+
+ if (!gc->results_widget)
+ goto out;
+
+ if (!gc->disk_util_frame) {
+ gc->disk_util_frame = gtk_frame_new("Disk utilization");
+ gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
+ }
+
+ vbox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), 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(gc->results_widget);
+out:
+ 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.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 gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
+ gpointer data)
+{
+ struct gfio_graphs *g = data;
+
+ graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
+ graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
+ graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
+ graph_set_position(g->bandwidth_graph, 0, 0);
+ return TRUE;
+}
+
+static void draw_graph(struct graph *g, cairo_t *cr)
+{
+ line_graph_draw(g, cr);
+ cairo_stroke(cr);
+}
+
+static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
+{
+ struct gfio_graphs *g = p;
+ cairo_t *cr;
+
+ cr = gdk_cairo_create(w->window);
+ cairo_set_source_rgb(cr, 0, 0, 0);
+ draw_graph(g->iops_graph, cr);
+ draw_graph(g->bandwidth_graph, cr);
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+
+/*
+ * 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;