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;
1465 struct gui_entry *ge = gc->ge;
1467 gfio_add_end_results(gc, ts, rs);
1469 gdk_threads_enter();
1470 if (ge->results_window)
1471 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1473 gfio_display_end_results(gc);
1474 gdk_threads_leave();
1477 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1479 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1480 struct gui *ui = &main_ui;
1484 char tmp[64], timebuf[80];
1487 tm = localtime(&sec);
1488 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1489 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1491 gdk_threads_enter();
1493 gtk_list_store_append(ui->log_model, &iter);
1494 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1495 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1496 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1497 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1499 if (p->level == FIO_LOG_ERR)
1500 view_log(NULL, (gpointer) ui);
1502 gdk_threads_leave();
1505 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1507 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1508 struct gfio_client *gc = client->client_data;
1509 struct gui_entry *ge = gc->ge;
1510 unsigned int nr = gc->nr_du;
1512 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1513 memcpy(&gc->du[nr], p, sizeof(*p));
1516 gdk_threads_enter();
1517 if (ge->results_window)
1518 __gfio_disk_util_show(ge->results_notebook, gc, p);
1520 gfio_disk_util_show(gc);
1521 gdk_threads_leave();
1524 extern int sum_stat_clients;
1525 extern struct thread_stat client_ts;
1526 extern struct group_run_stats client_gs;
1528 static int sum_stat_nr;
1530 static void gfio_thread_status_op(struct fio_client *client,
1531 struct fio_net_cmd *cmd)
1533 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1535 gfio_display_ts(client, &p->ts, &p->rs);
1537 if (sum_stat_clients == 1)
1540 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1541 sum_group_stats(&client_gs, &p->rs);
1543 client_ts.members++;
1544 client_ts.thread_number = p->ts.thread_number;
1545 client_ts.groupid = p->ts.groupid;
1547 if (++sum_stat_nr == sum_stat_clients) {
1548 strcpy(client_ts.name, "All clients");
1549 gfio_display_ts(client, &client_ts, &client_gs);
1553 static void gfio_group_stats_op(struct fio_client *client,
1554 struct fio_net_cmd *cmd)
1556 /* We're ignoring group stats for now */
1559 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1562 struct gfio_graphs *g = data;
1564 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1565 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1566 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1567 graph_set_position(g->bandwidth_graph, 0, 0);
1571 static void draw_graph(struct graph *g, cairo_t *cr)
1573 line_graph_draw(g, cr);
1577 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1578 gboolean keyboard_mode, GtkTooltip *tooltip,
1581 struct gfio_graphs *g = data;
1582 const char *text = NULL;
1584 if (graph_contains_xy(g->iops_graph, x, y))
1585 text = graph_find_tooltip(g->iops_graph, x, y);
1586 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1587 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1590 gtk_tooltip_set_text(tooltip, text);
1597 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1599 struct gfio_graphs *g = p;
1602 cr = gdk_cairo_create(w->window);
1604 if (graph_has_tooltips(g->iops_graph) ||
1605 graph_has_tooltips(g->bandwidth_graph)) {
1606 g_object_set(w, "has-tooltip", TRUE, NULL);
1607 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1610 cairo_set_source_rgb(cr, 0, 0, 0);
1611 draw_graph(g->iops_graph, cr);
1612 draw_graph(g->bandwidth_graph, cr);
1619 * Client specific ETA
1621 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1623 struct gfio_client *gc = client->client_data;
1624 struct gui_entry *ge = gc->ge;
1625 static int eta_good;
1632 gdk_threads_enter();
1637 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1638 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1639 eta_to_str(eta_str, je->eta_sec);
1642 sprintf(tmp, "%u", je->nr_running);
1643 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1644 sprintf(tmp, "%u", je->files_open);
1645 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1648 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1649 if (je->m_rate || je->t_rate) {
1652 mr = num2str(je->m_rate, 4, 0, i2p);
1653 tr = num2str(je->t_rate, 4, 0, i2p);
1654 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1655 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1658 } else if (je->m_iops || je->t_iops)
1659 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1661 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1662 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1663 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1664 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1667 if (je->eta_sec != INT_MAX && je->nr_running) {
1671 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1672 strcpy(output, "-.-% done");
1676 sprintf(output, "%3.1f%% done", perc);
1679 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1680 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1682 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1683 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1685 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1686 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1687 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1688 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1690 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1691 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1692 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1693 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1702 char *dst = output + strlen(output);
1704 sprintf(dst, " - %s", eta_str);
1707 gfio_update_thread_status(ge, output, perc);
1708 gdk_threads_leave();
1712 * Update ETA in main window for all clients
1714 static void gfio_update_all_eta(struct jobs_eta *je)
1716 struct gui *ui = &main_ui;
1717 static int eta_good;
1723 gdk_threads_enter();
1728 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1729 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1730 eta_to_str(eta_str, je->eta_sec);
1734 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1735 if (je->m_rate || je->t_rate) {
1738 mr = num2str(je->m_rate, 4, 0, i2p);
1739 tr = num2str(je->t_rate, 4, 0, i2p);
1740 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1741 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1744 } else if (je->m_iops || je->t_iops)
1745 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1747 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1748 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1749 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1750 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1753 entry_set_int_value(ui->eta.jobs, je->nr_running);
1755 if (je->eta_sec != INT_MAX && je->nr_running) {
1759 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1760 strcpy(output, "-.-% done");
1764 sprintf(output, "%3.1f%% done", perc);
1767 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1768 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1770 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1771 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1773 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1774 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1775 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1776 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1778 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1779 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1780 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1781 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1790 char *dst = output + strlen(output);
1792 sprintf(dst, " - %s", eta_str);
1795 gfio_update_thread_status_all(output, perc);
1796 gdk_threads_leave();
1799 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1801 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1802 struct gfio_client *gc = client->client_data;
1803 struct gui_entry *ge = gc->ge;
1804 const char *os, *arch;
1807 os = fio_get_os_string(probe->os);
1811 arch = fio_get_arch_string(probe->arch);
1816 client->name = strdup((char *) probe->hostname);
1818 gdk_threads_enter();
1820 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1821 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1822 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1823 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1824 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1826 gfio_set_state(ge, GE_STATE_CONNECTED);
1828 gdk_threads_leave();
1831 static void gfio_update_thread_status(struct gui_entry *ge,
1832 char *status_message, double perc)
1834 static char message[100];
1835 const char *m = message;
1837 strncpy(message, status_message, sizeof(message) - 1);
1838 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1839 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1840 gtk_widget_queue_draw(main_ui.window);
1843 static void gfio_update_thread_status_all(char *status_message, double perc)
1845 struct gui *ui = &main_ui;
1846 static char message[100];
1847 const char *m = message;
1849 strncpy(message, status_message, sizeof(message) - 1);
1850 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1851 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1852 gtk_widget_queue_draw(ui->window);
1855 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1857 struct gfio_client *gc = client->client_data;
1859 gdk_threads_enter();
1860 gfio_set_state(gc->ge, GE_STATE_NEW);
1861 gdk_threads_leave();
1864 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1866 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1867 struct gfio_client *gc = client->client_data;
1868 struct thread_options *o = &gc->o;
1869 struct gui_entry *ge = gc->ge;
1872 p->thread_number = le32_to_cpu(p->thread_number);
1873 p->groupid = le32_to_cpu(p->groupid);
1874 convert_thread_options_to_cpu(o, &p->top);
1876 gdk_threads_enter();
1878 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1880 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1881 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1883 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1884 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1886 sprintf(tmp, "%u", o->iodepth);
1887 multitext_add_entry(&ge->eta.iodepth, tmp);
1889 multitext_set_entry(&ge->eta.iotype, 0);
1890 multitext_set_entry(&ge->eta.ioengine, 0);
1891 multitext_set_entry(&ge->eta.iodepth, 0);
1893 gfio_set_state(ge, GE_STATE_JOB_SENT);
1895 gdk_threads_leave();
1898 static void gfio_client_timed_out(struct fio_client *client)
1900 struct gfio_client *gc = client->client_data;
1903 gdk_threads_enter();
1905 gfio_set_state(gc->ge, GE_STATE_NEW);
1906 clear_ge_ui_info(gc->ge);
1908 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1909 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1911 gdk_threads_leave();
1914 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1916 struct gfio_client *gc = client->client_data;
1918 gdk_threads_enter();
1920 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1923 entry_set_int_value(gc->err_entry, client->error);
1925 gdk_threads_leave();
1928 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1930 struct gfio_client *gc = client->client_data;
1932 gdk_threads_enter();
1933 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1934 gdk_threads_leave();
1937 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1939 struct gfio_client *gc = client->client_data;
1941 gdk_threads_enter();
1942 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1943 gdk_threads_leave();
1946 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1948 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1952 struct client_ops gfio_client_ops = {
1953 .text = gfio_text_op,
1954 .disk_util = gfio_disk_util_op,
1955 .thread_status = gfio_thread_status_op,
1956 .group_stats = gfio_group_stats_op,
1957 .jobs_eta = gfio_update_client_eta,
1958 .eta = gfio_update_all_eta,
1959 .probe = gfio_probe_op,
1960 .quit = gfio_quit_op,
1961 .add_job = gfio_add_job_op,
1962 .timed_out = gfio_client_timed_out,
1963 .stop = gfio_client_stop,
1964 .start = gfio_client_start,
1965 .job_start = gfio_client_job_start,
1966 .iolog = gfio_client_iolog,
1967 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1968 .stay_connected = 1,
1969 .client_type = FIO_CLIENT_TYPE_GUI,
1973 * FIXME: need more handling here
1975 static void ge_destroy(struct gui_entry *ge)
1977 struct gfio_client *gc = ge->client;
1979 if (gc && gc->client) {
1980 if (ge->state >= GE_STATE_CONNECTED)
1981 fio_client_terminate(gc->client);
1983 fio_put_client(gc->client);
1986 flist_del(&ge->list);
1990 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1994 static void gfio_quit(struct gui *ui)
1996 struct gui_entry *ge;
1998 while (!flist_empty(&ui->list)) {
1999 ge = flist_entry(ui->list.next, struct gui_entry, list);
2006 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
2007 __attribute__((unused)) gpointer data)
2012 static void *job_thread(void *arg)
2014 struct gui *ui = arg;
2016 ui->handler_running = 1;
2017 fio_handle_clients(&gfio_client_ops);
2018 ui->handler_running = 0;
2022 static int send_job_files(struct gui_entry *ge)
2024 struct gfio_client *gc = ge->client;
2027 for (i = 0; i < ge->nr_job_files; i++) {
2028 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
2032 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
2033 report_error(error);
2034 g_error_free(error);
2039 free(ge->job_files[i]);
2040 ge->job_files[i] = NULL;
2042 while (i < ge->nr_job_files) {
2043 free(ge->job_files[i]);
2044 ge->job_files[i] = NULL;
2048 free(ge->job_files);
2049 ge->job_files = NULL;
2050 ge->nr_job_files = 0;
2054 static void *server_thread(void *arg)
2057 gfio_server_running = 1;
2058 fio_start_server(NULL);
2059 gfio_server_running = 0;
2063 static void gfio_start_server(void)
2065 struct gui *ui = &main_ui;
2067 if (!gfio_server_running) {
2068 gfio_server_running = 1;
2069 pthread_create(&ui->server_t, NULL, server_thread, NULL);
2070 pthread_detach(ui->server_t);
2074 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2077 struct gui_entry *ge = data;
2078 struct gfio_client *gc = ge->client;
2081 fio_start_client(gc->client);
2084 static void file_open(GtkWidget *w, gpointer data);
2086 static void connect_clicked(GtkWidget *widget, gpointer data)
2088 struct gui_entry *ge = data;
2089 struct gfio_client *gc = ge->client;
2091 if (ge->state == GE_STATE_NEW) {
2094 if (!ge->nr_job_files)
2095 file_open(widget, ge->ui);
2096 if (!ge->nr_job_files)
2101 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2102 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2103 ret = fio_client_connect(gc->client);
2105 if (!ge->ui->handler_running)
2106 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2107 gfio_set_state(ge, GE_STATE_CONNECTED);
2111 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2112 report_error(error);
2113 g_error_free(error);
2116 fio_client_terminate(gc->client);
2117 gfio_set_state(ge, GE_STATE_NEW);
2118 clear_ge_ui_info(ge);
2122 static void send_clicked(GtkWidget *widget, gpointer data)
2124 struct gui_entry *ge = data;
2126 if (send_job_files(ge)) {
2129 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);
2130 report_error(error);
2131 g_error_free(error);
2133 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2137 static void on_info_bar_response(GtkWidget *widget, gint response,
2140 struct gui *ui = &main_ui;
2142 if (response == GTK_RESPONSE_OK) {
2143 gtk_widget_destroy(widget);
2144 ui->error_info_bar = NULL;
2148 void report_error(GError *error)
2150 struct gui *ui = &main_ui;
2152 if (ui->error_info_bar == NULL) {
2153 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2156 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2157 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2160 ui->error_label = gtk_label_new(error->message);
2161 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2162 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2164 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2165 gtk_widget_show_all(ui->vbox);
2168 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2169 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2173 struct connection_widgets
2180 static void hostname_cb(GtkEntry *entry, gpointer data)
2182 struct connection_widgets *cw = data;
2183 int uses_net = 0, is_localhost = 0;
2188 * Check whether to display the 'auto start backend' box
2189 * or not. Show it if we are a localhost and using network,
2190 * or using a socket.
2192 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2193 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2198 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2199 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2200 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2201 !strcmp(text, "ip6-loopback"))
2205 if (!uses_net || is_localhost) {
2206 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2207 gtk_widget_set_sensitive(cw->button, 1);
2209 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2210 gtk_widget_set_sensitive(cw->button, 0);
2214 static int get_connection_details(char **host, int *port, int *type,
2217 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2218 struct connection_widgets cw;
2221 dialog = gtk_dialog_new_with_buttons("Connection details",
2222 GTK_WINDOW(main_ui.window),
2223 GTK_DIALOG_DESTROY_WITH_PARENT,
2224 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2225 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2227 frame = gtk_frame_new("Hostname / socket name");
2228 /* gtk_dialog_get_content_area() is 2.14 and newer */
2229 vbox = GTK_DIALOG(dialog)->vbox;
2230 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2232 box = gtk_vbox_new(FALSE, 6);
2233 gtk_container_add(GTK_CONTAINER(frame), box);
2235 hbox = gtk_hbox_new(TRUE, 10);
2236 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2237 cw.hentry = gtk_entry_new();
2238 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2239 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2241 frame = gtk_frame_new("Port");
2242 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2243 box = gtk_vbox_new(FALSE, 10);
2244 gtk_container_add(GTK_CONTAINER(frame), box);
2246 hbox = gtk_hbox_new(TRUE, 4);
2247 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2248 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2250 frame = gtk_frame_new("Type");
2251 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2252 box = gtk_vbox_new(FALSE, 10);
2253 gtk_container_add(GTK_CONTAINER(frame), box);
2255 hbox = gtk_hbox_new(TRUE, 4);
2256 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2258 cw.combo = gtk_combo_box_new_text();
2259 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2260 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2261 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2262 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2264 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2266 frame = gtk_frame_new("Options");
2267 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2268 box = gtk_vbox_new(FALSE, 10);
2269 gtk_container_add(GTK_CONTAINER(frame), box);
2271 hbox = gtk_hbox_new(TRUE, 4);
2272 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2274 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2275 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2276 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.");
2277 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2280 * Connect edit signal, so we can show/not-show the auto start button
2282 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2283 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2285 gtk_widget_show_all(dialog);
2287 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2288 gtk_widget_destroy(dialog);
2292 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2293 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2295 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2296 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2297 *type = Fio_client_ipv4;
2298 else if (!strncmp(typeentry, "IPv6", 4))
2299 *type = Fio_client_ipv6;
2301 *type = Fio_client_socket;
2304 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2306 gtk_widget_destroy(dialog);
2310 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2312 struct gfio_client *gc;
2314 gc = malloc(sizeof(*gc));
2315 memset(gc, 0, sizeof(*gc));
2317 gc->client = fio_get_client(client);
2321 client->client_data = gc;
2324 static GtkWidget *new_client_page(struct gui_entry *ge);
2326 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2328 struct gui_entry *ge;
2330 ge = malloc(sizeof(*ge));
2331 memset(ge, 0, sizeof(*ge));
2332 ge->state = GE_STATE_NEW;
2333 INIT_FLIST_HEAD(&ge->list);
2334 flist_add_tail(&ge->list, &ui->list);
2339 static struct gui_entry *get_new_ge_with_tab(const char *name)
2341 struct gui_entry *ge;
2343 ge = alloc_new_gui_entry(&main_ui);
2345 ge->vbox = new_client_page(ge);
2346 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2348 ge->page_label = gtk_label_new(name);
2349 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2351 gtk_widget_show_all(main_ui.window);
2355 static void file_new(GtkWidget *w, gpointer data)
2357 struct gui *ui = (struct gui *) data;
2358 struct gui_entry *ge;
2360 ge = get_new_ge_with_tab("Untitled");
2361 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2365 * Return the 'ge' corresponding to the tab. If the active tab is the
2366 * main tab, open a new tab.
2368 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2370 struct flist_head *entry;
2371 struct gui_entry *ge;
2376 return get_new_ge_with_tab("Untitled");
2382 flist_for_each(entry, &main_ui.list) {
2383 ge = flist_entry(entry, struct gui_entry, list);
2384 if (ge->page_num == cur_page)
2391 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2396 * Main tab is tab 0, so any current page other than 0 holds
2399 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2401 return get_ge_from_page(cur_page, NULL);
2406 static void file_close(GtkWidget *w, gpointer data)
2408 struct gui *ui = (struct gui *) data;
2409 struct gui_entry *ge;
2412 * Can't close the main tab
2414 ge = get_ge_from_cur_tab(ui);
2416 gtk_widget_destroy(ge->vbox);
2420 if (!flist_empty(&ui->list)) {
2421 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2428 static void file_add_recent(struct gui *ui, const gchar *uri)
2432 memset(&grd, 0, sizeof(grd));
2433 grd.display_name = strdup("gfio");
2434 grd.description = strdup("Fio job file");
2435 grd.mime_type = strdup(GFIO_MIME);
2436 grd.app_name = strdup(g_get_application_name());
2437 grd.app_exec = strdup("gfio %f/%u");
2439 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2442 static gchar *get_filename_from_uri(const gchar *uri)
2444 if (strncmp(uri, "file://", 7))
2447 return strdup(uri + 7);
2450 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2453 struct fio_client *client;
2456 filename = get_filename_from_uri(uri);
2458 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2459 ge->job_files[ge->nr_job_files] = strdup(filename);
2462 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2466 error = g_error_new(g_quark_from_string("fio"), 1,
2467 "Failed to add client %s", host);
2468 report_error(error);
2469 g_error_free(error);
2473 gfio_client_added(ge, client);
2474 file_add_recent(ge->ui, uri);
2478 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2480 int port, type, server_start;
2481 struct gui_entry *ge;
2484 int ret, ge_is_new = 0;
2487 * Creates new tab if current tab is the main window, or the
2488 * current tab already has a client.
2490 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2491 ge = get_ge_from_page(cur_page, &ge_is_new);
2493 ge = get_new_ge_with_tab("Untitled");
2497 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2499 if (get_connection_details(&host, &port, &type, &server_start)) {
2501 gtk_widget_destroy(ge->vbox);
2506 ret = do_file_open(ge, uri, host, type, port);
2512 gfio_start_server();
2515 gtk_widget_destroy(ge->vbox);
2521 static void recent_open(GtkAction *action, gpointer data)
2523 struct gui *ui = (struct gui *) data;
2524 GtkRecentInfo *info;
2527 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2528 uri = gtk_recent_info_get_uri(info);
2530 do_file_open_with_tab(ui, uri);
2533 static void file_open(GtkWidget *w, gpointer data)
2535 struct gui *ui = data;
2537 GSList *filenames, *fn_glist;
2538 GtkFileFilter *filter;
2540 dialog = gtk_file_chooser_dialog_new("Open File",
2541 GTK_WINDOW(ui->window),
2542 GTK_FILE_CHOOSER_ACTION_OPEN,
2543 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2544 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2546 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2548 filter = gtk_file_filter_new();
2549 gtk_file_filter_add_pattern(filter, "*.fio");
2550 gtk_file_filter_add_pattern(filter, "*.job");
2551 gtk_file_filter_add_pattern(filter, "*.ini");
2552 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2553 gtk_file_filter_set_name(filter, "Fio job file");
2554 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2556 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2557 gtk_widget_destroy(dialog);
2561 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2563 gtk_widget_destroy(dialog);
2565 filenames = fn_glist;
2566 while (filenames != NULL) {
2567 if (do_file_open_with_tab(ui, filenames->data))
2569 filenames = g_slist_next(filenames);
2572 g_slist_free(fn_glist);
2575 static void file_save(GtkWidget *w, gpointer data)
2577 struct gui *ui = data;
2580 dialog = gtk_file_chooser_dialog_new("Save File",
2581 GTK_WINDOW(ui->window),
2582 GTK_FILE_CHOOSER_ACTION_SAVE,
2583 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2584 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2587 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2588 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2590 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2593 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2594 // save_job_file(filename);
2597 gtk_widget_destroy(dialog);
2600 static void view_log_destroy(GtkWidget *w, gpointer data)
2602 struct gui *ui = (struct gui *) data;
2604 gtk_widget_ref(ui->log_tree);
2605 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2606 gtk_widget_destroy(w);
2607 ui->log_view = NULL;
2610 static void view_log(GtkWidget *w, gpointer data)
2612 GtkWidget *win, *scroll, *vbox, *box;
2613 struct gui *ui = (struct gui *) data;
2618 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2619 gtk_window_set_title(GTK_WINDOW(win), "Log");
2620 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2622 scroll = gtk_scrolled_window_new(NULL, NULL);
2624 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2626 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2628 box = gtk_hbox_new(TRUE, 0);
2629 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2630 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2631 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2633 vbox = gtk_vbox_new(TRUE, 5);
2634 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2636 gtk_container_add(GTK_CONTAINER(win), vbox);
2637 gtk_widget_show_all(win);
2640 static void connect_job_entry(GtkWidget *w, gpointer data)
2642 struct gui *ui = (struct gui *) data;
2643 struct gui_entry *ge;
2645 ge = get_ge_from_cur_tab(ui);
2647 connect_clicked(w, ge);
2650 static void send_job_entry(GtkWidget *w, gpointer data)
2652 struct gui *ui = (struct gui *) data;
2653 struct gui_entry *ge;
2655 ge = get_ge_from_cur_tab(ui);
2657 send_clicked(w, ge);
2661 static void edit_job_entry(GtkWidget *w, gpointer data)
2665 static void start_job_entry(GtkWidget *w, gpointer data)
2667 struct gui *ui = (struct gui *) data;
2668 struct gui_entry *ge;
2670 ge = get_ge_from_cur_tab(ui);
2672 start_job_clicked(w, ge);
2675 static void view_results(GtkWidget *w, gpointer data)
2677 struct gui *ui = (struct gui *) data;
2678 struct gfio_client *gc;
2679 struct gui_entry *ge;
2681 ge = get_ge_from_cur_tab(ui);
2685 if (ge->results_window)
2689 if (gc && gc->nr_results)
2690 gfio_display_end_results(gc);
2693 static void __update_graph_limits(struct gfio_graphs *g)
2695 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2696 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2699 static void update_graph_limits(void)
2701 struct flist_head *entry;
2702 struct gui_entry *ge;
2704 __update_graph_limits(&main_ui.graphs);
2706 flist_for_each(entry, &main_ui.list) {
2707 ge = flist_entry(entry, struct gui_entry, list);
2708 __update_graph_limits(&ge->graphs);
2712 static void preferences(GtkWidget *w, gpointer data)
2714 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2715 GtkWidget *hbox, *spin, *entry, *spin_int;
2718 dialog = gtk_dialog_new_with_buttons("Preferences",
2719 GTK_WINDOW(main_ui.window),
2720 GTK_DIALOG_DESTROY_WITH_PARENT,
2721 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2722 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2725 frame = gtk_frame_new("Graphing");
2726 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2727 vbox = gtk_vbox_new(FALSE, 6);
2728 gtk_container_add(GTK_CONTAINER(frame), vbox);
2730 hbox = gtk_hbox_new(FALSE, 5);
2731 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2732 entry = gtk_label_new("Font face to use for graph labels");
2733 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2735 font = gtk_font_button_new();
2736 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2738 box = gtk_vbox_new(FALSE, 6);
2739 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2741 hbox = gtk_hbox_new(FALSE, 5);
2742 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2743 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2744 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2746 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2748 box = gtk_vbox_new(FALSE, 6);
2749 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2751 hbox = gtk_hbox_new(FALSE, 5);
2752 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2753 entry = gtk_label_new("Client ETA request interval (msec)");
2754 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2756 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2757 frame = gtk_frame_new("Debug logging");
2758 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2759 vbox = gtk_vbox_new(FALSE, 6);
2760 gtk_container_add(GTK_CONTAINER(frame), vbox);
2762 box = gtk_hbox_new(FALSE, 6);
2763 gtk_container_add(GTK_CONTAINER(vbox), box);
2765 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2767 for (i = 0; i < FD_DEBUG_MAX; i++) {
2769 box = gtk_hbox_new(FALSE, 6);
2770 gtk_container_add(GTK_CONTAINER(vbox), box);
2774 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2775 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2776 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2779 gtk_widget_show_all(dialog);
2781 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2782 gtk_widget_destroy(dialog);
2786 for (i = 0; i < FD_DEBUG_MAX; i++) {
2789 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2791 fio_debug |= (1UL << i);
2794 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2795 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2796 update_graph_limits();
2797 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2799 gtk_widget_destroy(dialog);
2802 static void about_dialog(GtkWidget *w, gpointer data)
2804 const char *authors[] = {
2805 "Jens Axboe <axboe@kernel.dk>",
2806 "Stephen Carmeron <stephenmcameron@gmail.com>",
2809 const char *license[] = {
2810 "Fio is free software; you can redistribute it and/or modify "
2811 "it under the terms of the GNU General Public License as published by "
2812 "the Free Software Foundation; either version 2 of the License, or "
2813 "(at your option) any later version.\n",
2814 "Fio is distributed in the hope that it will be useful, "
2815 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2816 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2817 "GNU General Public License for more details.\n",
2818 "You should have received a copy of the GNU General Public License "
2819 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2820 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2822 char *license_trans;
2824 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2825 license[2], "\n", NULL);
2827 gtk_show_about_dialog(NULL,
2828 "program-name", "gfio",
2829 "comments", "Gtk2 UI for fio",
2830 "license", license_trans,
2831 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2833 "version", fio_version_string,
2834 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2835 "logo-icon-name", "fio",
2837 "wrap-license", TRUE,
2840 g_free(license_trans);
2843 static GtkActionEntry menu_items[] = {
2844 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2845 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2846 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2847 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2848 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2849 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2850 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2851 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2852 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2853 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2854 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2855 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2856 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2857 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2858 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2859 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2860 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2862 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2864 static const gchar *ui_string = " \
2866 <menubar name=\"MainMenu\"> \
2867 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2868 <menuitem name=\"New\" action=\"NewFile\" /> \
2869 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2870 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2871 <separator name=\"Separator1\"/> \
2872 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2873 <separator name=\"Separator2\"/> \
2874 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2875 <separator name=\"Separator3\"/> \
2876 <placeholder name=\"FileRecentFiles\"/> \
2877 <separator name=\"Separator4\"/> \
2878 <menuitem name=\"Quit\" action=\"Quit\" /> \
2880 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2881 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2882 <separator name=\"Separator5\"/> \
2883 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2884 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2885 <separator name=\"Separator6\"/> \
2886 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2888 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2889 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2890 <separator name=\"Separator7\"/> \
2891 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2893 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2894 <menuitem name=\"About\" action=\"About\" /> \
2900 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2903 GtkActionGroup *action_group;
2906 action_group = gtk_action_group_new("Menu");
2907 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2909 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2910 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2912 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2914 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2917 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2918 GtkWidget *vbox, GtkUIManager *ui_manager)
2920 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2923 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2925 struct gui_entry *ge = (struct gui_entry *) data;
2928 index = gtk_combo_box_get_active(box);
2930 multitext_set_entry(&ge->eta.iotype, index);
2931 multitext_set_entry(&ge->eta.ioengine, index);
2932 multitext_set_entry(&ge->eta.iodepth, index);
2935 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2937 struct gui_entry *ge = (struct gui_entry *) data;
2939 multitext_free(&ge->eta.iotype);
2940 multitext_free(&ge->eta.ioengine);
2941 multitext_free(&ge->eta.iodepth);
2944 static GtkWidget *new_client_page(struct gui_entry *ge)
2946 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2947 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2949 main_vbox = gtk_vbox_new(FALSE, 3);
2951 top_align = gtk_alignment_new(0, 0, 1, 0);
2952 top_vbox = gtk_vbox_new(FALSE, 3);
2953 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2954 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2956 probe = gtk_frame_new("Job");
2957 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2958 probe_frame = gtk_vbox_new(FALSE, 3);
2959 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2961 probe_box = gtk_hbox_new(FALSE, 3);
2962 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2963 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2964 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2965 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2966 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2968 probe_box = gtk_hbox_new(FALSE, 3);
2969 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2971 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2972 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2973 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2974 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2975 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2976 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2977 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2978 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2980 probe_box = gtk_hbox_new(FALSE, 3);
2981 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2982 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2983 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2984 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2985 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2988 * Only add this if we have a commit rate
2991 probe_box = gtk_hbox_new(FALSE, 3);
2992 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2994 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2995 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2997 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2998 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3002 * Set up a drawing area and IOPS and bandwidth graphs
3004 ge->graphs.drawing_area = gtk_drawing_area_new();
3005 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
3006 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3007 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3008 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3009 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3010 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3011 G_CALLBACK(on_config_drawing_area), &ge->graphs);
3012 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3013 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3014 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3015 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3016 ge->graphs.drawing_area);
3017 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
3019 setup_graphs(&ge->graphs);
3022 * Set up alignments for widgets at the bottom of ui,
3023 * align bottom left, expand horizontally but not vertically
3025 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3026 ge->buttonbox = gtk_hbox_new(FALSE, 0);
3027 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3028 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3030 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3033 * Set up thread status progress bar
3035 ge->thread_status_pb = gtk_progress_bar_new();
3036 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3037 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3038 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3044 static GtkWidget *new_main_page(struct gui *ui)
3046 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
3047 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
3049 main_vbox = gtk_vbox_new(FALSE, 3);
3052 * Set up alignments for widgets at the top of ui,
3053 * align top left, expand horizontally but not vertically
3055 top_align = gtk_alignment_new(0, 0, 1, 0);
3056 top_vbox = gtk_vbox_new(FALSE, 0);
3057 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3058 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3060 probe = gtk_frame_new("Run statistics");
3061 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3062 probe_frame = gtk_vbox_new(FALSE, 3);
3063 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3065 probe_box = gtk_hbox_new(FALSE, 3);
3066 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3067 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3068 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3069 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3070 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3071 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3074 * Only add this if we have a commit rate
3077 probe_box = gtk_hbox_new(FALSE, 3);
3078 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3080 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3081 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3083 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3084 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3088 * Set up a drawing area and IOPS and bandwidth graphs
3090 ui->graphs.drawing_area = gtk_drawing_area_new();
3091 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3092 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3093 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3094 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3095 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3096 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3097 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3098 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3099 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3100 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3101 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3102 ui->graphs.drawing_area);
3103 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3106 setup_graphs(&ui->graphs);
3109 * Set up alignments for widgets at the bottom of ui,
3110 * align bottom left, expand horizontally but not vertically
3112 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3113 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3114 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3115 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3118 * Set up thread status progress bar
3120 ui->thread_status_pb = gtk_progress_bar_new();
3121 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3122 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3123 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3128 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3129 guint page, gpointer data)
3132 struct gui *ui = (struct gui *) data;
3133 struct gui_entry *ge;
3136 set_job_menu_visible(ui, 0);
3137 set_view_results_visible(ui, 0);
3141 set_job_menu_visible(ui, 1);
3142 ge = get_ge_from_page(page, NULL);
3144 update_button_states(ui, ge);
3149 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3151 time_t time_a = gtk_recent_info_get_visited(a);
3152 time_t time_b = gtk_recent_info_get_visited(b);
3154 return time_b - time_a;
3157 static void add_recent_file_items(struct gui *ui)
3159 const gchar *gfio = g_get_application_name();
3160 GList *items, *item;
3163 if (ui->recent_ui_id) {
3164 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3165 gtk_ui_manager_ensure_update(ui->uimanager);
3167 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3169 if (ui->actiongroup) {
3170 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3171 g_object_unref(ui->actiongroup);
3173 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3175 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3177 items = gtk_recent_manager_get_items(ui->recentmanager);
3178 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3180 for (item = items; item && item->data; item = g_list_next(item)) {
3181 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3186 if (!gtk_recent_info_has_application(info, gfio))
3190 * We only support local files for now
3192 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3195 action_name = g_strdup_printf("RecentFile%u", i++);
3196 label = gtk_recent_info_get_display_name(info);
3198 action = g_object_new(GTK_TYPE_ACTION,
3199 "name", action_name,
3200 "label", label, NULL);
3202 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3203 gtk_recent_info_ref(info),
3204 (GDestroyNotify) gtk_recent_info_unref);
3207 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3209 gtk_action_group_add_action(ui->actiongroup, action);
3210 g_object_unref(action);
3212 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3213 "/MainMenu/FileMenu/FileRecentFiles",
3215 GTK_UI_MANAGER_MENUITEM, FALSE);
3217 g_free(action_name);
3223 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3227 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3228 gint x, gint y, GtkSelectionData *data,
3229 guint info, guint time)
3231 struct gui *ui = &main_ui;
3236 source = gtk_drag_get_source_widget(ctx);
3237 if (source && widget == gtk_widget_get_toplevel(source)) {
3238 gtk_drag_finish(ctx, FALSE, FALSE, time);
3242 uris = gtk_selection_data_get_uris(data);
3244 gtk_drag_finish(ctx, FALSE, FALSE, time);
3250 if (do_file_open_with_tab(ui, uris[i]))
3255 gtk_drag_finish(ctx, TRUE, FALSE, time);
3259 static void init_ui(int *argc, char **argv[], struct gui *ui)
3261 GtkSettings *settings;
3264 /* Magical g*thread incantation, you just need this thread stuff.
3265 * Without it, the update that happens in gfio_update_thread_status
3266 * doesn't really happen in a timely fashion, you need expose events
3268 if (!g_thread_supported())
3269 g_thread_init(NULL);
3272 gtk_init(argc, argv);
3273 settings = gtk_settings_get_default();
3274 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3276 gdk_color_parse("white", &white);
3278 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3279 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3280 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3282 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3283 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3285 ui->vbox = gtk_vbox_new(FALSE, 0);
3286 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3288 ui->uimanager = gtk_ui_manager_new();
3289 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3290 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3292 ui->recentmanager = gtk_recent_manager_get_default();
3293 add_recent_file_items(ui);
3295 ui->notebook = gtk_notebook_new();
3296 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3297 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3298 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3299 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3301 vbox = new_main_page(ui);
3302 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3303 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3304 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3306 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3308 gfio_ui_setup_log(ui);
3310 gtk_widget_show_all(ui->window);
3313 int main(int argc, char *argv[], char *envp[])
3315 if (initialize_fio(envp))
3317 if (fio_init_options())
3320 memset(&main_ui, 0, sizeof(main_ui));
3321 INIT_FLIST_HEAD(&main_ui.list);
3323 init_ui(&argc, &argv, &main_ui);
3325 gdk_threads_enter();
3327 gdk_threads_leave();