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 GtkWidget *get_scrolled_window(gint border_width)
1296 scroll = gtk_scrolled_window_new(NULL, NULL);
1297 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1298 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1303 static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1305 GtkWidget *vbox, *box, *scroll, *res_notebook;
1307 if (ge->disk_util_vbox)
1308 return ge->disk_util_vbox;
1310 scroll = get_scrolled_window(5);
1311 vbox = gtk_vbox_new(FALSE, 3);
1312 box = gtk_hbox_new(FALSE, 0);
1313 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1315 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1316 res_notebook = get_results_window(ge);
1318 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1319 ge->disk_util_vbox = box;
1320 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1322 return ge->disk_util_vbox;
1325 static int __gfio_disk_util_show(GtkWidget *res_notebook,
1326 struct gfio_client *gc, struct cmd_du_pdu *p)
1328 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
1329 struct gui_entry *ge = gc->ge;
1333 util_vbox = gfio_disk_util_get_vbox(ge);
1335 vbox = gtk_vbox_new(FALSE, 3);
1336 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
1338 frame = gtk_frame_new((char *) p->dus.name);
1339 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1341 box = gtk_vbox_new(FALSE, 3);
1342 gtk_container_add(GTK_CONTAINER(frame), box);
1344 frame = gtk_frame_new("Read");
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, "IOs");
1349 entry_set_int_value(entry, p->dus.ios[0]);
1350 entry = new_info_entry_in_frame(vbox, "Merges");
1351 entry_set_int_value(entry, p->dus.merges[0]);
1352 entry = new_info_entry_in_frame(vbox, "Sectors");
1353 entry_set_int_value(entry, p->dus.sectors[0]);
1354 entry = new_info_entry_in_frame(vbox, "Ticks");
1355 entry_set_int_value(entry, p->dus.ticks[0]);
1357 frame = gtk_frame_new("Write");
1358 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1359 vbox = gtk_hbox_new(TRUE, 3);
1360 gtk_container_add(GTK_CONTAINER(frame), vbox);
1361 entry = new_info_entry_in_frame(vbox, "IOs");
1362 entry_set_int_value(entry, p->dus.ios[1]);
1363 entry = new_info_entry_in_frame(vbox, "Merges");
1364 entry_set_int_value(entry, p->dus.merges[1]);
1365 entry = new_info_entry_in_frame(vbox, "Sectors");
1366 entry_set_int_value(entry, p->dus.sectors[1]);
1367 entry = new_info_entry_in_frame(vbox, "Ticks");
1368 entry_set_int_value(entry, p->dus.ticks[1]);
1370 frame = gtk_frame_new("Shared");
1371 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1372 vbox = gtk_hbox_new(TRUE, 3);
1373 gtk_container_add(GTK_CONTAINER(frame), vbox);
1374 entry = new_info_entry_in_frame(vbox, "IO ticks");
1375 entry_set_int_value(entry, p->dus.io_ticks);
1376 entry = new_info_entry_in_frame(vbox, "Time in queue");
1377 entry_set_int_value(entry, p->dus.time_in_queue);
1381 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1385 sprintf(tmp, "%3.2f%%", util);
1386 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1387 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1389 gtk_widget_show_all(ge->results_window);
1393 static int gfio_disk_util_show(struct gfio_client *gc)
1395 struct gui_entry *ge = gc->ge;
1396 GtkWidget *res_notebook;
1402 res_notebook = get_results_window(ge);
1404 for (i = 0; i < gc->nr_du; i++) {
1405 struct cmd_du_pdu *p = &gc->du[i];
1407 __gfio_disk_util_show(res_notebook, gc, p);
1410 gtk_widget_show_all(ge->results_window);
1414 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1415 struct group_run_stats *rs)
1417 unsigned int nr = gc->nr_results;
1419 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1420 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1421 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1425 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1426 struct thread_stat *ts,
1427 struct group_run_stats *rs)
1429 GtkWidget *box, *vbox, *entry, *scroll;
1431 scroll = gtk_scrolled_window_new(NULL, NULL);
1432 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1433 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1435 vbox = gtk_vbox_new(FALSE, 3);
1437 box = gtk_hbox_new(FALSE, 0);
1438 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1440 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1442 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1444 entry = new_info_entry_in_frame(box, "Name");
1445 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1446 if (strlen(ts->description)) {
1447 entry = new_info_entry_in_frame(box, "Description");
1448 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1450 entry = new_info_entry_in_frame(box, "Group ID");
1451 entry_set_int_value(entry, ts->groupid);
1452 entry = new_info_entry_in_frame(box, "Jobs");
1453 entry_set_int_value(entry, ts->members);
1454 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1455 entry_set_int_value(entry, ts->error);
1456 entry = new_info_entry_in_frame(box, "PID");
1457 entry_set_int_value(entry, ts->pid);
1459 if (ts->io_bytes[DDIR_READ])
1460 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1461 if (ts->io_bytes[DDIR_WRITE])
1462 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1464 gfio_show_latency_buckets(gc, vbox, ts);
1465 gfio_show_cpu_usage(vbox, ts);
1466 gfio_show_io_depths(vbox, ts);
1469 static void gfio_display_end_results(struct gfio_client *gc)
1471 struct gui_entry *ge = gc->ge;
1472 GtkWidget *res_notebook;
1475 res_notebook = get_results_window(ge);
1477 for (i = 0; i < gc->nr_results; i++) {
1478 struct end_results *e = &gc->results[i];
1480 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1483 if (gfio_disk_util_show(gc))
1484 gtk_widget_show_all(ge->results_window);
1487 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1488 struct group_run_stats *rs)
1490 struct gfio_client *gc = client->client_data;
1491 struct gui_entry *ge = gc->ge;
1493 gfio_add_end_results(gc, ts, rs);
1495 gdk_threads_enter();
1496 if (ge->results_window)
1497 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1499 gfio_display_end_results(gc);
1500 gdk_threads_leave();
1503 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1505 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1506 struct gui *ui = &main_ui;
1510 char tmp[64], timebuf[80];
1513 tm = localtime(&sec);
1514 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1515 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1517 gdk_threads_enter();
1519 gtk_list_store_append(ui->log_model, &iter);
1520 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1521 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1522 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1523 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1525 if (p->level == FIO_LOG_ERR)
1526 view_log(NULL, (gpointer) ui);
1528 gdk_threads_leave();
1531 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1533 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1534 struct gfio_client *gc = client->client_data;
1535 struct gui_entry *ge = gc->ge;
1536 unsigned int nr = gc->nr_du;
1538 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1539 memcpy(&gc->du[nr], p, sizeof(*p));
1542 gdk_threads_enter();
1543 if (ge->results_window)
1544 __gfio_disk_util_show(ge->results_notebook, gc, p);
1546 gfio_disk_util_show(gc);
1547 gdk_threads_leave();
1550 extern int sum_stat_clients;
1551 extern struct thread_stat client_ts;
1552 extern struct group_run_stats client_gs;
1554 static int sum_stat_nr;
1556 static void gfio_thread_status_op(struct fio_client *client,
1557 struct fio_net_cmd *cmd)
1559 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1561 gfio_display_ts(client, &p->ts, &p->rs);
1563 if (sum_stat_clients == 1)
1566 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1567 sum_group_stats(&client_gs, &p->rs);
1569 client_ts.members++;
1570 client_ts.thread_number = p->ts.thread_number;
1571 client_ts.groupid = p->ts.groupid;
1573 if (++sum_stat_nr == sum_stat_clients) {
1574 strcpy(client_ts.name, "All clients");
1575 gfio_display_ts(client, &client_ts, &client_gs);
1579 static void gfio_group_stats_op(struct fio_client *client,
1580 struct fio_net_cmd *cmd)
1582 /* We're ignoring group stats for now */
1585 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1588 struct gfio_graphs *g = data;
1590 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1591 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1592 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1593 graph_set_position(g->bandwidth_graph, 0, 0);
1597 static void draw_graph(struct graph *g, cairo_t *cr)
1599 line_graph_draw(g, cr);
1603 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1604 gboolean keyboard_mode, GtkTooltip *tooltip,
1607 struct gfio_graphs *g = data;
1608 const char *text = NULL;
1610 if (graph_contains_xy(g->iops_graph, x, y))
1611 text = graph_find_tooltip(g->iops_graph, x, y);
1612 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1613 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1616 gtk_tooltip_set_text(tooltip, text);
1623 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1625 struct gfio_graphs *g = p;
1628 cr = gdk_cairo_create(w->window);
1630 if (graph_has_tooltips(g->iops_graph) ||
1631 graph_has_tooltips(g->bandwidth_graph)) {
1632 g_object_set(w, "has-tooltip", TRUE, NULL);
1633 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1636 cairo_set_source_rgb(cr, 0, 0, 0);
1637 draw_graph(g->iops_graph, cr);
1638 draw_graph(g->bandwidth_graph, cr);
1645 * Client specific ETA
1647 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1649 struct gfio_client *gc = client->client_data;
1650 struct gui_entry *ge = gc->ge;
1651 static int eta_good;
1658 gdk_threads_enter();
1663 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1664 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1665 eta_to_str(eta_str, je->eta_sec);
1668 sprintf(tmp, "%u", je->nr_running);
1669 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1670 sprintf(tmp, "%u", je->files_open);
1671 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1674 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1675 if (je->m_rate || je->t_rate) {
1678 mr = num2str(je->m_rate, 4, 0, i2p);
1679 tr = num2str(je->t_rate, 4, 0, i2p);
1680 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1681 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1684 } else if (je->m_iops || je->t_iops)
1685 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1687 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1688 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1689 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1690 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1693 if (je->eta_sec != INT_MAX && je->nr_running) {
1697 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1698 strcpy(output, "-.-% done");
1702 sprintf(output, "%3.1f%% done", perc);
1705 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1706 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1708 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1709 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1711 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1712 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1713 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1714 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1716 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1717 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1718 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1719 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1728 char *dst = output + strlen(output);
1730 sprintf(dst, " - %s", eta_str);
1733 gfio_update_thread_status(ge, output, perc);
1734 gdk_threads_leave();
1738 * Update ETA in main window for all clients
1740 static void gfio_update_all_eta(struct jobs_eta *je)
1742 struct gui *ui = &main_ui;
1743 static int eta_good;
1749 gdk_threads_enter();
1754 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1755 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1756 eta_to_str(eta_str, je->eta_sec);
1760 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1761 if (je->m_rate || je->t_rate) {
1764 mr = num2str(je->m_rate, 4, 0, i2p);
1765 tr = num2str(je->t_rate, 4, 0, i2p);
1766 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1767 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1770 } else if (je->m_iops || je->t_iops)
1771 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1773 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1774 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1775 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1776 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1779 entry_set_int_value(ui->eta.jobs, je->nr_running);
1781 if (je->eta_sec != INT_MAX && je->nr_running) {
1785 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1786 strcpy(output, "-.-% done");
1790 sprintf(output, "%3.1f%% done", perc);
1793 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1794 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1796 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1797 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1799 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1800 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1801 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1802 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1804 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1805 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1806 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1807 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1816 char *dst = output + strlen(output);
1818 sprintf(dst, " - %s", eta_str);
1821 gfio_update_thread_status_all(output, perc);
1822 gdk_threads_leave();
1825 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1827 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1828 struct gfio_client *gc = client->client_data;
1829 struct gui_entry *ge = gc->ge;
1830 const char *os, *arch;
1833 os = fio_get_os_string(probe->os);
1837 arch = fio_get_arch_string(probe->arch);
1842 client->name = strdup((char *) probe->hostname);
1844 gdk_threads_enter();
1846 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1847 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1848 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1849 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1850 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1852 gfio_set_state(ge, GE_STATE_CONNECTED);
1854 gdk_threads_leave();
1857 static void gfio_update_thread_status(struct gui_entry *ge,
1858 char *status_message, double perc)
1860 static char message[100];
1861 const char *m = message;
1863 strncpy(message, status_message, sizeof(message) - 1);
1864 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1865 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1866 gtk_widget_queue_draw(main_ui.window);
1869 static void gfio_update_thread_status_all(char *status_message, double perc)
1871 struct gui *ui = &main_ui;
1872 static char message[100];
1873 const char *m = message;
1875 strncpy(message, status_message, sizeof(message) - 1);
1876 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1877 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1878 gtk_widget_queue_draw(ui->window);
1881 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1883 struct gfio_client *gc = client->client_data;
1885 gdk_threads_enter();
1886 gfio_set_state(gc->ge, GE_STATE_NEW);
1887 gdk_threads_leave();
1890 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1892 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1893 struct gfio_client *gc = client->client_data;
1894 struct thread_options *o = &gc->o;
1895 struct gui_entry *ge = gc->ge;
1898 p->thread_number = le32_to_cpu(p->thread_number);
1899 p->groupid = le32_to_cpu(p->groupid);
1900 convert_thread_options_to_cpu(o, &p->top);
1902 gdk_threads_enter();
1904 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1906 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1907 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1909 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1910 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1912 sprintf(tmp, "%u", o->iodepth);
1913 multitext_add_entry(&ge->eta.iodepth, tmp);
1915 multitext_set_entry(&ge->eta.iotype, 0);
1916 multitext_set_entry(&ge->eta.ioengine, 0);
1917 multitext_set_entry(&ge->eta.iodepth, 0);
1919 gfio_set_state(ge, GE_STATE_JOB_SENT);
1921 gdk_threads_leave();
1924 static void gfio_client_timed_out(struct fio_client *client)
1926 struct gfio_client *gc = client->client_data;
1929 gdk_threads_enter();
1931 gfio_set_state(gc->ge, GE_STATE_NEW);
1932 clear_ge_ui_info(gc->ge);
1934 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1935 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1937 gdk_threads_leave();
1940 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1942 struct gfio_client *gc = client->client_data;
1944 gdk_threads_enter();
1946 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1949 entry_set_int_value(gc->err_entry, client->error);
1951 gdk_threads_leave();
1954 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1956 struct gfio_client *gc = client->client_data;
1958 gdk_threads_enter();
1959 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1960 gdk_threads_leave();
1963 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1965 struct gfio_client *gc = client->client_data;
1967 gdk_threads_enter();
1968 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1969 gdk_threads_leave();
1972 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1974 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1978 struct client_ops gfio_client_ops = {
1979 .text = gfio_text_op,
1980 .disk_util = gfio_disk_util_op,
1981 .thread_status = gfio_thread_status_op,
1982 .group_stats = gfio_group_stats_op,
1983 .jobs_eta = gfio_update_client_eta,
1984 .eta = gfio_update_all_eta,
1985 .probe = gfio_probe_op,
1986 .quit = gfio_quit_op,
1987 .add_job = gfio_add_job_op,
1988 .timed_out = gfio_client_timed_out,
1989 .stop = gfio_client_stop,
1990 .start = gfio_client_start,
1991 .job_start = gfio_client_job_start,
1992 .iolog = gfio_client_iolog,
1993 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1994 .stay_connected = 1,
1995 .client_type = FIO_CLIENT_TYPE_GUI,
1999 * FIXME: need more handling here
2001 static void ge_destroy(struct gui_entry *ge)
2003 struct gfio_client *gc = ge->client;
2005 if (gc && gc->client) {
2006 if (ge->state >= GE_STATE_CONNECTED)
2007 fio_client_terminate(gc->client);
2009 fio_put_client(gc->client);
2012 flist_del(&ge->list);
2016 static void ge_widget_destroy(GtkWidget *w, gpointer data)
2020 static void gfio_quit(struct gui *ui)
2022 struct gui_entry *ge;
2024 while (!flist_empty(&ui->list)) {
2025 ge = flist_entry(ui->list.next, struct gui_entry, list);
2032 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
2033 __attribute__((unused)) gpointer data)
2038 static void *job_thread(void *arg)
2040 struct gui *ui = arg;
2042 ui->handler_running = 1;
2043 fio_handle_clients(&gfio_client_ops);
2044 ui->handler_running = 0;
2048 static int send_job_files(struct gui_entry *ge)
2050 struct gfio_client *gc = ge->client;
2053 for (i = 0; i < ge->nr_job_files; i++) {
2054 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
2058 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
2059 report_error(error);
2060 g_error_free(error);
2065 free(ge->job_files[i]);
2066 ge->job_files[i] = NULL;
2068 while (i < ge->nr_job_files) {
2069 free(ge->job_files[i]);
2070 ge->job_files[i] = NULL;
2074 free(ge->job_files);
2075 ge->job_files = NULL;
2076 ge->nr_job_files = 0;
2080 static void *server_thread(void *arg)
2083 gfio_server_running = 1;
2084 fio_start_server(NULL);
2085 gfio_server_running = 0;
2089 static void gfio_start_server(void)
2091 struct gui *ui = &main_ui;
2093 if (!gfio_server_running) {
2094 gfio_server_running = 1;
2095 pthread_create(&ui->server_t, NULL, server_thread, NULL);
2096 pthread_detach(ui->server_t);
2100 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2103 struct gui_entry *ge = data;
2104 struct gfio_client *gc = ge->client;
2107 fio_start_client(gc->client);
2110 static void file_open(GtkWidget *w, gpointer data);
2112 static void connect_clicked(GtkWidget *widget, gpointer data)
2114 struct gui_entry *ge = data;
2115 struct gfio_client *gc = ge->client;
2117 if (ge->state == GE_STATE_NEW) {
2120 if (!ge->nr_job_files)
2121 file_open(widget, ge->ui);
2122 if (!ge->nr_job_files)
2127 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2128 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2129 ret = fio_client_connect(gc->client);
2131 if (!ge->ui->handler_running)
2132 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2133 gfio_set_state(ge, GE_STATE_CONNECTED);
2137 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2138 report_error(error);
2139 g_error_free(error);
2142 fio_client_terminate(gc->client);
2143 gfio_set_state(ge, GE_STATE_NEW);
2144 clear_ge_ui_info(ge);
2148 static void send_clicked(GtkWidget *widget, gpointer data)
2150 struct gui_entry *ge = data;
2152 if (send_job_files(ge)) {
2155 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);
2156 report_error(error);
2157 g_error_free(error);
2159 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2163 static void on_info_bar_response(GtkWidget *widget, gint response,
2166 struct gui *ui = &main_ui;
2168 if (response == GTK_RESPONSE_OK) {
2169 gtk_widget_destroy(widget);
2170 ui->error_info_bar = NULL;
2174 void report_error(GError *error)
2176 struct gui *ui = &main_ui;
2178 if (ui->error_info_bar == NULL) {
2179 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2182 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2183 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2186 ui->error_label = gtk_label_new(error->message);
2187 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2188 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2190 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2191 gtk_widget_show_all(ui->vbox);
2194 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2195 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2199 struct connection_widgets
2206 static void hostname_cb(GtkEntry *entry, gpointer data)
2208 struct connection_widgets *cw = data;
2209 int uses_net = 0, is_localhost = 0;
2214 * Check whether to display the 'auto start backend' box
2215 * or not. Show it if we are a localhost and using network,
2216 * or using a socket.
2218 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2219 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2224 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2225 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2226 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2227 !strcmp(text, "ip6-loopback"))
2231 if (!uses_net || is_localhost) {
2232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2233 gtk_widget_set_sensitive(cw->button, 1);
2235 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2236 gtk_widget_set_sensitive(cw->button, 0);
2240 static int get_connection_details(char **host, int *port, int *type,
2243 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2244 struct connection_widgets cw;
2247 dialog = gtk_dialog_new_with_buttons("Connection details",
2248 GTK_WINDOW(main_ui.window),
2249 GTK_DIALOG_DESTROY_WITH_PARENT,
2250 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2251 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2253 frame = gtk_frame_new("Hostname / socket name");
2254 /* gtk_dialog_get_content_area() is 2.14 and newer */
2255 vbox = GTK_DIALOG(dialog)->vbox;
2256 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2258 box = gtk_vbox_new(FALSE, 6);
2259 gtk_container_add(GTK_CONTAINER(frame), box);
2261 hbox = gtk_hbox_new(TRUE, 10);
2262 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2263 cw.hentry = gtk_entry_new();
2264 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2265 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2267 frame = gtk_frame_new("Port");
2268 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2269 box = gtk_vbox_new(FALSE, 10);
2270 gtk_container_add(GTK_CONTAINER(frame), box);
2272 hbox = gtk_hbox_new(TRUE, 4);
2273 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2274 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2276 frame = gtk_frame_new("Type");
2277 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2278 box = gtk_vbox_new(FALSE, 10);
2279 gtk_container_add(GTK_CONTAINER(frame), box);
2281 hbox = gtk_hbox_new(TRUE, 4);
2282 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2284 cw.combo = gtk_combo_box_new_text();
2285 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2286 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2287 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2288 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2290 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2292 frame = gtk_frame_new("Options");
2293 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2294 box = gtk_vbox_new(FALSE, 10);
2295 gtk_container_add(GTK_CONTAINER(frame), box);
2297 hbox = gtk_hbox_new(TRUE, 4);
2298 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2300 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2301 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2302 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.");
2303 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2306 * Connect edit signal, so we can show/not-show the auto start button
2308 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2309 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2311 gtk_widget_show_all(dialog);
2313 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2314 gtk_widget_destroy(dialog);
2318 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2319 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2321 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2322 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2323 *type = Fio_client_ipv4;
2324 else if (!strncmp(typeentry, "IPv6", 4))
2325 *type = Fio_client_ipv6;
2327 *type = Fio_client_socket;
2330 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2332 gtk_widget_destroy(dialog);
2336 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2338 struct gfio_client *gc;
2340 gc = malloc(sizeof(*gc));
2341 memset(gc, 0, sizeof(*gc));
2343 gc->client = fio_get_client(client);
2347 client->client_data = gc;
2350 static GtkWidget *new_client_page(struct gui_entry *ge);
2352 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2354 struct gui_entry *ge;
2356 ge = malloc(sizeof(*ge));
2357 memset(ge, 0, sizeof(*ge));
2358 ge->state = GE_STATE_NEW;
2359 INIT_FLIST_HEAD(&ge->list);
2360 flist_add_tail(&ge->list, &ui->list);
2365 static struct gui_entry *get_new_ge_with_tab(const char *name)
2367 struct gui_entry *ge;
2369 ge = alloc_new_gui_entry(&main_ui);
2371 ge->vbox = new_client_page(ge);
2372 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2374 ge->page_label = gtk_label_new(name);
2375 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2377 gtk_widget_show_all(main_ui.window);
2381 static void file_new(GtkWidget *w, gpointer data)
2383 struct gui *ui = (struct gui *) data;
2384 struct gui_entry *ge;
2386 ge = get_new_ge_with_tab("Untitled");
2387 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2391 * Return the 'ge' corresponding to the tab. If the active tab is the
2392 * main tab, open a new tab.
2394 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2396 struct flist_head *entry;
2397 struct gui_entry *ge;
2402 return get_new_ge_with_tab("Untitled");
2408 flist_for_each(entry, &main_ui.list) {
2409 ge = flist_entry(entry, struct gui_entry, list);
2410 if (ge->page_num == cur_page)
2417 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2422 * Main tab is tab 0, so any current page other than 0 holds
2425 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2427 return get_ge_from_page(cur_page, NULL);
2432 static void file_close(GtkWidget *w, gpointer data)
2434 struct gui *ui = (struct gui *) data;
2435 struct gui_entry *ge;
2438 * Can't close the main tab
2440 ge = get_ge_from_cur_tab(ui);
2442 gtk_widget_destroy(ge->vbox);
2446 if (!flist_empty(&ui->list)) {
2447 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2454 static void file_add_recent(struct gui *ui, const gchar *uri)
2458 memset(&grd, 0, sizeof(grd));
2459 grd.display_name = strdup("gfio");
2460 grd.description = strdup("Fio job file");
2461 grd.mime_type = strdup(GFIO_MIME);
2462 grd.app_name = strdup(g_get_application_name());
2463 grd.app_exec = strdup("gfio %f/%u");
2465 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2468 static gchar *get_filename_from_uri(const gchar *uri)
2470 if (strncmp(uri, "file://", 7))
2473 return strdup(uri + 7);
2476 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2479 struct fio_client *client;
2482 filename = get_filename_from_uri(uri);
2484 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2485 ge->job_files[ge->nr_job_files] = strdup(filename);
2488 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2492 error = g_error_new(g_quark_from_string("fio"), 1,
2493 "Failed to add client %s", host);
2494 report_error(error);
2495 g_error_free(error);
2499 gfio_client_added(ge, client);
2500 file_add_recent(ge->ui, uri);
2504 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2506 int port, type, server_start;
2507 struct gui_entry *ge;
2510 int ret, ge_is_new = 0;
2513 * Creates new tab if current tab is the main window, or the
2514 * current tab already has a client.
2516 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2517 ge = get_ge_from_page(cur_page, &ge_is_new);
2519 ge = get_new_ge_with_tab("Untitled");
2523 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2525 if (get_connection_details(&host, &port, &type, &server_start)) {
2527 gtk_widget_destroy(ge->vbox);
2532 ret = do_file_open(ge, uri, host, type, port);
2538 gfio_start_server();
2541 gtk_widget_destroy(ge->vbox);
2547 static void recent_open(GtkAction *action, gpointer data)
2549 struct gui *ui = (struct gui *) data;
2550 GtkRecentInfo *info;
2553 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2554 uri = gtk_recent_info_get_uri(info);
2556 do_file_open_with_tab(ui, uri);
2559 static void file_open(GtkWidget *w, gpointer data)
2561 struct gui *ui = data;
2563 GSList *filenames, *fn_glist;
2564 GtkFileFilter *filter;
2566 dialog = gtk_file_chooser_dialog_new("Open File",
2567 GTK_WINDOW(ui->window),
2568 GTK_FILE_CHOOSER_ACTION_OPEN,
2569 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2570 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2572 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2574 filter = gtk_file_filter_new();
2575 gtk_file_filter_add_pattern(filter, "*.fio");
2576 gtk_file_filter_add_pattern(filter, "*.job");
2577 gtk_file_filter_add_pattern(filter, "*.ini");
2578 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2579 gtk_file_filter_set_name(filter, "Fio job file");
2580 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2582 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2583 gtk_widget_destroy(dialog);
2587 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2589 gtk_widget_destroy(dialog);
2591 filenames = fn_glist;
2592 while (filenames != NULL) {
2593 if (do_file_open_with_tab(ui, filenames->data))
2595 filenames = g_slist_next(filenames);
2598 g_slist_free(fn_glist);
2601 static void file_save(GtkWidget *w, gpointer data)
2603 struct gui *ui = data;
2606 dialog = gtk_file_chooser_dialog_new("Save File",
2607 GTK_WINDOW(ui->window),
2608 GTK_FILE_CHOOSER_ACTION_SAVE,
2609 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2610 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2613 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2614 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2616 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2619 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2620 // save_job_file(filename);
2623 gtk_widget_destroy(dialog);
2626 static void view_log_destroy(GtkWidget *w, gpointer data)
2628 struct gui *ui = (struct gui *) data;
2630 gtk_widget_ref(ui->log_tree);
2631 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2632 gtk_widget_destroy(w);
2633 ui->log_view = NULL;
2636 static void view_log(GtkWidget *w, gpointer data)
2638 GtkWidget *win, *scroll, *vbox, *box;
2639 struct gui *ui = (struct gui *) data;
2644 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2645 gtk_window_set_title(GTK_WINDOW(win), "Log");
2646 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2648 scroll = gtk_scrolled_window_new(NULL, NULL);
2650 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2652 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2654 box = gtk_hbox_new(TRUE, 0);
2655 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2656 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2657 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2659 vbox = gtk_vbox_new(TRUE, 5);
2660 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2662 gtk_container_add(GTK_CONTAINER(win), vbox);
2663 gtk_widget_show_all(win);
2666 static void connect_job_entry(GtkWidget *w, gpointer data)
2668 struct gui *ui = (struct gui *) data;
2669 struct gui_entry *ge;
2671 ge = get_ge_from_cur_tab(ui);
2673 connect_clicked(w, ge);
2676 static void send_job_entry(GtkWidget *w, gpointer data)
2678 struct gui *ui = (struct gui *) data;
2679 struct gui_entry *ge;
2681 ge = get_ge_from_cur_tab(ui);
2683 send_clicked(w, ge);
2687 static void edit_job_entry(GtkWidget *w, gpointer data)
2691 static void start_job_entry(GtkWidget *w, gpointer data)
2693 struct gui *ui = (struct gui *) data;
2694 struct gui_entry *ge;
2696 ge = get_ge_from_cur_tab(ui);
2698 start_job_clicked(w, ge);
2701 static void view_results(GtkWidget *w, gpointer data)
2703 struct gui *ui = (struct gui *) data;
2704 struct gfio_client *gc;
2705 struct gui_entry *ge;
2707 ge = get_ge_from_cur_tab(ui);
2711 if (ge->results_window)
2715 if (gc && gc->nr_results)
2716 gfio_display_end_results(gc);
2719 static void __update_graph_limits(struct gfio_graphs *g)
2721 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2722 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2725 static void update_graph_limits(void)
2727 struct flist_head *entry;
2728 struct gui_entry *ge;
2730 __update_graph_limits(&main_ui.graphs);
2732 flist_for_each(entry, &main_ui.list) {
2733 ge = flist_entry(entry, struct gui_entry, list);
2734 __update_graph_limits(&ge->graphs);
2738 static void preferences(GtkWidget *w, gpointer data)
2740 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2741 GtkWidget *hbox, *spin, *entry, *spin_int;
2744 dialog = gtk_dialog_new_with_buttons("Preferences",
2745 GTK_WINDOW(main_ui.window),
2746 GTK_DIALOG_DESTROY_WITH_PARENT,
2747 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2748 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2751 frame = gtk_frame_new("Graphing");
2752 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2753 vbox = gtk_vbox_new(FALSE, 6);
2754 gtk_container_add(GTK_CONTAINER(frame), vbox);
2756 hbox = gtk_hbox_new(FALSE, 5);
2757 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2758 entry = gtk_label_new("Font face to use for graph labels");
2759 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2761 font = gtk_font_button_new();
2762 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2764 box = gtk_vbox_new(FALSE, 6);
2765 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2767 hbox = gtk_hbox_new(FALSE, 5);
2768 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2769 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2770 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2772 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2774 box = gtk_vbox_new(FALSE, 6);
2775 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2777 hbox = gtk_hbox_new(FALSE, 5);
2778 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2779 entry = gtk_label_new("Client ETA request interval (msec)");
2780 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2782 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2783 frame = gtk_frame_new("Debug logging");
2784 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2785 vbox = gtk_vbox_new(FALSE, 6);
2786 gtk_container_add(GTK_CONTAINER(frame), vbox);
2788 box = gtk_hbox_new(FALSE, 6);
2789 gtk_container_add(GTK_CONTAINER(vbox), box);
2791 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2793 for (i = 0; i < FD_DEBUG_MAX; i++) {
2795 box = gtk_hbox_new(FALSE, 6);
2796 gtk_container_add(GTK_CONTAINER(vbox), box);
2800 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2801 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2802 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2805 gtk_widget_show_all(dialog);
2807 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2808 gtk_widget_destroy(dialog);
2812 for (i = 0; i < FD_DEBUG_MAX; i++) {
2815 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2817 fio_debug |= (1UL << i);
2820 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2821 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2822 update_graph_limits();
2823 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2825 gtk_widget_destroy(dialog);
2828 static void about_dialog(GtkWidget *w, gpointer data)
2830 const char *authors[] = {
2831 "Jens Axboe <axboe@kernel.dk>",
2832 "Stephen Carmeron <stephenmcameron@gmail.com>",
2835 const char *license[] = {
2836 "Fio is free software; you can redistribute it and/or modify "
2837 "it under the terms of the GNU General Public License as published by "
2838 "the Free Software Foundation; either version 2 of the License, or "
2839 "(at your option) any later version.\n",
2840 "Fio is distributed in the hope that it will be useful, "
2841 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2842 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2843 "GNU General Public License for more details.\n",
2844 "You should have received a copy of the GNU General Public License "
2845 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2846 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2848 char *license_trans;
2850 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2851 license[2], "\n", NULL);
2853 gtk_show_about_dialog(NULL,
2854 "program-name", "gfio",
2855 "comments", "Gtk2 UI for fio",
2856 "license", license_trans,
2857 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2859 "version", fio_version_string,
2860 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2861 "logo-icon-name", "fio",
2863 "wrap-license", TRUE,
2866 g_free(license_trans);
2869 static GtkActionEntry menu_items[] = {
2870 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2871 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2872 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2873 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2874 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2875 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2876 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2877 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2878 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2879 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2880 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2881 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2882 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2883 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2884 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2885 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2886 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2888 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2890 static const gchar *ui_string = " \
2892 <menubar name=\"MainMenu\"> \
2893 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2894 <menuitem name=\"New\" action=\"NewFile\" /> \
2895 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2896 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2897 <separator name=\"Separator1\"/> \
2898 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2899 <separator name=\"Separator2\"/> \
2900 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2901 <separator name=\"Separator3\"/> \
2902 <placeholder name=\"FileRecentFiles\"/> \
2903 <separator name=\"Separator4\"/> \
2904 <menuitem name=\"Quit\" action=\"Quit\" /> \
2906 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2907 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2908 <separator name=\"Separator5\"/> \
2909 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2910 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2911 <separator name=\"Separator6\"/> \
2912 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2914 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2915 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2916 <separator name=\"Separator7\"/> \
2917 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2919 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2920 <menuitem name=\"About\" action=\"About\" /> \
2926 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2929 GtkActionGroup *action_group;
2932 action_group = gtk_action_group_new("Menu");
2933 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2935 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2936 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2938 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2940 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2943 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2944 GtkWidget *vbox, GtkUIManager *ui_manager)
2946 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2949 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2951 struct gui_entry *ge = (struct gui_entry *) data;
2954 index = gtk_combo_box_get_active(box);
2956 multitext_set_entry(&ge->eta.iotype, index);
2957 multitext_set_entry(&ge->eta.ioengine, index);
2958 multitext_set_entry(&ge->eta.iodepth, index);
2961 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2963 struct gui_entry *ge = (struct gui_entry *) data;
2965 multitext_free(&ge->eta.iotype);
2966 multitext_free(&ge->eta.ioengine);
2967 multitext_free(&ge->eta.iodepth);
2970 static GtkWidget *new_client_page(struct gui_entry *ge)
2972 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2973 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2975 main_vbox = gtk_vbox_new(FALSE, 3);
2977 top_align = gtk_alignment_new(0, 0, 1, 0);
2978 top_vbox = gtk_vbox_new(FALSE, 3);
2979 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2980 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2982 probe = gtk_frame_new("Job");
2983 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2984 probe_frame = gtk_vbox_new(FALSE, 3);
2985 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2987 probe_box = gtk_hbox_new(FALSE, 3);
2988 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2989 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2990 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2991 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2992 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2994 probe_box = gtk_hbox_new(FALSE, 3);
2995 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2997 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2998 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2999 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
3000 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
3001 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
3002 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
3003 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
3004 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
3006 probe_box = gtk_hbox_new(FALSE, 3);
3007 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3008 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3009 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3010 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3011 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3014 * Only add this if we have a commit rate
3017 probe_box = gtk_hbox_new(FALSE, 3);
3018 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3020 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3021 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3023 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3024 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3028 * Set up a drawing area and IOPS and bandwidth graphs
3030 ge->graphs.drawing_area = gtk_drawing_area_new();
3031 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
3032 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3033 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3034 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3035 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3036 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3037 G_CALLBACK(on_config_drawing_area), &ge->graphs);
3038 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3039 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3040 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3041 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3042 ge->graphs.drawing_area);
3043 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
3045 setup_graphs(&ge->graphs);
3048 * Set up alignments for widgets at the bottom of ui,
3049 * align bottom left, expand horizontally but not vertically
3051 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3052 ge->buttonbox = gtk_hbox_new(FALSE, 0);
3053 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3054 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3056 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3059 * Set up thread status progress bar
3061 ge->thread_status_pb = gtk_progress_bar_new();
3062 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3063 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3064 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3070 static GtkWidget *new_main_page(struct gui *ui)
3072 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
3073 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
3075 main_vbox = gtk_vbox_new(FALSE, 3);
3078 * Set up alignments for widgets at the top of ui,
3079 * align top left, expand horizontally but not vertically
3081 top_align = gtk_alignment_new(0, 0, 1, 0);
3082 top_vbox = gtk_vbox_new(FALSE, 0);
3083 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3084 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3086 probe = gtk_frame_new("Run statistics");
3087 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3088 probe_frame = gtk_vbox_new(FALSE, 3);
3089 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3091 probe_box = gtk_hbox_new(FALSE, 3);
3092 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3093 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3094 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3095 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3096 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3097 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3100 * Only add this if we have a commit rate
3103 probe_box = gtk_hbox_new(FALSE, 3);
3104 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3106 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3107 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3109 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3110 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3114 * Set up a drawing area and IOPS and bandwidth graphs
3116 ui->graphs.drawing_area = gtk_drawing_area_new();
3117 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3118 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3119 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3120 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3121 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3122 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3123 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3124 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3125 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3126 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3127 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3128 ui->graphs.drawing_area);
3129 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3132 setup_graphs(&ui->graphs);
3135 * Set up alignments for widgets at the bottom of ui,
3136 * align bottom left, expand horizontally but not vertically
3138 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3139 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3140 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3141 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3144 * Set up thread status progress bar
3146 ui->thread_status_pb = gtk_progress_bar_new();
3147 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3148 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3149 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3154 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3155 guint page, gpointer data)
3158 struct gui *ui = (struct gui *) data;
3159 struct gui_entry *ge;
3162 set_job_menu_visible(ui, 0);
3163 set_view_results_visible(ui, 0);
3167 set_job_menu_visible(ui, 1);
3168 ge = get_ge_from_page(page, NULL);
3170 update_button_states(ui, ge);
3175 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3177 time_t time_a = gtk_recent_info_get_visited(a);
3178 time_t time_b = gtk_recent_info_get_visited(b);
3180 return time_b - time_a;
3183 static void add_recent_file_items(struct gui *ui)
3185 const gchar *gfio = g_get_application_name();
3186 GList *items, *item;
3189 if (ui->recent_ui_id) {
3190 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3191 gtk_ui_manager_ensure_update(ui->uimanager);
3193 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3195 if (ui->actiongroup) {
3196 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3197 g_object_unref(ui->actiongroup);
3199 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3201 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3203 items = gtk_recent_manager_get_items(ui->recentmanager);
3204 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3206 for (item = items; item && item->data; item = g_list_next(item)) {
3207 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3212 if (!gtk_recent_info_has_application(info, gfio))
3216 * We only support local files for now
3218 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3221 action_name = g_strdup_printf("RecentFile%u", i++);
3222 label = gtk_recent_info_get_display_name(info);
3224 action = g_object_new(GTK_TYPE_ACTION,
3225 "name", action_name,
3226 "label", label, NULL);
3228 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3229 gtk_recent_info_ref(info),
3230 (GDestroyNotify) gtk_recent_info_unref);
3233 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3235 gtk_action_group_add_action(ui->actiongroup, action);
3236 g_object_unref(action);
3238 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3239 "/MainMenu/FileMenu/FileRecentFiles",
3241 GTK_UI_MANAGER_MENUITEM, FALSE);
3243 g_free(action_name);
3249 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3253 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3254 gint x, gint y, GtkSelectionData *data,
3255 guint info, guint time)
3257 struct gui *ui = &main_ui;
3262 source = gtk_drag_get_source_widget(ctx);
3263 if (source && widget == gtk_widget_get_toplevel(source)) {
3264 gtk_drag_finish(ctx, FALSE, FALSE, time);
3268 uris = gtk_selection_data_get_uris(data);
3270 gtk_drag_finish(ctx, FALSE, FALSE, time);
3276 if (do_file_open_with_tab(ui, uris[i]))
3281 gtk_drag_finish(ctx, TRUE, FALSE, time);
3285 static void init_ui(int *argc, char **argv[], struct gui *ui)
3287 GtkSettings *settings;
3290 /* Magical g*thread incantation, you just need this thread stuff.
3291 * Without it, the update that happens in gfio_update_thread_status
3292 * doesn't really happen in a timely fashion, you need expose events
3294 if (!g_thread_supported())
3295 g_thread_init(NULL);
3298 gtk_init(argc, argv);
3299 settings = gtk_settings_get_default();
3300 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3302 gdk_color_parse("white", &white);
3304 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3305 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3306 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3308 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3309 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3311 ui->vbox = gtk_vbox_new(FALSE, 0);
3312 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3314 ui->uimanager = gtk_ui_manager_new();
3315 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3316 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3318 ui->recentmanager = gtk_recent_manager_get_default();
3319 add_recent_file_items(ui);
3321 ui->notebook = gtk_notebook_new();
3322 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3323 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3324 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3325 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3327 vbox = new_main_page(ui);
3328 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3329 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3330 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3332 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3334 gfio_ui_setup_log(ui);
3336 gtk_widget_show_all(ui->window);
3339 int main(int argc, char *argv[], char *envp[])
3341 if (initialize_fio(envp))
3343 if (fio_init_options())
3346 memset(&main_ui, 0, sizeof(main_ui));
3347 INIT_FLIST_HEAD(&main_ui.list);
3349 init_ui(&argc, &argv, &main_ui);
3351 gdk_threads_enter();
3353 gdk_threads_leave();