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[2];
56 const int start_sensitive;
57 } buttonspeclist[] = {
58 #define CONNECT_BUTTON 0
60 #define START_JOB_BUTTON 2
61 { "Connect", connect_clicked, { "Disconnect from host", "Connect to host" }, 1 },
62 { "Send", send_clicked, { "Send job description to host", NULL }, 0 },
63 { "Start Job", start_job_clicked,
64 { "Start the current job on the server", NULL }, 0 },
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_window;
161 GtkWidget *results_notebook;
162 GtkUIManager *results_uimanager;
163 GtkWidget *results_menu;
164 GtkWidget *disk_util_vbox;
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 graph *clat_graph;
176 struct graph *lat_bucket_graph;
178 struct gfio_client *client;
184 struct group_run_stats gs;
185 struct thread_stat ts;
189 struct gui_entry *ge;
190 struct fio_client *client;
191 GtkWidget *err_entry;
192 struct thread_options o;
194 struct end_results *results;
195 unsigned int nr_results;
197 struct cmd_du_pdu *du;
201 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
202 static void gfio_update_thread_status_all(char *status_message, double perc);
203 void report_error(GError *error);
205 static struct graph *setup_iops_graph(void)
209 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
210 graph_title(g, "IOPS (IOs/sec)");
211 graph_x_title(g, "Time (secs)");
212 graph_add_label(g, "Read IOPS");
213 graph_add_label(g, "Write IOPS");
214 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
215 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
216 line_graph_set_data_count_limit(g, gfio_graph_limit);
217 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
221 static struct graph *setup_bandwidth_graph(void)
225 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
226 graph_title(g, "Bandwidth (bytes/sec)");
227 graph_x_title(g, "Time (secs)");
228 graph_add_label(g, "Read Bandwidth");
229 graph_add_label(g, "Write Bandwidth");
230 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
231 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
232 graph_set_base_offset(g, 1);
233 line_graph_set_data_count_limit(g, 100);
234 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
238 static void setup_graphs(struct gfio_graphs *g)
240 g->iops_graph = setup_iops_graph();
241 g->bandwidth_graph = setup_bandwidth_graph();
244 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
246 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
247 mt->text[mt->max_text] = strdup(text);
251 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
253 if (index >= mt->max_text)
255 if (!mt->text || !mt->text[index])
258 mt->cur_text = index;
259 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
262 static void multitext_update_entry(struct multitext_widget *mt,
263 unsigned int index, const char *text)
269 free(mt->text[index]);
271 mt->text[index] = strdup(text);
272 if (mt->cur_text == index)
273 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
276 static void multitext_free(struct multitext_widget *mt)
280 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
282 for (i = 0; i < mt->max_text; i++) {
292 static void clear_ge_ui_info(struct gui_entry *ge)
294 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
295 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
296 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
297 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
299 /* should we empty it... */
300 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
302 multitext_update_entry(&ge->eta.iotype, 0, "");
303 multitext_update_entry(&ge->eta.ioengine, 0, "");
304 multitext_update_entry(&ge->eta.iodepth, 0, "");
305 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
306 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
309 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
310 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
313 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
315 GtkWidget *entry, *frame;
317 frame = gtk_frame_new(label);
318 entry = gtk_combo_box_new_text();
319 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
320 gtk_container_add(GTK_CONTAINER(frame), entry);
325 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
327 GtkWidget *entry, *frame;
329 frame = gtk_frame_new(label);
330 entry = gtk_entry_new();
331 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
332 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
333 gtk_container_add(GTK_CONTAINER(frame), entry);
338 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
340 GtkWidget *label_widget;
343 frame = gtk_frame_new(label);
344 label_widget = gtk_label_new(NULL);
345 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
346 gtk_container_add(GTK_CONTAINER(frame), label_widget);
351 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
353 GtkWidget *button, *box;
355 box = gtk_hbox_new(FALSE, 3);
356 gtk_container_add(GTK_CONTAINER(hbox), box);
358 button = gtk_spin_button_new_with_range(min, max, 1.0);
359 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
361 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
362 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
367 static void label_set_int_value(GtkWidget *entry, unsigned int val)
371 sprintf(tmp, "%u", val);
372 gtk_label_set_text(GTK_LABEL(entry), tmp);
375 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
379 sprintf(tmp, "%u", val);
380 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
383 static void show_info_dialog(struct gui *ui, const char *title,
386 GtkWidget *dialog, *content, *label;
388 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
389 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
390 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
392 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
393 label = gtk_label_new(message);
394 gtk_container_add(GTK_CONTAINER(content), label);
395 gtk_widget_show_all(dialog);
396 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
397 gtk_dialog_run(GTK_DIALOG(dialog));
398 gtk_widget_destroy(dialog);
401 static void set_menu_entry_text(struct gui *ui, const char *path,
406 w = gtk_ui_manager_get_widget(ui->uimanager, path);
408 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
410 fprintf(stderr, "gfio: can't find path %s\n", path);
414 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
418 w = gtk_ui_manager_get_widget(ui->uimanager, path);
420 gtk_widget_set_sensitive(w, show);
422 fprintf(stderr, "gfio: can't find path %s\n", path);
425 static void set_job_menu_visible(struct gui *ui, int visible)
427 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
430 static void set_view_results_visible(struct gui *ui, int visible)
432 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
435 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
437 if (s->tooltiptext[sensitive])
438 return s->tooltiptext[sensitive];
440 return s->tooltiptext[0];
443 static GtkWidget *add_button(GtkWidget *buttonbox,
444 struct button_spec *buttonspec, gpointer data)
446 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
447 gboolean sens = buttonspec->start_sensitive;
449 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
450 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
452 sens = buttonspec->start_sensitive;
453 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
454 gtk_widget_set_sensitive(button, sens);
459 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
464 for (i = 0; i < nbuttons; i++)
465 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
469 * Update sensitivity of job buttons and job menu items, based on the
470 * state of the client.
472 static void update_button_states(struct gui *ui, struct gui_entry *ge)
474 unsigned int connect_state, send_state, start_state, edit_state;
475 const char *connect_str = NULL;
481 sprintf(tmp, "Bad client state: %u\n", ge->state);
482 show_info_dialog(ui, "Error", tmp);
483 /* fall through to new state */
489 connect_str = "Connect";
493 case GE_STATE_CONNECTED:
496 connect_str = "Disconnect";
500 case GE_STATE_JOB_SENT:
503 connect_str = "Disconnect";
507 case GE_STATE_JOB_STARTED:
510 connect_str = "Disconnect";
514 case GE_STATE_JOB_RUNNING:
517 connect_str = "Disconnect";
521 case GE_STATE_JOB_DONE:
524 connect_str = "Connect";
530 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
531 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
532 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
533 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
534 gtk_widget_set_tooltip_text(ge->button[CONNECT_BUTTON], get_button_tooltip(&buttonspeclist[CONNECT_BUTTON], connect_state));
536 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
537 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
539 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
540 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
541 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
543 if (ge->client && ge->client->nr_results)
544 set_view_results_visible(ui, 1);
546 set_view_results_visible(ui, 0);
549 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
552 update_button_states(ge->ui, ge);
556 #define ALIGN_RIGHT 2
560 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
562 GtkCellRenderer *renderer;
563 GtkTreeViewColumn *col;
564 double xalign = 0.0; /* left as default */
565 PangoAlignment align;
568 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
569 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
571 visible = !(flags & INVISIBLE);
573 renderer = gtk_cell_renderer_text_new();
574 col = gtk_tree_view_column_new();
576 gtk_tree_view_column_set_title(col, title);
577 if (!(flags & UNSORTABLE))
578 gtk_tree_view_column_set_sort_column_id(col, index);
579 gtk_tree_view_column_set_resizable(col, TRUE);
580 gtk_tree_view_column_pack_start(col, renderer, TRUE);
581 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
582 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
584 case PANGO_ALIGN_LEFT:
587 case PANGO_ALIGN_CENTER:
590 case PANGO_ALIGN_RIGHT:
594 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
595 gtk_tree_view_column_set_visible(col, visible);
596 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
600 static void gfio_ui_setup_log(struct gui *ui)
602 GtkTreeSelection *selection;
604 GtkWidget *tree_view;
606 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
608 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
609 gtk_widget_set_can_focus(tree_view, FALSE);
611 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
612 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
613 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
614 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
616 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
617 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
618 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
619 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
621 ui->log_model = model;
622 ui->log_tree = tree_view;
625 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
628 double xdim, double ydim)
633 g = graph_new(xdim, ydim, gfio_graph_font);
634 graph_title(g, title);
635 graph_x_title(g, "Percentile");
637 for (i = 0; i < len; i++) {
640 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
641 graph_add_label(g, fbuf);
642 graph_add_data(g, fbuf, (double) ovals[i]);
648 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
654 GType types[FIO_IO_U_LIST_MAX_LEN];
655 GtkWidget *tree_view;
656 GtkTreeSelection *selection;
661 for (i = 0; i < len; i++)
662 types[i] = G_TYPE_INT;
664 model = gtk_list_store_newv(len, types);
666 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
667 gtk_widget_set_can_focus(tree_view, FALSE);
669 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
670 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
672 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
673 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
675 for (i = 0; i < len; i++) {
678 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
679 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
682 gtk_list_store_append(model, &iter);
684 for (i = 0; i < len; i++) {
686 ovals[i] = (ovals[i] + 999) / 1000;
687 gtk_list_store_set(model, &iter, i, ovals[i], -1);
693 static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
698 cr = gdk_cairo_create(w->window);
700 if (graph_has_tooltips(g)) {
701 g_object_set(w, "has-tooltip", TRUE, NULL);
702 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
705 cairo_set_source_rgb(cr, 0, 0, 0);
706 bar_graph_draw(g, cr);
712 static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
715 struct graph *g = data;
717 graph_set_size(g, w->allocation.width, w->allocation.height);
718 graph_set_size(g, w->allocation.width, w->allocation.height);
719 graph_set_position(g, 0, 0);
723 static void gfio_show_clat_percentiles(struct gfio_client *gc,
724 GtkWidget *vbox, struct thread_stat *ts,
727 unsigned int *io_u_plat = ts->io_u_plat[ddir];
728 unsigned long nr = ts->clat_stat[ddir].samples;
729 fio_fp64_t *plist = ts->percentile_list;
730 unsigned int *ovals, len, minv, maxv, scale_down;
732 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
733 struct gui_entry *ge = gc->ge;
736 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
741 * We default to usecs, but if the value range is such that we
742 * should scale down to msecs, do that.
744 if (minv > 2000 && maxv > 99999) {
752 sprintf(tmp, "Completion percentiles (%s)", base);
753 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
754 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
756 frame = gtk_frame_new(tmp);
757 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
759 completion_vbox = gtk_vbox_new(FALSE, 3);
760 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
761 hbox = gtk_hbox_new(FALSE, 3);
762 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
763 drawing_area = gtk_drawing_area_new();
764 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
765 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
766 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
767 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
768 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
770 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
776 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
777 unsigned long max, double mean, double dev)
779 const char *base = "(usec)";
780 GtkWidget *hbox, *label, *frame;
784 if (!usec_to_msec(&min, &max, &mean, &dev))
787 minp = num2str(min, 6, 1, 0);
788 maxp = num2str(max, 6, 1, 0);
790 sprintf(tmp, "%s %s", name, base);
791 frame = gtk_frame_new(tmp);
792 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
794 hbox = gtk_hbox_new(FALSE, 3);
795 gtk_container_add(GTK_CONTAINER(frame), hbox);
797 label = new_info_label_in_frame(hbox, "Minimum");
798 gtk_label_set_text(GTK_LABEL(label), minp);
799 label = new_info_label_in_frame(hbox, "Maximum");
800 gtk_label_set_text(GTK_LABEL(label), maxp);
801 label = new_info_label_in_frame(hbox, "Average");
802 sprintf(tmp, "%5.02f", mean);
803 gtk_label_set_text(GTK_LABEL(label), tmp);
804 label = new_info_label_in_frame(hbox, "Standard deviation");
805 sprintf(tmp, "%5.02f", dev);
806 gtk_label_set_text(GTK_LABEL(label), tmp);
817 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
818 struct group_run_stats *rs,
819 struct thread_stat *ts, int ddir)
821 const char *ddir_label[2] = { "Read", "Write" };
822 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
823 unsigned long min[3], max[3], runt;
824 unsigned long long bw, iops;
825 unsigned int flags = 0;
826 double mean[3], dev[3];
827 char *io_p, *bw_p, *iops_p;
830 if (!ts->runtime[ddir])
833 i2p = is_power_of_2(rs->kb_base);
834 runt = ts->runtime[ddir];
836 bw = (1000 * ts->io_bytes[ddir]) / runt;
837 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
838 bw_p = num2str(bw, 6, 1, i2p);
840 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
841 iops_p = num2str(iops, 6, 1, 0);
843 box = gtk_hbox_new(FALSE, 3);
844 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
846 frame = gtk_frame_new(ddir_label[ddir]);
847 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
849 main_vbox = gtk_vbox_new(FALSE, 3);
850 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
852 box = gtk_hbox_new(FALSE, 3);
853 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
855 label = new_info_label_in_frame(box, "IO");
856 gtk_label_set_text(GTK_LABEL(label), io_p);
857 label = new_info_label_in_frame(box, "Bandwidth");
858 gtk_label_set_text(GTK_LABEL(label), bw_p);
859 label = new_info_label_in_frame(box, "IOPS");
860 gtk_label_set_text(GTK_LABEL(label), iops_p);
861 label = new_info_label_in_frame(box, "Runtime (msec)");
862 label_set_int_value(label, ts->runtime[ddir]);
864 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
865 double p_of_agg = 100.0;
866 const char *bw_str = "KB";
870 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
871 if (p_of_agg > 100.0)
875 if (mean[0] > 999999.9) {
883 sprintf(tmp, "Bandwidth (%s)", bw_str);
884 frame = gtk_frame_new(tmp);
885 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
887 box = gtk_hbox_new(FALSE, 3);
888 gtk_container_add(GTK_CONTAINER(frame), box);
890 label = new_info_label_in_frame(box, "Minimum");
891 label_set_int_value(label, min[0]);
892 label = new_info_label_in_frame(box, "Maximum");
893 label_set_int_value(label, max[0]);
894 label = new_info_label_in_frame(box, "Percentage of jobs");
895 sprintf(tmp, "%3.2f%%", p_of_agg);
896 gtk_label_set_text(GTK_LABEL(label), tmp);
897 label = new_info_label_in_frame(box, "Average");
898 sprintf(tmp, "%5.02f", mean[0]);
899 gtk_label_set_text(GTK_LABEL(label), tmp);
900 label = new_info_label_in_frame(box, "Standard deviation");
901 sprintf(tmp, "%5.02f", dev[0]);
902 gtk_label_set_text(GTK_LABEL(label), tmp);
905 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
907 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
909 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
913 frame = gtk_frame_new("Latency");
914 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
916 vbox = gtk_vbox_new(FALSE, 3);
917 gtk_container_add(GTK_CONTAINER(frame), vbox);
919 if (flags & GFIO_SLAT)
920 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
921 if (flags & GFIO_CLAT)
922 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
923 if (flags & GFIO_LAT)
924 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
927 if (ts->clat_percentiles)
928 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
935 static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
938 double xdim, double ydim)
943 g = graph_new(xdim, ydim, gfio_graph_font);
944 graph_title(g, title);
945 graph_x_title(g, "Buckets");
947 for (i = 0; i < len; i++) {
948 graph_add_label(g, labels[i]);
949 graph_add_data(g, labels[i], lat[i]);
955 static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
958 GtkWidget *tree_view;
959 GtkTreeSelection *selection;
965 types = malloc(num * sizeof(GType));
967 for (i = 0; i < num; i++)
968 types[i] = G_TYPE_STRING;
970 model = gtk_list_store_newv(num, types);
974 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
975 gtk_widget_set_can_focus(tree_view, FALSE);
977 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
978 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
980 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
981 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
983 for (i = 0; i < num; i++)
984 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
986 gtk_list_store_append(model, &iter);
988 for (i = 0; i < num; i++) {
992 sprintf(fbuf, "0.00");
994 sprintf(fbuf, "%3.2f%%", lat[i]);
996 gtk_list_store_set(model, &iter, i, fbuf, -1);
1002 static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
1003 struct thread_stat *ts)
1005 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
1006 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
1007 "250u", "500u", "750u", "1m", "2m",
1008 "4m", "10m", "20m", "50m", "100m",
1009 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
1011 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
1012 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
1013 struct gui_entry *ge = gc->ge;
1015 stat_calc_lat_u(ts, io_u_lat);
1016 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
1019 * Found out which first bucket has entries, and which last bucket
1022 for (i = 0; i < total; i++) {
1023 if (io_u_lat[i] == 0.00)
1037 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1038 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
1040 frame = gtk_frame_new("Latency buckets");
1041 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1043 completion_vbox = gtk_vbox_new(FALSE, 3);
1044 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1045 hbox = gtk_hbox_new(FALSE, 3);
1046 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1048 drawing_area = gtk_drawing_area_new();
1049 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1050 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1051 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1052 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1053 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1055 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
1058 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1060 GtkWidget *box, *frame, *entry;
1061 double usr_cpu, sys_cpu;
1062 unsigned long runtime;
1065 runtime = ts->total_run_time;
1067 double runt = (double) runtime;
1069 usr_cpu = (double) ts->usr_time * 100 / runt;
1070 sys_cpu = (double) ts->sys_time * 100 / runt;
1076 frame = gtk_frame_new("OS resources");
1077 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1079 box = gtk_hbox_new(FALSE, 3);
1080 gtk_container_add(GTK_CONTAINER(frame), box);
1082 entry = new_info_entry_in_frame(box, "User CPU");
1083 sprintf(tmp, "%3.2f%%", usr_cpu);
1084 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1085 entry = new_info_entry_in_frame(box, "System CPU");
1086 sprintf(tmp, "%3.2f%%", sys_cpu);
1087 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1088 entry = new_info_entry_in_frame(box, "Context switches");
1089 entry_set_int_value(entry, ts->ctx);
1090 entry = new_info_entry_in_frame(box, "Major faults");
1091 entry_set_int_value(entry, ts->majf);
1092 entry = new_info_entry_in_frame(box, "Minor faults");
1093 entry_set_int_value(entry, ts->minf);
1095 static void gfio_add_sc_depths_tree(GtkListStore *model,
1096 struct thread_stat *ts, unsigned int len,
1099 double io_u_dist[FIO_IO_U_MAP_NR];
1101 /* Bits 0, and 3-8 */
1102 const int add_mask = 0x1f9;
1106 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1108 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1110 gtk_list_store_append(model, &iter);
1112 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1114 for (i = 1, j = 0; i < len; i++) {
1117 if (!(add_mask & (1UL << (i - 1))))
1118 sprintf(fbuf, "0.0%%");
1120 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1124 gtk_list_store_set(model, &iter, i, fbuf, -1);
1129 static void gfio_add_total_depths_tree(GtkListStore *model,
1130 struct thread_stat *ts, unsigned int len)
1132 double io_u_dist[FIO_IO_U_MAP_NR];
1134 /* Bits 1-6, and 8 */
1135 const int add_mask = 0x17e;
1138 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1140 gtk_list_store_append(model, &iter);
1142 gtk_list_store_set(model, &iter, 0, "Total", -1);
1144 for (i = 1, j = 0; i < len; i++) {
1147 if (!(add_mask & (1UL << (i - 1))))
1148 sprintf(fbuf, "0.0%%");
1150 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1154 gtk_list_store_set(model, &iter, i, fbuf, -1);
1159 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1161 GtkWidget *frame, *box, *tree_view;
1162 GtkTreeSelection *selection;
1163 GtkListStore *model;
1164 GType types[FIO_IO_U_MAP_NR + 1];
1166 #define NR_LABELS 10
1167 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1169 frame = gtk_frame_new("IO depths");
1170 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1172 box = gtk_hbox_new(FALSE, 3);
1173 gtk_container_add(GTK_CONTAINER(frame), box);
1175 for (i = 0; i < NR_LABELS; i++)
1176 types[i] = G_TYPE_STRING;
1178 model = gtk_list_store_newv(NR_LABELS, types);
1180 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1181 gtk_widget_set_can_focus(tree_view, FALSE);
1183 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1184 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1186 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1187 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1189 for (i = 0; i < NR_LABELS; i++)
1190 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1192 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1193 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1194 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1196 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1199 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1201 struct gui_entry *ge = (struct gui_entry *) data;
1203 gtk_widget_destroy(w);
1204 ge->results_window = NULL;
1205 ge->results_notebook = NULL;
1209 static void results_close(GtkWidget *w, gpointer *data)
1211 struct gui_entry *ge = (struct gui_entry *) data;
1213 gtk_widget_destroy(ge->results_window);
1216 static GtkActionEntry results_menu_items[] = {
1217 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1218 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1219 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1221 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1223 static const gchar *results_ui_string = " \
1225 <menubar name=\"MainMenu\"> \
1226 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1227 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1229 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1235 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1237 GtkActionGroup *action_group;
1241 ge->results_uimanager = gtk_ui_manager_new();
1243 action_group = gtk_action_group_new("ResultsMenu");
1244 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1246 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1247 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1249 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1251 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1255 static GtkWidget *get_results_window(struct gui_entry *ge)
1257 GtkWidget *win, *notebook, *vbox;
1259 if (ge->results_window)
1260 return ge->results_notebook;
1262 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1263 gtk_window_set_title(GTK_WINDOW(win), "Results");
1264 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1265 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1266 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1268 vbox = gtk_vbox_new(FALSE, 0);
1269 gtk_container_add(GTK_CONTAINER(win), vbox);
1271 ge->results_menu = get_results_menubar(win, ge);
1272 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1274 notebook = gtk_notebook_new();
1275 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1276 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1277 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1279 ge->results_window = win;
1280 ge->results_notebook = notebook;
1281 return ge->results_notebook;
1284 static void disk_util_destroy(GtkWidget *w, gpointer data)
1286 struct gui_entry *ge = (struct gui_entry *) data;
1288 ge->disk_util_vbox = NULL;
1289 gtk_widget_destroy(w);
1292 static int __gfio_disk_util_show(GtkWidget *res_notebook,
1293 struct gfio_client *gc, struct cmd_du_pdu *p)
1295 GtkWidget *box, *frame, *entry, *vbox;
1296 struct gui_entry *ge = gc->ge;
1300 res_notebook = get_results_window(ge);
1302 if (!ge->disk_util_vbox) {
1303 vbox = gtk_vbox_new(FALSE, 3);
1304 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), vbox, gtk_label_new("Disk utilization"));
1305 ge->disk_util_vbox = vbox;
1306 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1309 vbox = gtk_vbox_new(FALSE, 3);
1310 gtk_container_add(GTK_CONTAINER(ge->disk_util_vbox), vbox);
1312 frame = gtk_frame_new((char *) p->dus.name);
1313 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1315 box = gtk_vbox_new(FALSE, 3);
1316 gtk_container_add(GTK_CONTAINER(frame), box);
1318 frame = gtk_frame_new("Read");
1319 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1320 vbox = gtk_hbox_new(TRUE, 3);
1321 gtk_container_add(GTK_CONTAINER(frame), vbox);
1322 entry = new_info_entry_in_frame(vbox, "IOs");
1323 entry_set_int_value(entry, p->dus.ios[0]);
1324 entry = new_info_entry_in_frame(vbox, "Merges");
1325 entry_set_int_value(entry, p->dus.merges[0]);
1326 entry = new_info_entry_in_frame(vbox, "Sectors");
1327 entry_set_int_value(entry, p->dus.sectors[0]);
1328 entry = new_info_entry_in_frame(vbox, "Ticks");
1329 entry_set_int_value(entry, p->dus.ticks[0]);
1331 frame = gtk_frame_new("Write");
1332 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1333 vbox = gtk_hbox_new(TRUE, 3);
1334 gtk_container_add(GTK_CONTAINER(frame), vbox);
1335 entry = new_info_entry_in_frame(vbox, "IOs");
1336 entry_set_int_value(entry, p->dus.ios[1]);
1337 entry = new_info_entry_in_frame(vbox, "Merges");
1338 entry_set_int_value(entry, p->dus.merges[1]);
1339 entry = new_info_entry_in_frame(vbox, "Sectors");
1340 entry_set_int_value(entry, p->dus.sectors[1]);
1341 entry = new_info_entry_in_frame(vbox, "Ticks");
1342 entry_set_int_value(entry, p->dus.ticks[1]);
1344 frame = gtk_frame_new("Shared");
1345 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1346 vbox = gtk_hbox_new(TRUE, 3);
1347 gtk_container_add(GTK_CONTAINER(frame), vbox);
1348 entry = new_info_entry_in_frame(vbox, "IO ticks");
1349 entry_set_int_value(entry, p->dus.io_ticks);
1350 entry = new_info_entry_in_frame(vbox, "Time in queue");
1351 entry_set_int_value(entry, p->dus.time_in_queue);
1355 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1359 sprintf(tmp, "%3.2f%%", util);
1360 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1361 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1363 gtk_widget_show_all(ge->results_window);
1367 static int gfio_disk_util_show(struct gfio_client *gc)
1369 struct gui_entry *ge = gc->ge;
1370 GtkWidget *res_notebook;
1376 res_notebook = get_results_window(ge);
1378 for (i = 0; i < gc->nr_du; i++) {
1379 struct cmd_du_pdu *p = &gc->du[i];
1381 __gfio_disk_util_show(res_notebook, gc, p);
1384 gtk_widget_show_all(ge->results_window);
1388 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1389 struct group_run_stats *rs)
1391 unsigned int nr = gc->nr_results;
1393 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1394 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1395 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1399 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1400 struct thread_stat *ts,
1401 struct group_run_stats *rs)
1403 GtkWidget *box, *vbox, *entry, *scroll;
1405 scroll = gtk_scrolled_window_new(NULL, NULL);
1406 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1407 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1409 vbox = gtk_vbox_new(FALSE, 3);
1411 box = gtk_hbox_new(FALSE, 0);
1412 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1414 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1416 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1418 entry = new_info_entry_in_frame(box, "Name");
1419 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1420 if (strlen(ts->description)) {
1421 entry = new_info_entry_in_frame(box, "Description");
1422 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1424 entry = new_info_entry_in_frame(box, "Group ID");
1425 entry_set_int_value(entry, ts->groupid);
1426 entry = new_info_entry_in_frame(box, "Jobs");
1427 entry_set_int_value(entry, ts->members);
1428 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1429 entry_set_int_value(entry, ts->error);
1430 entry = new_info_entry_in_frame(box, "PID");
1431 entry_set_int_value(entry, ts->pid);
1433 if (ts->io_bytes[DDIR_READ])
1434 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1435 if (ts->io_bytes[DDIR_WRITE])
1436 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1438 gfio_show_latency_buckets(gc, vbox, ts);
1439 gfio_show_cpu_usage(vbox, ts);
1440 gfio_show_io_depths(vbox, ts);
1443 static void gfio_display_end_results(struct gfio_client *gc)
1445 struct gui_entry *ge = gc->ge;
1446 GtkWidget *res_notebook;
1449 res_notebook = get_results_window(ge);
1451 for (i = 0; i < gc->nr_results; i++) {
1452 struct end_results *e = &gc->results[i];
1454 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1457 if (gfio_disk_util_show(gc))
1458 gtk_widget_show_all(ge->results_window);
1461 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1462 struct group_run_stats *rs)
1464 struct gfio_client *gc = client->client_data;
1466 gfio_add_end_results(gc, ts, rs);
1468 gdk_threads_enter();
1469 gfio_display_end_results(gc);
1470 gdk_threads_leave();
1473 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1475 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1476 struct gui *ui = &main_ui;
1480 char tmp[64], timebuf[80];
1483 tm = localtime(&sec);
1484 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1485 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1487 gdk_threads_enter();
1489 gtk_list_store_append(ui->log_model, &iter);
1490 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1491 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1492 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1493 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1495 if (p->level == FIO_LOG_ERR)
1496 view_log(NULL, (gpointer) ui);
1498 gdk_threads_leave();
1501 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1503 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1504 struct gfio_client *gc = client->client_data;
1505 unsigned int nr = gc->nr_du;
1507 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1508 memcpy(&gc->du[nr], p, sizeof(*p));
1511 gdk_threads_enter();
1512 gfio_disk_util_show(gc);
1513 gdk_threads_leave();
1516 extern int sum_stat_clients;
1517 extern struct thread_stat client_ts;
1518 extern struct group_run_stats client_gs;
1520 static int sum_stat_nr;
1522 static void gfio_thread_status_op(struct fio_client *client,
1523 struct fio_net_cmd *cmd)
1525 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1527 gfio_display_ts(client, &p->ts, &p->rs);
1529 if (sum_stat_clients == 1)
1532 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1533 sum_group_stats(&client_gs, &p->rs);
1535 client_ts.members++;
1536 client_ts.thread_number = p->ts.thread_number;
1537 client_ts.groupid = p->ts.groupid;
1539 if (++sum_stat_nr == sum_stat_clients) {
1540 strcpy(client_ts.name, "All clients");
1541 gfio_display_ts(client, &client_ts, &client_gs);
1545 static void gfio_group_stats_op(struct fio_client *client,
1546 struct fio_net_cmd *cmd)
1548 /* We're ignoring group stats for now */
1551 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1554 struct gfio_graphs *g = data;
1556 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1557 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1558 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1559 graph_set_position(g->bandwidth_graph, 0, 0);
1563 static void draw_graph(struct graph *g, cairo_t *cr)
1565 line_graph_draw(g, cr);
1569 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1570 gboolean keyboard_mode, GtkTooltip *tooltip,
1573 struct gfio_graphs *g = data;
1574 const char *text = NULL;
1576 if (graph_contains_xy(g->iops_graph, x, y))
1577 text = graph_find_tooltip(g->iops_graph, x, y);
1578 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1579 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1582 gtk_tooltip_set_text(tooltip, text);
1589 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1591 struct gfio_graphs *g = p;
1594 cr = gdk_cairo_create(w->window);
1596 if (graph_has_tooltips(g->iops_graph) ||
1597 graph_has_tooltips(g->bandwidth_graph)) {
1598 g_object_set(w, "has-tooltip", TRUE, NULL);
1599 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1602 cairo_set_source_rgb(cr, 0, 0, 0);
1603 draw_graph(g->iops_graph, cr);
1604 draw_graph(g->bandwidth_graph, cr);
1611 * Client specific ETA
1613 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1615 struct gfio_client *gc = client->client_data;
1616 struct gui_entry *ge = gc->ge;
1617 static int eta_good;
1624 gdk_threads_enter();
1629 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1630 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1631 eta_to_str(eta_str, je->eta_sec);
1634 sprintf(tmp, "%u", je->nr_running);
1635 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1636 sprintf(tmp, "%u", je->files_open);
1637 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1640 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1641 if (je->m_rate || je->t_rate) {
1644 mr = num2str(je->m_rate, 4, 0, i2p);
1645 tr = num2str(je->t_rate, 4, 0, i2p);
1646 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1647 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1650 } else if (je->m_iops || je->t_iops)
1651 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1653 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1654 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1655 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1656 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1659 if (je->eta_sec != INT_MAX && je->nr_running) {
1663 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1664 strcpy(output, "-.-% done");
1668 sprintf(output, "%3.1f%% done", perc);
1671 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1672 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1674 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1675 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1677 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1678 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1679 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1680 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1682 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1683 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1684 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1685 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1694 char *dst = output + strlen(output);
1696 sprintf(dst, " - %s", eta_str);
1699 gfio_update_thread_status(ge, output, perc);
1700 gdk_threads_leave();
1704 * Update ETA in main window for all clients
1706 static void gfio_update_all_eta(struct jobs_eta *je)
1708 struct gui *ui = &main_ui;
1709 static int eta_good;
1715 gdk_threads_enter();
1720 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1721 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1722 eta_to_str(eta_str, je->eta_sec);
1726 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1727 if (je->m_rate || je->t_rate) {
1730 mr = num2str(je->m_rate, 4, 0, i2p);
1731 tr = num2str(je->t_rate, 4, 0, i2p);
1732 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1733 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1736 } else if (je->m_iops || je->t_iops)
1737 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1739 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1740 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1741 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1742 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1745 entry_set_int_value(ui->eta.jobs, je->nr_running);
1747 if (je->eta_sec != INT_MAX && je->nr_running) {
1751 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1752 strcpy(output, "-.-% done");
1756 sprintf(output, "%3.1f%% done", perc);
1759 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1760 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1762 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1763 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1765 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1766 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1767 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1768 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1770 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1771 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1772 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1773 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1782 char *dst = output + strlen(output);
1784 sprintf(dst, " - %s", eta_str);
1787 gfio_update_thread_status_all(output, perc);
1788 gdk_threads_leave();
1791 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1793 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1794 struct gfio_client *gc = client->client_data;
1795 struct gui_entry *ge = gc->ge;
1796 const char *os, *arch;
1799 os = fio_get_os_string(probe->os);
1803 arch = fio_get_arch_string(probe->arch);
1808 client->name = strdup((char *) probe->hostname);
1810 gdk_threads_enter();
1812 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1813 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1814 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1815 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1816 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1818 gfio_set_state(ge, GE_STATE_CONNECTED);
1820 gdk_threads_leave();
1823 static void gfio_update_thread_status(struct gui_entry *ge,
1824 char *status_message, double perc)
1826 static char message[100];
1827 const char *m = message;
1829 strncpy(message, status_message, sizeof(message) - 1);
1830 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1831 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1832 gtk_widget_queue_draw(main_ui.window);
1835 static void gfio_update_thread_status_all(char *status_message, double perc)
1837 struct gui *ui = &main_ui;
1838 static char message[100];
1839 const char *m = message;
1841 strncpy(message, status_message, sizeof(message) - 1);
1842 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1843 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1844 gtk_widget_queue_draw(ui->window);
1847 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1849 struct gfio_client *gc = client->client_data;
1851 gdk_threads_enter();
1852 gfio_set_state(gc->ge, GE_STATE_NEW);
1853 gdk_threads_leave();
1856 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1858 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1859 struct gfio_client *gc = client->client_data;
1860 struct thread_options *o = &gc->o;
1861 struct gui_entry *ge = gc->ge;
1864 p->thread_number = le32_to_cpu(p->thread_number);
1865 p->groupid = le32_to_cpu(p->groupid);
1866 convert_thread_options_to_cpu(o, &p->top);
1868 gdk_threads_enter();
1870 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1872 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1873 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1875 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1876 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1878 sprintf(tmp, "%u", o->iodepth);
1879 multitext_add_entry(&ge->eta.iodepth, tmp);
1881 multitext_set_entry(&ge->eta.iotype, 0);
1882 multitext_set_entry(&ge->eta.ioengine, 0);
1883 multitext_set_entry(&ge->eta.iodepth, 0);
1885 gfio_set_state(ge, GE_STATE_JOB_SENT);
1887 gdk_threads_leave();
1890 static void gfio_client_timed_out(struct fio_client *client)
1892 struct gfio_client *gc = client->client_data;
1895 gdk_threads_enter();
1897 gfio_set_state(gc->ge, GE_STATE_NEW);
1898 clear_ge_ui_info(gc->ge);
1900 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1901 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1903 gdk_threads_leave();
1906 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1908 struct gfio_client *gc = client->client_data;
1910 gdk_threads_enter();
1912 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1915 entry_set_int_value(gc->err_entry, client->error);
1917 gdk_threads_leave();
1920 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1922 struct gfio_client *gc = client->client_data;
1924 gdk_threads_enter();
1925 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1926 gdk_threads_leave();
1929 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1931 struct gfio_client *gc = client->client_data;
1933 gdk_threads_enter();
1934 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1935 gdk_threads_leave();
1938 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1940 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1944 struct client_ops gfio_client_ops = {
1945 .text = gfio_text_op,
1946 .disk_util = gfio_disk_util_op,
1947 .thread_status = gfio_thread_status_op,
1948 .group_stats = gfio_group_stats_op,
1949 .jobs_eta = gfio_update_client_eta,
1950 .eta = gfio_update_all_eta,
1951 .probe = gfio_probe_op,
1952 .quit = gfio_quit_op,
1953 .add_job = gfio_add_job_op,
1954 .timed_out = gfio_client_timed_out,
1955 .stop = gfio_client_stop,
1956 .start = gfio_client_start,
1957 .job_start = gfio_client_job_start,
1958 .iolog = gfio_client_iolog,
1959 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1960 .stay_connected = 1,
1961 .client_type = FIO_CLIENT_TYPE_GUI,
1965 * FIXME: need more handling here
1967 static void ge_destroy(struct gui_entry *ge)
1969 struct gfio_client *gc = ge->client;
1971 if (gc && gc->client) {
1972 if (ge->state >= GE_STATE_CONNECTED)
1973 fio_client_terminate(gc->client);
1975 fio_put_client(gc->client);
1978 flist_del(&ge->list);
1982 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1986 static void gfio_quit(struct gui *ui)
1988 struct gui_entry *ge;
1990 while (!flist_empty(&ui->list)) {
1991 ge = flist_entry(ui->list.next, struct gui_entry, list);
1998 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1999 __attribute__((unused)) gpointer data)
2004 static void *job_thread(void *arg)
2006 struct gui *ui = arg;
2008 ui->handler_running = 1;
2009 fio_handle_clients(&gfio_client_ops);
2010 ui->handler_running = 0;
2014 static int send_job_files(struct gui_entry *ge)
2016 struct gfio_client *gc = ge->client;
2019 for (i = 0; i < ge->nr_job_files; i++) {
2020 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
2024 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
2025 report_error(error);
2026 g_error_free(error);
2031 free(ge->job_files[i]);
2032 ge->job_files[i] = NULL;
2034 while (i < ge->nr_job_files) {
2035 free(ge->job_files[i]);
2036 ge->job_files[i] = NULL;
2040 free(ge->job_files);
2041 ge->job_files = NULL;
2042 ge->nr_job_files = 0;
2046 static void *server_thread(void *arg)
2049 gfio_server_running = 1;
2050 fio_start_server(NULL);
2051 gfio_server_running = 0;
2055 static void gfio_start_server(void)
2057 struct gui *ui = &main_ui;
2059 if (!gfio_server_running) {
2060 gfio_server_running = 1;
2061 pthread_create(&ui->server_t, NULL, server_thread, NULL);
2062 pthread_detach(ui->server_t);
2066 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2069 struct gui_entry *ge = data;
2070 struct gfio_client *gc = ge->client;
2073 fio_start_client(gc->client);
2076 static void file_open(GtkWidget *w, gpointer data);
2078 static void connect_clicked(GtkWidget *widget, gpointer data)
2080 struct gui_entry *ge = data;
2081 struct gfio_client *gc = ge->client;
2083 if (ge->state == GE_STATE_NEW) {
2086 if (!ge->nr_job_files)
2087 file_open(widget, ge->ui);
2088 if (!ge->nr_job_files)
2093 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2094 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2095 ret = fio_client_connect(gc->client);
2097 if (!ge->ui->handler_running)
2098 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2099 gfio_set_state(ge, GE_STATE_CONNECTED);
2103 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2104 report_error(error);
2105 g_error_free(error);
2108 fio_client_terminate(gc->client);
2109 gfio_set_state(ge, GE_STATE_NEW);
2110 clear_ge_ui_info(ge);
2114 static void send_clicked(GtkWidget *widget, gpointer data)
2116 struct gui_entry *ge = data;
2118 if (send_job_files(ge)) {
2121 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);
2122 report_error(error);
2123 g_error_free(error);
2125 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2129 static void on_info_bar_response(GtkWidget *widget, gint response,
2132 struct gui *ui = &main_ui;
2134 if (response == GTK_RESPONSE_OK) {
2135 gtk_widget_destroy(widget);
2136 ui->error_info_bar = NULL;
2140 void report_error(GError *error)
2142 struct gui *ui = &main_ui;
2144 if (ui->error_info_bar == NULL) {
2145 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2148 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2149 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2152 ui->error_label = gtk_label_new(error->message);
2153 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2154 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2156 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2157 gtk_widget_show_all(ui->vbox);
2160 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2161 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2165 struct connection_widgets
2172 static void hostname_cb(GtkEntry *entry, gpointer data)
2174 struct connection_widgets *cw = data;
2175 int uses_net = 0, is_localhost = 0;
2180 * Check whether to display the 'auto start backend' box
2181 * or not. Show it if we are a localhost and using network,
2182 * or using a socket.
2184 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2185 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2190 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2191 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2192 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2193 !strcmp(text, "ip6-loopback"))
2197 if (!uses_net || is_localhost) {
2198 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2199 gtk_widget_set_sensitive(cw->button, 1);
2201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2202 gtk_widget_set_sensitive(cw->button, 0);
2206 static int get_connection_details(char **host, int *port, int *type,
2209 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2210 struct connection_widgets cw;
2213 dialog = gtk_dialog_new_with_buttons("Connection details",
2214 GTK_WINDOW(main_ui.window),
2215 GTK_DIALOG_DESTROY_WITH_PARENT,
2216 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2217 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2219 frame = gtk_frame_new("Hostname / socket name");
2220 /* gtk_dialog_get_content_area() is 2.14 and newer */
2221 vbox = GTK_DIALOG(dialog)->vbox;
2222 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2224 box = gtk_vbox_new(FALSE, 6);
2225 gtk_container_add(GTK_CONTAINER(frame), box);
2227 hbox = gtk_hbox_new(TRUE, 10);
2228 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2229 cw.hentry = gtk_entry_new();
2230 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2231 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2233 frame = gtk_frame_new("Port");
2234 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2235 box = gtk_vbox_new(FALSE, 10);
2236 gtk_container_add(GTK_CONTAINER(frame), box);
2238 hbox = gtk_hbox_new(TRUE, 4);
2239 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2240 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2242 frame = gtk_frame_new("Type");
2243 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2244 box = gtk_vbox_new(FALSE, 10);
2245 gtk_container_add(GTK_CONTAINER(frame), box);
2247 hbox = gtk_hbox_new(TRUE, 4);
2248 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2250 cw.combo = gtk_combo_box_new_text();
2251 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2252 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2253 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2254 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2256 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2258 frame = gtk_frame_new("Options");
2259 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2260 box = gtk_vbox_new(FALSE, 10);
2261 gtk_container_add(GTK_CONTAINER(frame), box);
2263 hbox = gtk_hbox_new(TRUE, 4);
2264 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2266 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2267 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2268 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.");
2269 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2272 * Connect edit signal, so we can show/not-show the auto start button
2274 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2275 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2277 gtk_widget_show_all(dialog);
2279 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2280 gtk_widget_destroy(dialog);
2284 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2285 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2287 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2288 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2289 *type = Fio_client_ipv4;
2290 else if (!strncmp(typeentry, "IPv6", 4))
2291 *type = Fio_client_ipv6;
2293 *type = Fio_client_socket;
2296 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2298 gtk_widget_destroy(dialog);
2302 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2304 struct gfio_client *gc;
2306 gc = malloc(sizeof(*gc));
2307 memset(gc, 0, sizeof(*gc));
2309 gc->client = fio_get_client(client);
2313 client->client_data = gc;
2316 static GtkWidget *new_client_page(struct gui_entry *ge);
2318 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2320 struct gui_entry *ge;
2322 ge = malloc(sizeof(*ge));
2323 memset(ge, 0, sizeof(*ge));
2324 ge->state = GE_STATE_NEW;
2325 INIT_FLIST_HEAD(&ge->list);
2326 flist_add_tail(&ge->list, &ui->list);
2331 static struct gui_entry *get_new_ge_with_tab(const char *name)
2333 struct gui_entry *ge;
2335 ge = alloc_new_gui_entry(&main_ui);
2337 ge->vbox = new_client_page(ge);
2338 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2340 ge->page_label = gtk_label_new(name);
2341 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2343 gtk_widget_show_all(main_ui.window);
2347 static void file_new(GtkWidget *w, gpointer data)
2349 struct gui *ui = (struct gui *) data;
2350 struct gui_entry *ge;
2352 ge = get_new_ge_with_tab("Untitled");
2353 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2357 * Return the 'ge' corresponding to the tab. If the active tab is the
2358 * main tab, open a new tab.
2360 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2362 struct flist_head *entry;
2363 struct gui_entry *ge;
2368 return get_new_ge_with_tab("Untitled");
2374 flist_for_each(entry, &main_ui.list) {
2375 ge = flist_entry(entry, struct gui_entry, list);
2376 if (ge->page_num == cur_page)
2383 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2388 * Main tab is tab 0, so any current page other than 0 holds
2391 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2393 return get_ge_from_page(cur_page, NULL);
2398 static void file_close(GtkWidget *w, gpointer data)
2400 struct gui *ui = (struct gui *) data;
2401 struct gui_entry *ge;
2404 * Can't close the main tab
2406 ge = get_ge_from_cur_tab(ui);
2408 gtk_widget_destroy(ge->vbox);
2412 if (!flist_empty(&ui->list)) {
2413 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2420 static void file_add_recent(struct gui *ui, const gchar *uri)
2424 memset(&grd, 0, sizeof(grd));
2425 grd.display_name = strdup("gfio");
2426 grd.description = strdup("Fio job file");
2427 grd.mime_type = strdup(GFIO_MIME);
2428 grd.app_name = strdup(g_get_application_name());
2429 grd.app_exec = strdup("gfio %f/%u");
2431 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2434 static gchar *get_filename_from_uri(const gchar *uri)
2436 if (strncmp(uri, "file://", 7))
2439 return strdup(uri + 7);
2442 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2445 struct fio_client *client;
2448 filename = get_filename_from_uri(uri);
2450 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2451 ge->job_files[ge->nr_job_files] = strdup(filename);
2454 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2458 error = g_error_new(g_quark_from_string("fio"), 1,
2459 "Failed to add client %s", host);
2460 report_error(error);
2461 g_error_free(error);
2465 gfio_client_added(ge, client);
2466 file_add_recent(ge->ui, uri);
2470 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2472 int port, type, server_start;
2473 struct gui_entry *ge;
2476 int ret, ge_is_new = 0;
2479 * Creates new tab if current tab is the main window, or the
2480 * current tab already has a client.
2482 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2483 ge = get_ge_from_page(cur_page, &ge_is_new);
2485 ge = get_new_ge_with_tab("Untitled");
2489 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2491 if (get_connection_details(&host, &port, &type, &server_start)) {
2493 gtk_widget_destroy(ge->vbox);
2498 ret = do_file_open(ge, uri, host, type, port);
2504 gfio_start_server();
2507 gtk_widget_destroy(ge->vbox);
2513 static void recent_open(GtkAction *action, gpointer data)
2515 struct gui *ui = (struct gui *) data;
2516 GtkRecentInfo *info;
2519 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2520 uri = gtk_recent_info_get_uri(info);
2522 do_file_open_with_tab(ui, uri);
2525 static void file_open(GtkWidget *w, gpointer data)
2527 struct gui *ui = data;
2529 GSList *filenames, *fn_glist;
2530 GtkFileFilter *filter;
2532 dialog = gtk_file_chooser_dialog_new("Open File",
2533 GTK_WINDOW(ui->window),
2534 GTK_FILE_CHOOSER_ACTION_OPEN,
2535 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2536 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2538 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2540 filter = gtk_file_filter_new();
2541 gtk_file_filter_add_pattern(filter, "*.fio");
2542 gtk_file_filter_add_pattern(filter, "*.job");
2543 gtk_file_filter_add_pattern(filter, "*.ini");
2544 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2545 gtk_file_filter_set_name(filter, "Fio job file");
2546 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2548 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2549 gtk_widget_destroy(dialog);
2553 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2555 gtk_widget_destroy(dialog);
2557 filenames = fn_glist;
2558 while (filenames != NULL) {
2559 if (do_file_open_with_tab(ui, filenames->data))
2561 filenames = g_slist_next(filenames);
2564 g_slist_free(fn_glist);
2567 static void file_save(GtkWidget *w, gpointer data)
2569 struct gui *ui = data;
2572 dialog = gtk_file_chooser_dialog_new("Save File",
2573 GTK_WINDOW(ui->window),
2574 GTK_FILE_CHOOSER_ACTION_SAVE,
2575 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2576 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2579 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2580 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2582 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2585 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2586 // save_job_file(filename);
2589 gtk_widget_destroy(dialog);
2592 static void view_log_destroy(GtkWidget *w, gpointer data)
2594 struct gui *ui = (struct gui *) data;
2596 gtk_widget_ref(ui->log_tree);
2597 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2598 gtk_widget_destroy(w);
2599 ui->log_view = NULL;
2602 static void view_log(GtkWidget *w, gpointer data)
2604 GtkWidget *win, *scroll, *vbox, *box;
2605 struct gui *ui = (struct gui *) data;
2610 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2611 gtk_window_set_title(GTK_WINDOW(win), "Log");
2612 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2614 scroll = gtk_scrolled_window_new(NULL, NULL);
2616 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2618 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2620 box = gtk_hbox_new(TRUE, 0);
2621 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2622 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2623 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2625 vbox = gtk_vbox_new(TRUE, 5);
2626 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2628 gtk_container_add(GTK_CONTAINER(win), vbox);
2629 gtk_widget_show_all(win);
2632 static void connect_job_entry(GtkWidget *w, gpointer data)
2634 struct gui *ui = (struct gui *) data;
2635 struct gui_entry *ge;
2637 ge = get_ge_from_cur_tab(ui);
2639 connect_clicked(w, ge);
2642 static void send_job_entry(GtkWidget *w, gpointer data)
2644 struct gui *ui = (struct gui *) data;
2645 struct gui_entry *ge;
2647 ge = get_ge_from_cur_tab(ui);
2649 send_clicked(w, ge);
2653 static void edit_job_entry(GtkWidget *w, gpointer data)
2657 static void start_job_entry(GtkWidget *w, gpointer data)
2659 struct gui *ui = (struct gui *) data;
2660 struct gui_entry *ge;
2662 ge = get_ge_from_cur_tab(ui);
2664 start_job_clicked(w, ge);
2667 static void view_results(GtkWidget *w, gpointer data)
2669 struct gui *ui = (struct gui *) data;
2670 struct gfio_client *gc;
2671 struct gui_entry *ge;
2673 ge = get_ge_from_cur_tab(ui);
2677 if (ge->results_window)
2681 if (gc && gc->nr_results)
2682 gfio_display_end_results(gc);
2685 static void __update_graph_limits(struct gfio_graphs *g)
2687 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2688 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2691 static void update_graph_limits(void)
2693 struct flist_head *entry;
2694 struct gui_entry *ge;
2696 __update_graph_limits(&main_ui.graphs);
2698 flist_for_each(entry, &main_ui.list) {
2699 ge = flist_entry(entry, struct gui_entry, list);
2700 __update_graph_limits(&ge->graphs);
2704 static void preferences(GtkWidget *w, gpointer data)
2706 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2707 GtkWidget *hbox, *spin, *entry, *spin_int;
2710 dialog = gtk_dialog_new_with_buttons("Preferences",
2711 GTK_WINDOW(main_ui.window),
2712 GTK_DIALOG_DESTROY_WITH_PARENT,
2713 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2714 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2717 frame = gtk_frame_new("Graphing");
2718 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2719 vbox = gtk_vbox_new(FALSE, 6);
2720 gtk_container_add(GTK_CONTAINER(frame), vbox);
2722 hbox = gtk_hbox_new(FALSE, 5);
2723 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2724 entry = gtk_label_new("Font face to use for graph labels");
2725 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2727 font = gtk_font_button_new();
2728 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2730 box = gtk_vbox_new(FALSE, 6);
2731 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2733 hbox = gtk_hbox_new(FALSE, 5);
2734 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2735 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2736 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2738 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2740 box = gtk_vbox_new(FALSE, 6);
2741 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2743 hbox = gtk_hbox_new(FALSE, 5);
2744 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2745 entry = gtk_label_new("Client ETA request interval (msec)");
2746 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2748 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2749 frame = gtk_frame_new("Debug logging");
2750 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2751 vbox = gtk_vbox_new(FALSE, 6);
2752 gtk_container_add(GTK_CONTAINER(frame), vbox);
2754 box = gtk_hbox_new(FALSE, 6);
2755 gtk_container_add(GTK_CONTAINER(vbox), box);
2757 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2759 for (i = 0; i < FD_DEBUG_MAX; i++) {
2761 box = gtk_hbox_new(FALSE, 6);
2762 gtk_container_add(GTK_CONTAINER(vbox), box);
2766 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2767 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2768 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2771 gtk_widget_show_all(dialog);
2773 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2774 gtk_widget_destroy(dialog);
2778 for (i = 0; i < FD_DEBUG_MAX; i++) {
2781 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2783 fio_debug |= (1UL << i);
2786 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2787 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2788 update_graph_limits();
2789 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2791 gtk_widget_destroy(dialog);
2794 static void about_dialog(GtkWidget *w, gpointer data)
2796 const char *authors[] = {
2797 "Jens Axboe <axboe@kernel.dk>",
2798 "Stephen Carmeron <stephenmcameron@gmail.com>",
2801 const char *license[] = {
2802 "Fio is free software; you can redistribute it and/or modify "
2803 "it under the terms of the GNU General Public License as published by "
2804 "the Free Software Foundation; either version 2 of the License, or "
2805 "(at your option) any later version.\n",
2806 "Fio is distributed in the hope that it will be useful, "
2807 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2808 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2809 "GNU General Public License for more details.\n",
2810 "You should have received a copy of the GNU General Public License "
2811 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2812 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2814 char *license_trans;
2816 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2817 license[2], "\n", NULL);
2819 gtk_show_about_dialog(NULL,
2820 "program-name", "gfio",
2821 "comments", "Gtk2 UI for fio",
2822 "license", license_trans,
2823 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2825 "version", fio_version_string,
2826 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2827 "logo-icon-name", "fio",
2829 "wrap-license", TRUE,
2832 g_free(license_trans);
2835 static GtkActionEntry menu_items[] = {
2836 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2837 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2838 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2839 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2840 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2841 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2842 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2843 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2844 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2845 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2846 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2847 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2848 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2849 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2850 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2851 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2852 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2854 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2856 static const gchar *ui_string = " \
2858 <menubar name=\"MainMenu\"> \
2859 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2860 <menuitem name=\"New\" action=\"NewFile\" /> \
2861 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2862 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2863 <separator name=\"Separator1\"/> \
2864 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2865 <separator name=\"Separator2\"/> \
2866 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2867 <separator name=\"Separator3\"/> \
2868 <placeholder name=\"FileRecentFiles\"/> \
2869 <separator name=\"Separator4\"/> \
2870 <menuitem name=\"Quit\" action=\"Quit\" /> \
2872 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2873 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2874 <separator name=\"Separator5\"/> \
2875 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2876 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2877 <separator name=\"Separator6\"/> \
2878 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2880 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2881 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2882 <separator name=\"Separator7\"/> \
2883 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2885 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2886 <menuitem name=\"About\" action=\"About\" /> \
2892 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2895 GtkActionGroup *action_group;
2898 action_group = gtk_action_group_new("Menu");
2899 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2901 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2902 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2904 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2906 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2909 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2910 GtkWidget *vbox, GtkUIManager *ui_manager)
2912 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2915 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2917 struct gui_entry *ge = (struct gui_entry *) data;
2920 index = gtk_combo_box_get_active(box);
2922 multitext_set_entry(&ge->eta.iotype, index);
2923 multitext_set_entry(&ge->eta.ioengine, index);
2924 multitext_set_entry(&ge->eta.iodepth, index);
2927 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2929 struct gui_entry *ge = (struct gui_entry *) data;
2931 multitext_free(&ge->eta.iotype);
2932 multitext_free(&ge->eta.ioengine);
2933 multitext_free(&ge->eta.iodepth);
2936 static GtkWidget *new_client_page(struct gui_entry *ge)
2938 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2939 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2941 main_vbox = gtk_vbox_new(FALSE, 3);
2943 top_align = gtk_alignment_new(0, 0, 1, 0);
2944 top_vbox = gtk_vbox_new(FALSE, 3);
2945 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2946 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2948 probe = gtk_frame_new("Job");
2949 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2950 probe_frame = gtk_vbox_new(FALSE, 3);
2951 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2953 probe_box = gtk_hbox_new(FALSE, 3);
2954 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2955 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2956 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2957 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2958 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2960 probe_box = gtk_hbox_new(FALSE, 3);
2961 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2963 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2964 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2965 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2966 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2967 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2968 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2969 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2970 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2972 probe_box = gtk_hbox_new(FALSE, 3);
2973 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2974 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2975 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2976 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2977 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2980 * Only add this if we have a commit rate
2983 probe_box = gtk_hbox_new(FALSE, 3);
2984 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2986 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2987 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2989 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2990 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2994 * Set up a drawing area and IOPS and bandwidth graphs
2996 ge->graphs.drawing_area = gtk_drawing_area_new();
2997 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2998 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2999 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3000 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3001 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3002 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3003 G_CALLBACK(on_config_drawing_area), &ge->graphs);
3004 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3005 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3006 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3007 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3008 ge->graphs.drawing_area);
3009 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
3011 setup_graphs(&ge->graphs);
3014 * Set up alignments for widgets at the bottom of ui,
3015 * align bottom left, expand horizontally but not vertically
3017 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3018 ge->buttonbox = gtk_hbox_new(FALSE, 0);
3019 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3020 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3022 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3025 * Set up thread status progress bar
3027 ge->thread_status_pb = gtk_progress_bar_new();
3028 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3029 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3030 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3036 static GtkWidget *new_main_page(struct gui *ui)
3038 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
3039 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
3041 main_vbox = gtk_vbox_new(FALSE, 3);
3044 * Set up alignments for widgets at the top of ui,
3045 * align top left, expand horizontally but not vertically
3047 top_align = gtk_alignment_new(0, 0, 1, 0);
3048 top_vbox = gtk_vbox_new(FALSE, 0);
3049 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3050 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3052 probe = gtk_frame_new("Run statistics");
3053 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3054 probe_frame = gtk_vbox_new(FALSE, 3);
3055 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3057 probe_box = gtk_hbox_new(FALSE, 3);
3058 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3059 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3060 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3061 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3062 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3063 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3066 * Only add this if we have a commit rate
3069 probe_box = gtk_hbox_new(FALSE, 3);
3070 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3072 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3073 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3075 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3076 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3080 * Set up a drawing area and IOPS and bandwidth graphs
3082 ui->graphs.drawing_area = gtk_drawing_area_new();
3083 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3084 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3085 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3086 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3087 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3088 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3089 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3090 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3091 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3092 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3093 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3094 ui->graphs.drawing_area);
3095 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3098 setup_graphs(&ui->graphs);
3101 * Set up alignments for widgets at the bottom of ui,
3102 * align bottom left, expand horizontally but not vertically
3104 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3105 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3106 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3107 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3110 * Set up thread status progress bar
3112 ui->thread_status_pb = gtk_progress_bar_new();
3113 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3114 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3115 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3120 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3121 guint page, gpointer data)
3124 struct gui *ui = (struct gui *) data;
3125 struct gui_entry *ge;
3128 set_job_menu_visible(ui, 0);
3129 set_view_results_visible(ui, 0);
3133 set_job_menu_visible(ui, 1);
3134 ge = get_ge_from_page(page, NULL);
3136 update_button_states(ui, ge);
3141 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3143 time_t time_a = gtk_recent_info_get_visited(a);
3144 time_t time_b = gtk_recent_info_get_visited(b);
3146 return time_b - time_a;
3149 static void add_recent_file_items(struct gui *ui)
3151 const gchar *gfio = g_get_application_name();
3152 GList *items, *item;
3155 if (ui->recent_ui_id) {
3156 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3157 gtk_ui_manager_ensure_update(ui->uimanager);
3159 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3161 if (ui->actiongroup) {
3162 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3163 g_object_unref(ui->actiongroup);
3165 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3167 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3169 items = gtk_recent_manager_get_items(ui->recentmanager);
3170 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3172 for (item = items; item && item->data; item = g_list_next(item)) {
3173 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3178 if (!gtk_recent_info_has_application(info, gfio))
3182 * We only support local files for now
3184 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3187 action_name = g_strdup_printf("RecentFile%u", i++);
3188 label = gtk_recent_info_get_display_name(info);
3190 action = g_object_new(GTK_TYPE_ACTION,
3191 "name", action_name,
3192 "label", label, NULL);
3194 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3195 gtk_recent_info_ref(info),
3196 (GDestroyNotify) gtk_recent_info_unref);
3199 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3201 gtk_action_group_add_action(ui->actiongroup, action);
3202 g_object_unref(action);
3204 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3205 "/MainMenu/FileMenu/FileRecentFiles",
3207 GTK_UI_MANAGER_MENUITEM, FALSE);
3209 g_free(action_name);
3215 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3219 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3220 gint x, gint y, GtkSelectionData *data,
3221 guint info, guint time)
3223 struct gui *ui = &main_ui;
3228 source = gtk_drag_get_source_widget(ctx);
3229 if (source && widget == gtk_widget_get_toplevel(source)) {
3230 gtk_drag_finish(ctx, FALSE, FALSE, time);
3234 uris = gtk_selection_data_get_uris(data);
3236 gtk_drag_finish(ctx, FALSE, FALSE, time);
3242 if (do_file_open_with_tab(ui, uris[i]))
3247 gtk_drag_finish(ctx, TRUE, FALSE, time);
3251 static void init_ui(int *argc, char **argv[], struct gui *ui)
3253 GtkSettings *settings;
3256 /* Magical g*thread incantation, you just need this thread stuff.
3257 * Without it, the update that happens in gfio_update_thread_status
3258 * doesn't really happen in a timely fashion, you need expose events
3260 if (!g_thread_supported())
3261 g_thread_init(NULL);
3264 gtk_init(argc, argv);
3265 settings = gtk_settings_get_default();
3266 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3268 gdk_color_parse("white", &white);
3270 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3271 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3272 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3274 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3275 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3277 ui->vbox = gtk_vbox_new(FALSE, 0);
3278 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3280 ui->uimanager = gtk_ui_manager_new();
3281 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3282 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3284 ui->recentmanager = gtk_recent_manager_get_default();
3285 add_recent_file_items(ui);
3287 ui->notebook = gtk_notebook_new();
3288 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3289 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3290 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3291 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3293 vbox = new_main_page(ui);
3294 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3295 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3296 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3298 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3300 gfio_ui_setup_log(ui);
3302 gtk_widget_show_all(ui->window);
3305 int main(int argc, char *argv[], char *envp[])
3307 if (initialize_fio(envp))
3309 if (fio_init_options())
3312 memset(&main_ui, 0, sizeof(main_ui));
3313 INIT_FLIST_HEAD(&main_ui.list);
3315 init_ui(&argc, &argv, &main_ui);
3317 gdk_threads_enter();
3319 gdk_threads_leave();