2 * gfio - gui front end for fio - the flexible io tester
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #define GFIO_MIME "text/fio"
37 static int gfio_server_running;
38 static const char *gfio_graph_font;
39 static unsigned int gfio_graph_limit = 100;
40 static GdkColor white;
42 static void view_log(GtkWidget *w, gpointer data);
44 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
46 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
48 static void connect_clicked(GtkWidget *widget, gpointer data);
49 static void start_job_clicked(GtkWidget *widget, gpointer data);
50 static void send_clicked(GtkWidget *widget, gpointer data);
52 static struct button_spec {
53 const char *buttontext;
55 const char *tooltiptext;
56 const int start_insensitive;
57 } buttonspeclist[] = {
58 #define CONNECT_BUTTON 0
60 #define START_JOB_BUTTON 2
61 { "Connect", connect_clicked, "Connect to host", 0 },
62 { "Send", send_clicked, "Send job description to host", 1 },
63 { "Start Job", start_job_clicked,
64 "Start the current job on the server", 1 },
74 struct multitext_widget {
77 unsigned int cur_text;
78 unsigned int max_text;
83 struct multitext_widget iotype;
84 struct multitext_widget ioengine;
85 struct multitext_widget iodepth;
93 GtkWidget *write_iops;
99 #define DRAWING_AREA_XDIM 1000
100 #define DRAWING_AREA_YDIM 400
101 GtkWidget *drawing_area;
102 struct graph *iops_graph;
103 struct graph *bandwidth_graph;
107 * Main window widgets and data
110 GtkUIManager *uimanager;
111 GtkRecentManager *recentmanager;
112 GtkActionGroup *actiongroup;
117 GtkWidget *thread_status_pb;
118 GtkWidget *buttonbox;
120 GtkWidget *error_info_bar;
121 GtkWidget *error_label;
122 GtkListStore *log_model;
125 struct gfio_graphs graphs;
126 struct probe_widget probe;
127 struct eta_widget eta;
133 struct flist_head list;
140 GE_STATE_JOB_STARTED,
141 GE_STATE_JOB_RUNNING,
149 struct flist_head list;
153 GtkWidget *job_notebook;
154 GtkWidget *thread_status_pb;
155 GtkWidget *buttonbox;
156 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
158 GtkWidget *error_info_bar;
159 GtkWidget *error_label;
160 GtkWidget *results_notebook;
161 GtkWidget *results_window;
162 GtkUIManager *results_uimanager;
163 GtkWidget *results_vbox;
164 GtkWidget *results_menu;
165 GtkListStore *log_model;
168 struct gfio_graphs graphs;
169 struct probe_widget probe;
170 struct eta_widget eta;
171 GtkWidget *page_label;
175 struct gfio_client *client;
178 struct graph *clat_graph;
182 struct gui_entry *ge;
183 struct fio_client *client;
184 GtkWidget *results_widget;
185 GtkWidget *disk_util_frame;
186 GtkWidget *err_entry;
187 unsigned int job_added;
188 struct thread_options o;
191 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
192 static void gfio_update_thread_status_all(char *status_message, double perc);
193 void report_error(GError *error);
195 static struct graph *setup_iops_graph(void)
199 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
200 graph_title(g, "IOPS (IOs/sec)");
201 graph_x_title(g, "Time (secs)");
202 graph_add_label(g, "Read IOPS");
203 graph_add_label(g, "Write IOPS");
204 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
205 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
206 line_graph_set_data_count_limit(g, gfio_graph_limit);
207 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
211 static struct graph *setup_bandwidth_graph(void)
215 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
216 graph_title(g, "Bandwidth (bytes/sec)");
217 graph_x_title(g, "Time (secs)");
218 graph_add_label(g, "Read Bandwidth");
219 graph_add_label(g, "Write Bandwidth");
220 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
221 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
222 graph_set_base_offset(g, 1);
223 line_graph_set_data_count_limit(g, 100);
224 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
228 static void setup_graphs(struct gfio_graphs *g)
230 g->iops_graph = setup_iops_graph();
231 g->bandwidth_graph = setup_bandwidth_graph();
234 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
236 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
237 mt->text[mt->max_text] = strdup(text);
241 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
243 if (index >= mt->max_text)
245 if (!mt->text || !mt->text[index])
248 mt->cur_text = index;
249 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
252 static void multitext_update_entry(struct multitext_widget *mt,
253 unsigned int index, const char *text)
259 free(mt->text[index]);
261 mt->text[index] = strdup(text);
262 if (mt->cur_text == index)
263 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
266 static void multitext_free(struct multitext_widget *mt)
270 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
272 for (i = 0; i < mt->max_text; i++) {
282 static void clear_ge_ui_info(struct gui_entry *ge)
284 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
285 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
286 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
287 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
289 /* should we empty it... */
290 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
292 multitext_update_entry(&ge->eta.iotype, 0, "");
293 multitext_update_entry(&ge->eta.ioengine, 0, "");
294 multitext_update_entry(&ge->eta.iodepth, 0, "");
295 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
296 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
297 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
298 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
299 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
300 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
303 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
305 GtkWidget *entry, *frame;
307 frame = gtk_frame_new(label);
308 entry = gtk_combo_box_new_text();
309 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
310 gtk_container_add(GTK_CONTAINER(frame), entry);
315 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
317 GtkWidget *entry, *frame;
319 frame = gtk_frame_new(label);
320 entry = gtk_entry_new();
321 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
322 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
323 gtk_container_add(GTK_CONTAINER(frame), entry);
328 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
330 GtkWidget *label_widget;
333 frame = gtk_frame_new(label);
334 label_widget = gtk_label_new(NULL);
335 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
336 gtk_container_add(GTK_CONTAINER(frame), label_widget);
341 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
343 GtkWidget *button, *box;
345 box = gtk_hbox_new(FALSE, 3);
346 gtk_container_add(GTK_CONTAINER(hbox), box);
348 button = gtk_spin_button_new_with_range(min, max, 1.0);
349 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
351 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
352 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
357 static void label_set_int_value(GtkWidget *entry, unsigned int val)
361 sprintf(tmp, "%u", val);
362 gtk_label_set_text(GTK_LABEL(entry), tmp);
365 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
369 sprintf(tmp, "%u", val);
370 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
373 static void show_info_dialog(struct gui *ui, const char *title,
376 GtkWidget *dialog, *content, *label;
378 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
379 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
380 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
382 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
383 label = gtk_label_new(message);
384 gtk_container_add(GTK_CONTAINER(content), label);
385 gtk_widget_show_all(dialog);
386 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
387 gtk_dialog_run(GTK_DIALOG(dialog));
388 gtk_widget_destroy(dialog);
392 * Update sensitivity of job buttons and job menu items, based on the
393 * state of the client.
395 static void update_button_states(struct gui *ui, struct gui_entry *ge)
397 unsigned int connect_state, send_state, start_state, edit_state;
398 const char *connect_str = NULL;
405 sprintf(tmp, "Bad client state: %u\n", ge->state);
406 show_info_dialog(ui, "Error", tmp);
407 /* fall through to new state */
413 connect_str = "Connect";
417 case GE_STATE_CONNECTED:
420 connect_str = "Disconnect";
424 case GE_STATE_JOB_SENT:
427 connect_str = "Disconnect";
431 case GE_STATE_JOB_STARTED:
434 connect_str = "Disconnect";
438 case GE_STATE_JOB_RUNNING:
441 connect_str = "Disconnect";
445 case GE_STATE_JOB_DONE:
448 connect_str = "Connect";
454 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
455 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
456 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
457 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
459 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Connect");
460 gtk_widget_set_sensitive(w, connect_state);
461 gtk_menu_item_set_label(GTK_MENU_ITEM(w), connect_str);
463 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Edit job");
464 gtk_widget_set_sensitive(w, edit_state);
466 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Send job");
467 gtk_widget_set_sensitive(w, send_state);
469 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Start job");
470 gtk_widget_set_sensitive(w, start_state);
473 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
476 update_button_states(ge->ui, ge);
480 #define ALIGN_RIGHT 2
484 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
486 GtkCellRenderer *renderer;
487 GtkTreeViewColumn *col;
488 double xalign = 0.0; /* left as default */
489 PangoAlignment align;
492 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
493 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
495 visible = !(flags & INVISIBLE);
497 renderer = gtk_cell_renderer_text_new();
498 col = gtk_tree_view_column_new();
500 gtk_tree_view_column_set_title(col, title);
501 if (!(flags & UNSORTABLE))
502 gtk_tree_view_column_set_sort_column_id(col, index);
503 gtk_tree_view_column_set_resizable(col, TRUE);
504 gtk_tree_view_column_pack_start(col, renderer, TRUE);
505 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
506 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
508 case PANGO_ALIGN_LEFT:
511 case PANGO_ALIGN_CENTER:
514 case PANGO_ALIGN_RIGHT:
518 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
519 gtk_tree_view_column_set_visible(col, visible);
520 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
524 static void gfio_ui_setup_log(struct gui *ui)
526 GtkTreeSelection *selection;
528 GtkWidget *tree_view;
530 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
532 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
533 gtk_widget_set_can_focus(tree_view, FALSE);
535 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
536 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
537 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
538 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
540 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
541 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
542 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
543 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
545 ui->log_model = model;
546 ui->log_tree = tree_view;
549 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
552 double xdim, double ydim)
557 g = graph_new(xdim, ydim, gfio_graph_font);
558 graph_title(g, title);
559 graph_x_title(g, "Percentile");
561 for (i = 0; i < len; i++) {
564 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
565 graph_add_label(g, fbuf);
566 graph_add_data(g, fbuf, (double) ovals[i]);
572 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
578 GType types[FIO_IO_U_LIST_MAX_LEN];
579 GtkWidget *tree_view;
580 GtkTreeSelection *selection;
585 for (i = 0; i < len; i++)
586 types[i] = G_TYPE_INT;
588 model = gtk_list_store_newv(len, types);
590 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
591 gtk_widget_set_can_focus(tree_view, FALSE);
593 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
594 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
596 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
597 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
599 for (i = 0; i < len; i++) {
602 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
603 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
606 gtk_list_store_append(model, &iter);
608 for (i = 0; i < len; i++) {
610 ovals[i] = (ovals[i] + 999) / 1000;
611 gtk_list_store_set(model, &iter, i, ovals[i], -1);
617 static int on_expose_clat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
622 cr = gdk_cairo_create(w->window);
624 if (graph_has_tooltips(g)) {
625 g_object_set(w, "has-tooltip", TRUE, NULL);
626 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
629 cairo_set_source_rgb(cr, 0, 0, 0);
630 bar_graph_draw(g, cr);
636 static gint on_config_clat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
639 struct graph *g = data;
641 graph_set_size(g, w->allocation.width, w->allocation.height);
642 graph_set_size(g, w->allocation.width, w->allocation.height);
643 graph_set_position(g, 0, 0);
647 static void gfio_show_clat_percentiles(struct gfio_client *gc,
648 GtkWidget *vbox, struct thread_stat *ts,
651 unsigned int *io_u_plat = ts->io_u_plat[ddir];
652 unsigned long nr = ts->clat_stat[ddir].samples;
653 fio_fp64_t *plist = ts->percentile_list;
654 unsigned int *ovals, len, minv, maxv, scale_down;
656 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
659 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
664 * We default to usecs, but if the value range is such that we
665 * should scale down to msecs, do that.
667 if (minv > 2000 && maxv > 99999) {
675 sprintf(tmp, "Completion percentiles (%s)", base);
676 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
677 gc->ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
679 frame = gtk_frame_new(tmp);
680 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
682 completion_vbox = gtk_vbox_new(FALSE, 3);
683 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
684 hbox = gtk_hbox_new(FALSE, 3);
685 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
686 drawing_area = gtk_drawing_area_new();
687 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
688 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
689 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
690 g_signal_connect(G_OBJECT(drawing_area), "expose_event",
691 G_CALLBACK(on_expose_clat_drawing_area), gc->ge->clat_graph);
692 g_signal_connect(G_OBJECT(drawing_area), "configure_event",
693 G_CALLBACK(on_config_clat_drawing_area), gc->ge->clat_graph);
695 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
701 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
702 unsigned long max, double mean, double dev)
704 const char *base = "(usec)";
705 GtkWidget *hbox, *label, *frame;
709 if (!usec_to_msec(&min, &max, &mean, &dev))
712 minp = num2str(min, 6, 1, 0);
713 maxp = num2str(max, 6, 1, 0);
715 sprintf(tmp, "%s %s", name, base);
716 frame = gtk_frame_new(tmp);
717 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
719 hbox = gtk_hbox_new(FALSE, 3);
720 gtk_container_add(GTK_CONTAINER(frame), hbox);
722 label = new_info_label_in_frame(hbox, "Minimum");
723 gtk_label_set_text(GTK_LABEL(label), minp);
724 label = new_info_label_in_frame(hbox, "Maximum");
725 gtk_label_set_text(GTK_LABEL(label), maxp);
726 label = new_info_label_in_frame(hbox, "Average");
727 sprintf(tmp, "%5.02f", mean);
728 gtk_label_set_text(GTK_LABEL(label), tmp);
729 label = new_info_label_in_frame(hbox, "Standard deviation");
730 sprintf(tmp, "%5.02f", dev);
731 gtk_label_set_text(GTK_LABEL(label), tmp);
742 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
743 struct group_run_stats *rs,
744 struct thread_stat *ts, int ddir)
746 const char *ddir_label[2] = { "Read", "Write" };
747 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
748 unsigned long min[3], max[3], runt;
749 unsigned long long bw, iops;
750 unsigned int flags = 0;
751 double mean[3], dev[3];
752 char *io_p, *bw_p, *iops_p;
755 if (!ts->runtime[ddir])
758 i2p = is_power_of_2(rs->kb_base);
759 runt = ts->runtime[ddir];
761 bw = (1000 * ts->io_bytes[ddir]) / runt;
762 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
763 bw_p = num2str(bw, 6, 1, i2p);
765 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
766 iops_p = num2str(iops, 6, 1, 0);
768 box = gtk_hbox_new(FALSE, 3);
769 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
771 frame = gtk_frame_new(ddir_label[ddir]);
772 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
774 main_vbox = gtk_vbox_new(FALSE, 3);
775 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
777 box = gtk_hbox_new(FALSE, 3);
778 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
780 label = new_info_label_in_frame(box, "IO");
781 gtk_label_set_text(GTK_LABEL(label), io_p);
782 label = new_info_label_in_frame(box, "Bandwidth");
783 gtk_label_set_text(GTK_LABEL(label), bw_p);
784 label = new_info_label_in_frame(box, "IOPS");
785 gtk_label_set_text(GTK_LABEL(label), iops_p);
786 label = new_info_label_in_frame(box, "Runtime (msec)");
787 label_set_int_value(label, ts->runtime[ddir]);
789 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
790 double p_of_agg = 100.0;
791 const char *bw_str = "KB";
795 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
796 if (p_of_agg > 100.0)
800 if (mean[0] > 999999.9) {
808 sprintf(tmp, "Bandwidth (%s)", bw_str);
809 frame = gtk_frame_new(tmp);
810 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
812 box = gtk_hbox_new(FALSE, 3);
813 gtk_container_add(GTK_CONTAINER(frame), box);
815 label = new_info_label_in_frame(box, "Minimum");
816 label_set_int_value(label, min[0]);
817 label = new_info_label_in_frame(box, "Maximum");
818 label_set_int_value(label, max[0]);
819 label = new_info_label_in_frame(box, "Percentage of jobs");
820 sprintf(tmp, "%3.2f%%", p_of_agg);
821 gtk_label_set_text(GTK_LABEL(label), tmp);
822 label = new_info_label_in_frame(box, "Average");
823 sprintf(tmp, "%5.02f", mean[0]);
824 gtk_label_set_text(GTK_LABEL(label), tmp);
825 label = new_info_label_in_frame(box, "Standard deviation");
826 sprintf(tmp, "%5.02f", dev[0]);
827 gtk_label_set_text(GTK_LABEL(label), tmp);
830 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
832 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
834 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
838 frame = gtk_frame_new("Latency");
839 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
841 vbox = gtk_vbox_new(FALSE, 3);
842 gtk_container_add(GTK_CONTAINER(frame), vbox);
844 if (flags & GFIO_SLAT)
845 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
846 if (flags & GFIO_CLAT)
847 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
848 if (flags & GFIO_LAT)
849 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
852 if (ts->clat_percentiles)
853 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
861 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
864 GtkWidget *tree_view;
865 GtkTreeSelection *selection;
872 * Check if all are empty, in which case don't bother
874 for (i = 0, skipped = 0; i < num; i++)
881 types = malloc(num * sizeof(GType));
883 for (i = 0; i < num; i++)
884 types[i] = G_TYPE_STRING;
886 model = gtk_list_store_newv(num, types);
890 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
891 gtk_widget_set_can_focus(tree_view, FALSE);
893 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
894 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
896 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
897 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
899 for (i = 0; i < num; i++)
900 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
902 gtk_list_store_append(model, &iter);
904 for (i = 0; i < num; i++) {
908 sprintf(fbuf, "0.00");
910 sprintf(fbuf, "%3.2f%%", lat[i]);
912 gtk_list_store_set(model, &iter, i, fbuf, -1);
918 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
920 GtkWidget *box, *frame, *tree_view;
921 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
922 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
923 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
924 "250", "500", "750", "1000", };
925 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
926 "250", "500", "750", "1000", "2000",
929 stat_calc_lat_u(ts, io_u_lat_u);
930 stat_calc_lat_m(ts, io_u_lat_m);
932 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
934 frame = gtk_frame_new("Latency buckets (usec)");
935 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
937 box = gtk_hbox_new(FALSE, 3);
938 gtk_container_add(GTK_CONTAINER(frame), box);
939 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
942 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
944 frame = gtk_frame_new("Latency buckets (msec)");
945 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
947 box = gtk_hbox_new(FALSE, 3);
948 gtk_container_add(GTK_CONTAINER(frame), box);
949 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
953 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
955 GtkWidget *box, *frame, *entry;
956 double usr_cpu, sys_cpu;
957 unsigned long runtime;
960 runtime = ts->total_run_time;
962 double runt = (double) runtime;
964 usr_cpu = (double) ts->usr_time * 100 / runt;
965 sys_cpu = (double) ts->sys_time * 100 / runt;
971 frame = gtk_frame_new("OS resources");
972 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
974 box = gtk_hbox_new(FALSE, 3);
975 gtk_container_add(GTK_CONTAINER(frame), box);
977 entry = new_info_entry_in_frame(box, "User CPU");
978 sprintf(tmp, "%3.2f%%", usr_cpu);
979 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
980 entry = new_info_entry_in_frame(box, "System CPU");
981 sprintf(tmp, "%3.2f%%", sys_cpu);
982 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
983 entry = new_info_entry_in_frame(box, "Context switches");
984 entry_set_int_value(entry, ts->ctx);
985 entry = new_info_entry_in_frame(box, "Major faults");
986 entry_set_int_value(entry, ts->majf);
987 entry = new_info_entry_in_frame(box, "Minor faults");
988 entry_set_int_value(entry, ts->minf);
990 static void gfio_add_sc_depths_tree(GtkListStore *model,
991 struct thread_stat *ts, unsigned int len,
994 double io_u_dist[FIO_IO_U_MAP_NR];
996 /* Bits 0, and 3-8 */
997 const int add_mask = 0x1f9;
1001 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1003 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1005 gtk_list_store_append(model, &iter);
1007 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1009 for (i = 1, j = 0; i < len; i++) {
1012 if (!(add_mask & (1UL << (i - 1))))
1013 sprintf(fbuf, "0.0%%");
1015 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1019 gtk_list_store_set(model, &iter, i, fbuf, -1);
1024 static void gfio_add_total_depths_tree(GtkListStore *model,
1025 struct thread_stat *ts, unsigned int len)
1027 double io_u_dist[FIO_IO_U_MAP_NR];
1029 /* Bits 1-6, and 8 */
1030 const int add_mask = 0x17e;
1033 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1035 gtk_list_store_append(model, &iter);
1037 gtk_list_store_set(model, &iter, 0, "Total", -1);
1039 for (i = 1, j = 0; i < len; i++) {
1042 if (!(add_mask & (1UL << (i - 1))))
1043 sprintf(fbuf, "0.0%%");
1045 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1049 gtk_list_store_set(model, &iter, i, fbuf, -1);
1054 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1056 GtkWidget *frame, *box, *tree_view;
1057 GtkTreeSelection *selection;
1058 GtkListStore *model;
1059 GType types[FIO_IO_U_MAP_NR + 1];
1061 #define NR_LABELS 10
1062 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1064 frame = gtk_frame_new("IO depths");
1065 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1067 box = gtk_hbox_new(FALSE, 3);
1068 gtk_container_add(GTK_CONTAINER(frame), box);
1070 for (i = 0; i < NR_LABELS; i++)
1071 types[i] = G_TYPE_STRING;
1073 model = gtk_list_store_newv(NR_LABELS, types);
1075 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1076 gtk_widget_set_can_focus(tree_view, FALSE);
1078 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1079 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1081 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1082 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1084 for (i = 0; i < NR_LABELS; i++)
1085 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1087 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1088 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1089 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1091 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1094 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1096 struct gui_entry *ge = (struct gui_entry *) data;
1098 gtk_widget_destroy(w);
1099 ge->results_window = NULL;
1100 ge->results_notebook = NULL;
1104 static void results_close(GtkWidget *w, gpointer *data)
1106 struct gui_entry *ge = (struct gui_entry *) data;
1108 gtk_widget_destroy(ge->results_window);
1111 static GtkActionEntry results_menu_items[] = {
1112 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1113 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1114 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1116 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1118 static const gchar *results_ui_string = " \
1120 <menubar name=\"MainMenu\"> \
1121 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1122 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1124 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1130 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1132 GtkActionGroup *action_group;
1136 ge->results_uimanager = gtk_ui_manager_new();
1138 action_group = gtk_action_group_new("ResultsMenu");
1139 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1141 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1142 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1144 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1146 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1150 static GtkWidget *get_results_window(struct gui_entry *ge)
1152 GtkWidget *win, *notebook, *vbox;
1154 if (ge->results_window)
1155 return ge->results_notebook;
1157 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1158 gtk_window_set_title(GTK_WINDOW(win), "Results");
1159 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1160 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1161 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1163 vbox = gtk_vbox_new(FALSE, 0);
1164 gtk_container_add(GTK_CONTAINER(win), vbox);
1166 ge->results_menu = get_results_menubar(win, ge);
1167 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1169 notebook = gtk_notebook_new();
1170 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1171 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1172 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1174 ge->results_window = win;
1175 ge->results_notebook = notebook;
1176 return ge->results_notebook;
1179 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1180 struct group_run_stats *rs)
1182 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
1183 struct gfio_client *gc = client->client_data;
1185 gdk_threads_enter();
1187 res_win = get_results_window(gc->ge);
1189 scroll = gtk_scrolled_window_new(NULL, NULL);
1190 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1191 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1193 vbox = gtk_vbox_new(FALSE, 3);
1195 box = gtk_hbox_new(FALSE, 0);
1196 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1198 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1200 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1202 gc->results_widget = vbox;
1204 entry = new_info_entry_in_frame(box, "Name");
1205 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1206 if (strlen(ts->description)) {
1207 entry = new_info_entry_in_frame(box, "Description");
1208 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1210 entry = new_info_entry_in_frame(box, "Group ID");
1211 entry_set_int_value(entry, ts->groupid);
1212 entry = new_info_entry_in_frame(box, "Jobs");
1213 entry_set_int_value(entry, ts->members);
1214 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1215 entry_set_int_value(entry, ts->error);
1216 entry = new_info_entry_in_frame(box, "PID");
1217 entry_set_int_value(entry, ts->pid);
1219 if (ts->io_bytes[DDIR_READ])
1220 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1221 if (ts->io_bytes[DDIR_WRITE])
1222 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1224 gfio_show_latency_buckets(vbox, ts);
1225 gfio_show_cpu_usage(vbox, ts);
1226 gfio_show_io_depths(vbox, ts);
1228 gtk_widget_show_all(gc->ge->results_window);
1229 gdk_threads_leave();
1232 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1234 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1235 struct gui *ui = &main_ui;
1239 char tmp[64], timebuf[80];
1242 tm = localtime(&sec);
1243 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1244 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1246 gdk_threads_enter();
1248 gtk_list_store_append(ui->log_model, &iter);
1249 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1250 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1251 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1252 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1254 if (p->level == FIO_LOG_ERR)
1255 view_log(NULL, (gpointer) ui);
1257 gdk_threads_leave();
1260 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1262 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1263 struct gfio_client *gc = client->client_data;
1264 GtkWidget *box, *frame, *entry, *vbox;
1268 gdk_threads_enter();
1270 if (!gc->results_widget)
1273 if (!gc->disk_util_frame) {
1274 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1275 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1278 vbox = gtk_vbox_new(FALSE, 3);
1279 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1281 frame = gtk_frame_new((char *) p->dus.name);
1282 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1284 box = gtk_vbox_new(FALSE, 3);
1285 gtk_container_add(GTK_CONTAINER(frame), box);
1287 frame = gtk_frame_new("Read");
1288 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1289 vbox = gtk_hbox_new(TRUE, 3);
1290 gtk_container_add(GTK_CONTAINER(frame), vbox);
1291 entry = new_info_entry_in_frame(vbox, "IOs");
1292 entry_set_int_value(entry, p->dus.ios[0]);
1293 entry = new_info_entry_in_frame(vbox, "Merges");
1294 entry_set_int_value(entry, p->dus.merges[0]);
1295 entry = new_info_entry_in_frame(vbox, "Sectors");
1296 entry_set_int_value(entry, p->dus.sectors[0]);
1297 entry = new_info_entry_in_frame(vbox, "Ticks");
1298 entry_set_int_value(entry, p->dus.ticks[0]);
1300 frame = gtk_frame_new("Write");
1301 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1302 vbox = gtk_hbox_new(TRUE, 3);
1303 gtk_container_add(GTK_CONTAINER(frame), vbox);
1304 entry = new_info_entry_in_frame(vbox, "IOs");
1305 entry_set_int_value(entry, p->dus.ios[1]);
1306 entry = new_info_entry_in_frame(vbox, "Merges");
1307 entry_set_int_value(entry, p->dus.merges[1]);
1308 entry = new_info_entry_in_frame(vbox, "Sectors");
1309 entry_set_int_value(entry, p->dus.sectors[1]);
1310 entry = new_info_entry_in_frame(vbox, "Ticks");
1311 entry_set_int_value(entry, p->dus.ticks[1]);
1313 frame = gtk_frame_new("Shared");
1314 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1315 vbox = gtk_hbox_new(TRUE, 3);
1316 gtk_container_add(GTK_CONTAINER(frame), vbox);
1317 entry = new_info_entry_in_frame(vbox, "IO ticks");
1318 entry_set_int_value(entry, p->dus.io_ticks);
1319 entry = new_info_entry_in_frame(vbox, "Time in queue");
1320 entry_set_int_value(entry, p->dus.time_in_queue);
1324 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1328 sprintf(tmp, "%3.2f%%", util);
1329 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1330 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1332 gtk_widget_show_all(gc->results_widget);
1334 gdk_threads_leave();
1337 extern int sum_stat_clients;
1338 extern struct thread_stat client_ts;
1339 extern struct group_run_stats client_gs;
1341 static int sum_stat_nr;
1343 static void gfio_thread_status_op(struct fio_client *client,
1344 struct fio_net_cmd *cmd)
1346 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1348 gfio_display_ts(client, &p->ts, &p->rs);
1350 if (sum_stat_clients == 1)
1353 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1354 sum_group_stats(&client_gs, &p->rs);
1356 client_ts.members++;
1357 client_ts.groupid = p->ts.groupid;
1359 if (++sum_stat_nr == sum_stat_clients) {
1360 strcpy(client_ts.name, "All clients");
1361 gfio_display_ts(client, &client_ts, &client_gs);
1365 static void gfio_group_stats_op(struct fio_client *client,
1366 struct fio_net_cmd *cmd)
1368 /* We're ignoring group stats for now */
1371 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1374 struct gfio_graphs *g = data;
1376 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1377 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1378 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1379 graph_set_position(g->bandwidth_graph, 0, 0);
1383 static void draw_graph(struct graph *g, cairo_t *cr)
1385 line_graph_draw(g, cr);
1389 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1390 gboolean keyboard_mode, GtkTooltip *tooltip,
1393 struct gfio_graphs *g = data;
1394 const char *text = NULL;
1396 if (graph_contains_xy(g->iops_graph, x, y))
1397 text = graph_find_tooltip(g->iops_graph, x, y);
1398 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1399 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1402 gtk_tooltip_set_text(tooltip, text);
1409 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1411 struct gfio_graphs *g = p;
1414 cr = gdk_cairo_create(w->window);
1416 if (graph_has_tooltips(g->iops_graph) ||
1417 graph_has_tooltips(g->bandwidth_graph)) {
1418 g_object_set(w, "has-tooltip", TRUE, NULL);
1419 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1422 cairo_set_source_rgb(cr, 0, 0, 0);
1423 draw_graph(g->iops_graph, cr);
1424 draw_graph(g->bandwidth_graph, cr);
1431 * Client specific ETA
1433 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1435 struct gfio_client *gc = client->client_data;
1436 struct gui_entry *ge = gc->ge;
1437 static int eta_good;
1444 gdk_threads_enter();
1449 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1450 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1451 eta_to_str(eta_str, je->eta_sec);
1454 sprintf(tmp, "%u", je->nr_running);
1455 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1456 sprintf(tmp, "%u", je->files_open);
1457 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1460 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1461 if (je->m_rate || je->t_rate) {
1464 mr = num2str(je->m_rate, 4, 0, i2p);
1465 tr = num2str(je->t_rate, 4, 0, i2p);
1466 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1467 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1470 } else if (je->m_iops || je->t_iops)
1471 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1473 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1474 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1475 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1476 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1479 if (je->eta_sec != INT_MAX && je->nr_running) {
1483 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1484 strcpy(output, "-.-% done");
1488 sprintf(output, "%3.1f%% done", perc);
1491 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1492 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1494 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1495 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1497 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1498 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1499 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1500 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1502 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1503 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1504 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1505 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1514 char *dst = output + strlen(output);
1516 sprintf(dst, " - %s", eta_str);
1519 gfio_update_thread_status(ge, output, perc);
1520 gdk_threads_leave();
1524 * Update ETA in main window for all clients
1526 static void gfio_update_all_eta(struct jobs_eta *je)
1528 struct gui *ui = &main_ui;
1529 static int eta_good;
1535 gdk_threads_enter();
1540 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1541 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1542 eta_to_str(eta_str, je->eta_sec);
1546 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1547 if (je->m_rate || je->t_rate) {
1550 mr = num2str(je->m_rate, 4, 0, i2p);
1551 tr = num2str(je->t_rate, 4, 0, i2p);
1552 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1553 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1556 } else if (je->m_iops || je->t_iops)
1557 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1559 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1560 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1561 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1562 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1565 entry_set_int_value(ui->eta.jobs, je->nr_running);
1567 if (je->eta_sec != INT_MAX && je->nr_running) {
1571 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1572 strcpy(output, "-.-% done");
1576 sprintf(output, "%3.1f%% done", perc);
1579 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1580 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1582 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1583 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1585 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1586 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1587 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1588 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1590 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1591 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1592 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1593 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1602 char *dst = output + strlen(output);
1604 sprintf(dst, " - %s", eta_str);
1607 gfio_update_thread_status_all(output, perc);
1608 gdk_threads_leave();
1611 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1613 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1614 struct gfio_client *gc = client->client_data;
1615 struct gui_entry *ge = gc->ge;
1616 const char *os, *arch;
1619 os = fio_get_os_string(probe->os);
1623 arch = fio_get_arch_string(probe->arch);
1628 client->name = strdup((char *) probe->hostname);
1630 gdk_threads_enter();
1632 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1633 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1634 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1635 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1636 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1638 gfio_set_state(ge, GE_STATE_CONNECTED);
1640 gdk_threads_leave();
1643 static void gfio_update_thread_status(struct gui_entry *ge,
1644 char *status_message, double perc)
1646 static char message[100];
1647 const char *m = message;
1649 strncpy(message, status_message, sizeof(message) - 1);
1650 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1651 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1652 gtk_widget_queue_draw(main_ui.window);
1655 static void gfio_update_thread_status_all(char *status_message, double perc)
1657 struct gui *ui = &main_ui;
1658 static char message[100];
1659 const char *m = message;
1661 strncpy(message, status_message, sizeof(message) - 1);
1662 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1663 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1664 gtk_widget_queue_draw(ui->window);
1667 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1669 struct gfio_client *gc = client->client_data;
1671 gdk_threads_enter();
1672 gfio_set_state(gc->ge, GE_STATE_NEW);
1673 gdk_threads_leave();
1676 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1678 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1679 struct gfio_client *gc = client->client_data;
1680 struct thread_options *o = &gc->o;
1681 struct gui_entry *ge = gc->ge;
1684 convert_thread_options_to_cpu(o, &p->top);
1686 gdk_threads_enter();
1688 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1690 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1691 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1693 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1694 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1696 sprintf(tmp, "%u", o->iodepth);
1697 multitext_add_entry(&ge->eta.iodepth, tmp);
1699 multitext_set_entry(&ge->eta.iotype, 0);
1700 multitext_set_entry(&ge->eta.ioengine, 0);
1701 multitext_set_entry(&ge->eta.iodepth, 0);
1705 gfio_set_state(ge, GE_STATE_JOB_SENT);
1707 gdk_threads_leave();
1710 static void gfio_client_timed_out(struct fio_client *client)
1712 struct gfio_client *gc = client->client_data;
1715 gdk_threads_enter();
1717 gfio_set_state(gc->ge, GE_STATE_NEW);
1718 clear_ge_ui_info(gc->ge);
1720 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1721 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1723 gdk_threads_leave();
1726 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1728 struct gfio_client *gc = client->client_data;
1730 gdk_threads_enter();
1732 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1735 entry_set_int_value(gc->err_entry, client->error);
1737 gdk_threads_leave();
1740 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1742 struct gfio_client *gc = client->client_data;
1744 gdk_threads_enter();
1745 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1746 gdk_threads_leave();
1749 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1751 struct gfio_client *gc = client->client_data;
1753 gdk_threads_enter();
1754 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1755 gdk_threads_leave();
1758 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1760 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1764 struct client_ops gfio_client_ops = {
1765 .text = gfio_text_op,
1766 .disk_util = gfio_disk_util_op,
1767 .thread_status = gfio_thread_status_op,
1768 .group_stats = gfio_group_stats_op,
1769 .jobs_eta = gfio_update_client_eta,
1770 .eta = gfio_update_all_eta,
1771 .probe = gfio_probe_op,
1772 .quit = gfio_quit_op,
1773 .add_job = gfio_add_job_op,
1774 .timed_out = gfio_client_timed_out,
1775 .stop = gfio_client_stop,
1776 .start = gfio_client_start,
1777 .job_start = gfio_client_job_start,
1778 .iolog = gfio_client_iolog,
1779 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1780 .stay_connected = 1,
1781 .client_type = FIO_CLIENT_TYPE_GUI,
1785 * FIXME: need more handling here
1787 static void ge_destroy(struct gui_entry *ge)
1789 struct gfio_client *gc = ge->client;
1791 if (gc && gc->client) {
1792 if (ge->state >= GE_STATE_CONNECTED)
1793 fio_client_terminate(gc->client);
1795 fio_put_client(gc->client);
1798 flist_del(&ge->list);
1802 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1806 static void gfio_quit(struct gui *ui)
1808 struct gui_entry *ge;
1810 while (!flist_empty(&ui->list)) {
1811 ge = flist_entry(ui->list.next, struct gui_entry, list);
1818 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1819 __attribute__((unused)) gpointer data)
1824 static void *job_thread(void *arg)
1826 struct gui *ui = arg;
1828 ui->handler_running = 1;
1829 fio_handle_clients(&gfio_client_ops);
1830 ui->handler_running = 0;
1834 static int send_job_files(struct gui_entry *ge)
1836 struct gfio_client *gc = ge->client;
1839 for (i = 0; i < ge->nr_job_files; i++) {
1840 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1844 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1845 report_error(error);
1846 g_error_free(error);
1851 free(ge->job_files[i]);
1852 ge->job_files[i] = NULL;
1854 while (i < ge->nr_job_files) {
1855 free(ge->job_files[i]);
1856 ge->job_files[i] = NULL;
1860 free(ge->job_files);
1861 ge->job_files = NULL;
1862 ge->nr_job_files = 0;
1866 static void *server_thread(void *arg)
1869 gfio_server_running = 1;
1870 fio_start_server(NULL);
1871 gfio_server_running = 0;
1875 static void gfio_start_server(void)
1877 struct gui *ui = &main_ui;
1879 if (!gfio_server_running) {
1880 gfio_server_running = 1;
1881 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1882 pthread_detach(ui->server_t);
1886 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1889 struct gui_entry *ge = data;
1890 struct gfio_client *gc = ge->client;
1893 fio_start_client(gc->client);
1896 static void file_open(GtkWidget *w, gpointer data);
1898 static void connect_clicked(GtkWidget *widget, gpointer data)
1900 struct gui_entry *ge = data;
1901 struct gfio_client *gc = ge->client;
1903 if (ge->state == GE_STATE_NEW) {
1906 if (!ge->nr_job_files)
1907 file_open(widget, ge->ui);
1908 if (!ge->nr_job_files)
1911 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1912 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1913 ret = fio_client_connect(gc->client);
1915 if (!ge->ui->handler_running)
1916 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1917 gfio_set_state(ge, GE_STATE_CONNECTED);
1921 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1922 report_error(error);
1923 g_error_free(error);
1926 fio_client_terminate(gc->client);
1927 gfio_set_state(ge, GE_STATE_NEW);
1928 clear_ge_ui_info(ge);
1932 static void send_clicked(GtkWidget *widget, gpointer data)
1934 struct gui_entry *ge = data;
1936 if (send_job_files(ge)) {
1939 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1940 report_error(error);
1941 g_error_free(error);
1943 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1947 static GtkWidget *add_button(GtkWidget *buttonbox,
1948 struct button_spec *buttonspec, gpointer data)
1950 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1952 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1953 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1954 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1955 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1960 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1965 for (i = 0; i < nbuttons; i++)
1966 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1969 static void on_info_bar_response(GtkWidget *widget, gint response,
1972 struct gui *ui = &main_ui;
1974 if (response == GTK_RESPONSE_OK) {
1975 gtk_widget_destroy(widget);
1976 ui->error_info_bar = NULL;
1980 void report_error(GError *error)
1982 struct gui *ui = &main_ui;
1984 if (ui->error_info_bar == NULL) {
1985 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1988 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1989 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1992 ui->error_label = gtk_label_new(error->message);
1993 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1994 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1996 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1997 gtk_widget_show_all(ui->vbox);
2000 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2001 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2005 struct connection_widgets
2012 static void hostname_cb(GtkEntry *entry, gpointer data)
2014 struct connection_widgets *cw = data;
2015 int uses_net = 0, is_localhost = 0;
2020 * Check whether to display the 'auto start backend' box
2021 * or not. Show it if we are a localhost and using network,
2022 * or using a socket.
2024 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2025 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2030 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2031 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2032 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2033 !strcmp(text, "ip6-loopback"))
2037 if (!uses_net || is_localhost) {
2038 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2039 gtk_widget_set_sensitive(cw->button, 1);
2041 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2042 gtk_widget_set_sensitive(cw->button, 0);
2046 static int get_connection_details(char **host, int *port, int *type,
2049 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2050 struct connection_widgets cw;
2053 dialog = gtk_dialog_new_with_buttons("Connection details",
2054 GTK_WINDOW(main_ui.window),
2055 GTK_DIALOG_DESTROY_WITH_PARENT,
2056 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2057 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2059 frame = gtk_frame_new("Hostname / socket name");
2060 /* gtk_dialog_get_content_area() is 2.14 and newer */
2061 vbox = GTK_DIALOG(dialog)->vbox;
2062 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2064 box = gtk_vbox_new(FALSE, 6);
2065 gtk_container_add(GTK_CONTAINER(frame), box);
2067 hbox = gtk_hbox_new(TRUE, 10);
2068 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2069 cw.hentry = gtk_entry_new();
2070 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2071 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2073 frame = gtk_frame_new("Port");
2074 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2075 box = gtk_vbox_new(FALSE, 10);
2076 gtk_container_add(GTK_CONTAINER(frame), box);
2078 hbox = gtk_hbox_new(TRUE, 4);
2079 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2080 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2082 frame = gtk_frame_new("Type");
2083 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2084 box = gtk_vbox_new(FALSE, 10);
2085 gtk_container_add(GTK_CONTAINER(frame), box);
2087 hbox = gtk_hbox_new(TRUE, 4);
2088 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2090 cw.combo = gtk_combo_box_new_text();
2091 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2092 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2093 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2094 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2096 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2098 frame = gtk_frame_new("Options");
2099 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2100 box = gtk_vbox_new(FALSE, 10);
2101 gtk_container_add(GTK_CONTAINER(frame), box);
2103 hbox = gtk_hbox_new(TRUE, 4);
2104 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2106 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2107 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2108 gtk_widget_set_tooltip_text(cw.button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
2109 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2112 * Connect edit signal, so we can show/not-show the auto start button
2114 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2115 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2117 gtk_widget_show_all(dialog);
2119 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2120 gtk_widget_destroy(dialog);
2124 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2125 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2127 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2128 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2129 *type = Fio_client_ipv4;
2130 else if (!strncmp(typeentry, "IPv6", 4))
2131 *type = Fio_client_ipv6;
2133 *type = Fio_client_socket;
2136 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2138 gtk_widget_destroy(dialog);
2142 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2144 struct gfio_client *gc;
2146 gc = malloc(sizeof(*gc));
2147 memset(gc, 0, sizeof(*gc));
2149 gc->client = fio_get_client(client);
2153 client->client_data = gc;
2156 static GtkWidget *new_client_page(struct gui_entry *ge);
2158 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2160 struct gui_entry *ge;
2162 ge = malloc(sizeof(*ge));
2163 memset(ge, 0, sizeof(*ge));
2164 ge->state = GE_STATE_NEW;
2165 INIT_FLIST_HEAD(&ge->list);
2166 flist_add_tail(&ge->list, &ui->list);
2171 static struct gui_entry *get_new_ge_with_tab(const char *name)
2173 struct gui_entry *ge;
2175 ge = alloc_new_gui_entry(&main_ui);
2177 ge->vbox = new_client_page(ge);
2178 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2180 ge->page_label = gtk_label_new(name);
2181 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2183 gtk_widget_show_all(main_ui.window);
2187 static void file_new(GtkWidget *w, gpointer data)
2189 struct gui *ui = (struct gui *) data;
2190 struct gui_entry *ge;
2192 ge = get_new_ge_with_tab("Untitled");
2193 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2197 * Return the 'ge' corresponding to the tab. If the active tab is the
2198 * main tab, open a new tab.
2200 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2202 struct flist_head *entry;
2203 struct gui_entry *ge;
2208 return get_new_ge_with_tab("Untitled");
2214 flist_for_each(entry, &main_ui.list) {
2215 ge = flist_entry(entry, struct gui_entry, list);
2216 if (ge->page_num == cur_page)
2223 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2228 * Main tab is tab 0, so any current page other than 0 holds
2231 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2233 return get_ge_from_page(cur_page, NULL);
2238 static void file_close(GtkWidget *w, gpointer data)
2240 struct gui *ui = (struct gui *) data;
2241 struct gui_entry *ge;
2244 * Can't close the main tab
2246 ge = get_ge_from_cur_tab(ui);
2248 gtk_widget_destroy(ge->vbox);
2252 if (!flist_empty(&ui->list)) {
2253 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2260 static void file_add_recent(struct gui *ui, const gchar *uri)
2264 memset(&grd, 0, sizeof(grd));
2265 grd.display_name = strdup("gfio");
2266 grd.description = strdup("Fio job file");
2267 grd.mime_type = strdup(GFIO_MIME);
2268 grd.app_name = strdup(g_get_application_name());
2269 grd.app_exec = strdup("gfio %f/%u");
2271 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2274 static gchar *get_filename_from_uri(const gchar *uri)
2276 if (strncmp(uri, "file://", 7))
2279 return strdup(uri + 7);
2282 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2285 struct fio_client *client;
2288 filename = get_filename_from_uri(uri);
2290 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2291 ge->job_files[ge->nr_job_files] = strdup(filename);
2294 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2298 error = g_error_new(g_quark_from_string("fio"), 1,
2299 "Failed to add client %s", host);
2300 report_error(error);
2301 g_error_free(error);
2305 gfio_client_added(ge, client);
2306 file_add_recent(ge->ui, uri);
2310 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2312 int port, type, server_start;
2313 struct gui_entry *ge;
2316 int ret, ge_is_new = 0;
2319 * Creates new tab if current tab is the main window, or the
2320 * current tab already has a client.
2322 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2323 ge = get_ge_from_page(cur_page, &ge_is_new);
2325 ge = get_new_ge_with_tab("Untitled");
2329 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2331 if (get_connection_details(&host, &port, &type, &server_start)) {
2333 gtk_widget_destroy(ge->vbox);
2338 ret = do_file_open(ge, uri, host, type, port);
2344 gfio_start_server();
2347 gtk_widget_destroy(ge->vbox);
2353 static void recent_open(GtkAction *action, gpointer data)
2355 struct gui *ui = (struct gui *) data;
2356 GtkRecentInfo *info;
2359 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2360 uri = gtk_recent_info_get_uri(info);
2362 do_file_open_with_tab(ui, uri);
2365 static void file_open(GtkWidget *w, gpointer data)
2367 struct gui *ui = data;
2369 GSList *filenames, *fn_glist;
2370 GtkFileFilter *filter;
2372 dialog = gtk_file_chooser_dialog_new("Open File",
2373 GTK_WINDOW(ui->window),
2374 GTK_FILE_CHOOSER_ACTION_OPEN,
2375 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2376 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2378 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2380 filter = gtk_file_filter_new();
2381 gtk_file_filter_add_pattern(filter, "*.fio");
2382 gtk_file_filter_add_pattern(filter, "*.job");
2383 gtk_file_filter_add_pattern(filter, "*.ini");
2384 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2385 gtk_file_filter_set_name(filter, "Fio job file");
2386 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2388 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2389 gtk_widget_destroy(dialog);
2393 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2395 gtk_widget_destroy(dialog);
2397 filenames = fn_glist;
2398 while (filenames != NULL) {
2399 if (do_file_open_with_tab(ui, filenames->data))
2401 filenames = g_slist_next(filenames);
2404 g_slist_free(fn_glist);
2407 static void file_save(GtkWidget *w, gpointer data)
2409 struct gui *ui = data;
2412 dialog = gtk_file_chooser_dialog_new("Save File",
2413 GTK_WINDOW(ui->window),
2414 GTK_FILE_CHOOSER_ACTION_SAVE,
2415 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2416 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2419 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2420 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2422 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2425 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2426 // save_job_file(filename);
2429 gtk_widget_destroy(dialog);
2432 static void view_log_destroy(GtkWidget *w, gpointer data)
2434 struct gui *ui = (struct gui *) data;
2436 gtk_widget_ref(ui->log_tree);
2437 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2438 gtk_widget_destroy(w);
2439 ui->log_view = NULL;
2442 static void view_log(GtkWidget *w, gpointer data)
2444 GtkWidget *win, *scroll, *vbox, *box;
2445 struct gui *ui = (struct gui *) data;
2450 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2451 gtk_window_set_title(GTK_WINDOW(win), "Log");
2452 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2454 scroll = gtk_scrolled_window_new(NULL, NULL);
2456 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2458 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2460 box = gtk_hbox_new(TRUE, 0);
2461 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2462 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2463 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2465 vbox = gtk_vbox_new(TRUE, 5);
2466 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2468 gtk_container_add(GTK_CONTAINER(win), vbox);
2469 gtk_widget_show_all(win);
2472 static void connect_job_entry(GtkWidget *w, gpointer data)
2474 struct gui *ui = (struct gui *) data;
2475 struct gui_entry *ge;
2477 ge = get_ge_from_cur_tab(ui);
2479 connect_clicked(w, ge);
2482 static void send_job_entry(GtkWidget *w, gpointer data)
2484 struct gui *ui = (struct gui *) data;
2485 struct gui_entry *ge;
2487 ge = get_ge_from_cur_tab(ui);
2489 send_clicked(w, ge);
2493 static void edit_job_entry(GtkWidget *w, gpointer data)
2497 static void start_job_entry(GtkWidget *w, gpointer data)
2499 struct gui *ui = (struct gui *) data;
2500 struct gui_entry *ge;
2502 ge = get_ge_from_cur_tab(ui);
2504 start_job_clicked(w, ge);
2507 static void __update_graph_limits(struct gfio_graphs *g)
2509 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2510 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2513 static void update_graph_limits(void)
2515 struct flist_head *entry;
2516 struct gui_entry *ge;
2518 __update_graph_limits(&main_ui.graphs);
2520 flist_for_each(entry, &main_ui.list) {
2521 ge = flist_entry(entry, struct gui_entry, list);
2522 __update_graph_limits(&ge->graphs);
2526 static void preferences(GtkWidget *w, gpointer data)
2528 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2529 GtkWidget *hbox, *spin, *entry, *spin_int;
2532 dialog = gtk_dialog_new_with_buttons("Preferences",
2533 GTK_WINDOW(main_ui.window),
2534 GTK_DIALOG_DESTROY_WITH_PARENT,
2535 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2536 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2539 frame = gtk_frame_new("Graphing");
2540 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2541 vbox = gtk_vbox_new(FALSE, 6);
2542 gtk_container_add(GTK_CONTAINER(frame), vbox);
2544 hbox = gtk_hbox_new(FALSE, 5);
2545 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2546 entry = gtk_label_new("Font face to use for graph labels");
2547 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2549 font = gtk_font_button_new();
2550 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2552 box = gtk_vbox_new(FALSE, 6);
2553 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2555 hbox = gtk_hbox_new(FALSE, 5);
2556 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2557 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2558 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2560 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2562 box = gtk_vbox_new(FALSE, 6);
2563 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2565 hbox = gtk_hbox_new(FALSE, 5);
2566 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2567 entry = gtk_label_new("Client ETA request interval (msec)");
2568 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2570 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2571 frame = gtk_frame_new("Debug logging");
2572 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2573 vbox = gtk_vbox_new(FALSE, 6);
2574 gtk_container_add(GTK_CONTAINER(frame), vbox);
2576 box = gtk_hbox_new(FALSE, 6);
2577 gtk_container_add(GTK_CONTAINER(vbox), box);
2579 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2581 for (i = 0; i < FD_DEBUG_MAX; i++) {
2583 box = gtk_hbox_new(FALSE, 6);
2584 gtk_container_add(GTK_CONTAINER(vbox), box);
2588 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2589 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2590 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2593 gtk_widget_show_all(dialog);
2595 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2596 gtk_widget_destroy(dialog);
2600 for (i = 0; i < FD_DEBUG_MAX; i++) {
2603 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2605 fio_debug |= (1UL << i);
2608 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2609 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2610 update_graph_limits();
2611 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2613 gtk_widget_destroy(dialog);
2616 static void about_dialog(GtkWidget *w, gpointer data)
2618 const char *authors[] = {
2619 "Jens Axboe <axboe@kernel.dk>",
2620 "Stephen Carmeron <stephenmcameron@gmail.com>",
2623 const char *license[] = {
2624 "Fio is free software; you can redistribute it and/or modify "
2625 "it under the terms of the GNU General Public License as published by "
2626 "the Free Software Foundation; either version 2 of the License, or "
2627 "(at your option) any later version.\n",
2628 "Fio is distributed in the hope that it will be useful, "
2629 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2630 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2631 "GNU General Public License for more details.\n",
2632 "You should have received a copy of the GNU General Public License "
2633 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2634 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2636 char *license_trans;
2638 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2639 license[2], "\n", NULL);
2641 gtk_show_about_dialog(NULL,
2642 "program-name", "gfio",
2643 "comments", "Gtk2 UI for fio",
2644 "license", license_trans,
2645 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2647 "version", fio_version_string,
2648 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2649 "logo-icon-name", "fio",
2651 "wrap-license", TRUE,
2654 g_free(license_trans);
2657 static GtkActionEntry menu_items[] = {
2658 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2659 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2660 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2661 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2662 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2663 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2664 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2665 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2666 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2667 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2668 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2669 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2670 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2671 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2672 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2673 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2675 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2677 static const gchar *ui_string = " \
2679 <menubar name=\"MainMenu\"> \
2680 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2681 <menuitem name=\"New\" action=\"NewFile\" /> \
2682 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2683 <separator name=\"Separator1\"/> \
2684 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2685 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2686 <separator name=\"Separator2\"/> \
2687 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2688 <separator name=\"Separator3\"/> \
2689 <placeholder name=\"FileRecentFiles\"/> \
2690 <separator name=\"Separator4\"/> \
2691 <menuitem name=\"Quit\" action=\"Quit\" /> \
2693 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2694 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2695 <separator name=\"Separator5\"/> \
2696 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2697 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2698 <separator name=\"Separator6\"/> \
2699 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2701 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2702 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2704 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2705 <menuitem name=\"About\" action=\"About\" /> \
2711 static void set_job_menu_visible(struct gui *ui, int visible)
2715 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2716 gtk_widget_set_sensitive(job, visible);
2719 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2722 GtkActionGroup *action_group;
2725 action_group = gtk_action_group_new("Menu");
2726 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2728 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2729 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2731 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2733 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2736 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2737 GtkWidget *vbox, GtkUIManager *ui_manager)
2739 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2742 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2744 struct gui_entry *ge = (struct gui_entry *) data;
2747 index = gtk_combo_box_get_active(box);
2749 multitext_set_entry(&ge->eta.iotype, index);
2750 multitext_set_entry(&ge->eta.ioengine, index);
2751 multitext_set_entry(&ge->eta.iodepth, index);
2754 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2756 struct gui_entry *ge = (struct gui_entry *) data;
2758 multitext_free(&ge->eta.iotype);
2759 multitext_free(&ge->eta.ioengine);
2760 multitext_free(&ge->eta.iodepth);
2763 static GtkWidget *new_client_page(struct gui_entry *ge)
2765 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2766 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2768 main_vbox = gtk_vbox_new(FALSE, 3);
2770 top_align = gtk_alignment_new(0, 0, 1, 0);
2771 top_vbox = gtk_vbox_new(FALSE, 3);
2772 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2773 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2775 probe = gtk_frame_new("Job");
2776 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2777 probe_frame = gtk_vbox_new(FALSE, 3);
2778 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2780 probe_box = gtk_hbox_new(FALSE, 3);
2781 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2782 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2783 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2784 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2785 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2787 probe_box = gtk_hbox_new(FALSE, 3);
2788 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2790 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2791 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2792 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2793 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2794 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2795 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2796 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2797 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2799 probe_box = gtk_hbox_new(FALSE, 3);
2800 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2801 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2802 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2803 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2804 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2807 * Only add this if we have a commit rate
2810 probe_box = gtk_hbox_new(FALSE, 3);
2811 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2813 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2814 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2816 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2817 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2821 * Set up a drawing area and IOPS and bandwidth graphs
2823 ge->graphs.drawing_area = gtk_drawing_area_new();
2824 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2825 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2826 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2827 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2828 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2829 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2830 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2831 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2832 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2833 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2834 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2835 ge->graphs.drawing_area);
2836 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2838 setup_graphs(&ge->graphs);
2841 * Set up alignments for widgets at the bottom of ui,
2842 * align bottom left, expand horizontally but not vertically
2844 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2845 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2846 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2847 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2849 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2852 * Set up thread status progress bar
2854 ge->thread_status_pb = gtk_progress_bar_new();
2855 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2856 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2857 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2863 static GtkWidget *new_main_page(struct gui *ui)
2865 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2866 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2868 main_vbox = gtk_vbox_new(FALSE, 3);
2871 * Set up alignments for widgets at the top of ui,
2872 * align top left, expand horizontally but not vertically
2874 top_align = gtk_alignment_new(0, 0, 1, 0);
2875 top_vbox = gtk_vbox_new(FALSE, 0);
2876 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2877 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2879 probe = gtk_frame_new("Run statistics");
2880 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2881 probe_frame = gtk_vbox_new(FALSE, 3);
2882 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2884 probe_box = gtk_hbox_new(FALSE, 3);
2885 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2886 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2887 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2888 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2889 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2890 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2893 * Only add this if we have a commit rate
2896 probe_box = gtk_hbox_new(FALSE, 3);
2897 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2899 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2900 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2902 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2903 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2907 * Set up a drawing area and IOPS and bandwidth graphs
2909 ui->graphs.drawing_area = gtk_drawing_area_new();
2910 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2911 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2912 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2913 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2914 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2915 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2916 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2917 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2918 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2919 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2920 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2921 ui->graphs.drawing_area);
2922 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
2925 setup_graphs(&ui->graphs);
2928 * Set up alignments for widgets at the bottom of ui,
2929 * align bottom left, expand horizontally but not vertically
2931 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2932 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2933 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2934 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2937 * Set up thread status progress bar
2939 ui->thread_status_pb = gtk_progress_bar_new();
2940 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2941 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2942 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2947 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2948 guint page, gpointer data)
2951 struct gui *ui = (struct gui *) data;
2952 struct gui_entry *ge;
2955 set_job_menu_visible(ui, 0);
2959 set_job_menu_visible(ui, 1);
2960 ge = get_ge_from_page(page, NULL);
2962 update_button_states(ui, ge);
2967 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
2969 time_t time_a = gtk_recent_info_get_visited(a);
2970 time_t time_b = gtk_recent_info_get_visited(b);
2972 return time_b - time_a;
2975 static void add_recent_file_items(struct gui *ui)
2977 const gchar *gfio = g_get_application_name();
2978 GList *items, *item;
2981 if (ui->recent_ui_id) {
2982 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
2983 gtk_ui_manager_ensure_update(ui->uimanager);
2985 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
2987 if (ui->actiongroup) {
2988 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
2989 g_object_unref(ui->actiongroup);
2991 ui->actiongroup = gtk_action_group_new("RecentFileActions");
2993 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
2995 items = gtk_recent_manager_get_items(ui->recentmanager);
2996 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
2998 for (item = items; item && item->data; item = g_list_next(item)) {
2999 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3004 if (!gtk_recent_info_has_application(info, gfio))
3008 * We only support local files for now
3010 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3013 action_name = g_strdup_printf("RecentFile%u", i++);
3014 label = gtk_recent_info_get_display_name(info);
3016 action = g_object_new(GTK_TYPE_ACTION,
3017 "name", action_name,
3018 "label", label, NULL);
3020 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3021 gtk_recent_info_ref(info),
3022 (GDestroyNotify) gtk_recent_info_unref);
3025 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3027 gtk_action_group_add_action(ui->actiongroup, action);
3028 g_object_unref(action);
3030 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3031 "/MainMenu/FileMenu/FileRecentFiles",
3033 GTK_UI_MANAGER_MENUITEM, FALSE);
3035 g_free(action_name);
3041 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3045 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3046 gint x, gint y, GtkSelectionData *data,
3047 guint info, guint time)
3049 struct gui *ui = &main_ui;
3054 source = gtk_drag_get_source_widget(ctx);
3055 if (source && widget == gtk_widget_get_toplevel(source)) {
3056 gtk_drag_finish(ctx, FALSE, FALSE, time);
3060 uris = gtk_selection_data_get_uris(data);
3062 gtk_drag_finish(ctx, FALSE, FALSE, time);
3068 if (do_file_open_with_tab(ui, uris[i]))
3073 gtk_drag_finish(ctx, TRUE, FALSE, time);
3077 static void init_ui(int *argc, char **argv[], struct gui *ui)
3079 GtkSettings *settings;
3082 /* Magical g*thread incantation, you just need this thread stuff.
3083 * Without it, the update that happens in gfio_update_thread_status
3084 * doesn't really happen in a timely fashion, you need expose events
3086 if (!g_thread_supported())
3087 g_thread_init(NULL);
3090 gtk_init(argc, argv);
3091 settings = gtk_settings_get_default();
3092 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3094 gdk_color_parse("white", &white);
3096 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3097 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3098 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3100 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3101 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3103 ui->vbox = gtk_vbox_new(FALSE, 0);
3104 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3106 ui->uimanager = gtk_ui_manager_new();
3107 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3108 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3110 ui->recentmanager = gtk_recent_manager_get_default();
3111 add_recent_file_items(ui);
3113 ui->notebook = gtk_notebook_new();
3114 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3115 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3116 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3117 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3119 vbox = new_main_page(ui);
3120 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3121 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3122 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3124 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3126 gfio_ui_setup_log(ui);
3128 gtk_widget_show_all(ui->window);
3131 int main(int argc, char *argv[], char *envp[])
3133 if (initialize_fio(envp))
3135 if (fio_init_options())
3138 memset(&main_ui, 0, sizeof(main_ui));
3139 INIT_FLIST_HEAD(&main_ui.list);
3141 init_ui(&argc, &argv, &main_ui);
3143 gdk_threads_enter();
3145 gdk_threads_leave();