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 unsigned int job_added;
193 struct thread_options o;
195 struct end_results *results;
196 unsigned int nr_results;
198 struct cmd_du_pdu *du;
202 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
203 static void gfio_update_thread_status_all(char *status_message, double perc);
204 void report_error(GError *error);
206 static struct graph *setup_iops_graph(void)
210 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
211 graph_title(g, "IOPS (IOs/sec)");
212 graph_x_title(g, "Time (secs)");
213 graph_add_label(g, "Read IOPS");
214 graph_add_label(g, "Write IOPS");
215 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
216 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
217 line_graph_set_data_count_limit(g, gfio_graph_limit);
218 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
222 static struct graph *setup_bandwidth_graph(void)
226 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
227 graph_title(g, "Bandwidth (bytes/sec)");
228 graph_x_title(g, "Time (secs)");
229 graph_add_label(g, "Read Bandwidth");
230 graph_add_label(g, "Write Bandwidth");
231 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
232 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
233 graph_set_base_offset(g, 1);
234 line_graph_set_data_count_limit(g, 100);
235 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
239 static void setup_graphs(struct gfio_graphs *g)
241 g->iops_graph = setup_iops_graph();
242 g->bandwidth_graph = setup_bandwidth_graph();
245 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
247 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
248 mt->text[mt->max_text] = strdup(text);
252 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
254 if (index >= mt->max_text)
256 if (!mt->text || !mt->text[index])
259 mt->cur_text = index;
260 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
263 static void multitext_update_entry(struct multitext_widget *mt,
264 unsigned int index, const char *text)
270 free(mt->text[index]);
272 mt->text[index] = strdup(text);
273 if (mt->cur_text == index)
274 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
277 static void multitext_free(struct multitext_widget *mt)
281 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
283 for (i = 0; i < mt->max_text; i++) {
293 static void clear_ge_ui_info(struct gui_entry *ge)
295 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
296 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
297 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
298 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
300 /* should we empty it... */
301 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
303 multitext_update_entry(&ge->eta.iotype, 0, "");
304 multitext_update_entry(&ge->eta.ioengine, 0, "");
305 multitext_update_entry(&ge->eta.iodepth, 0, "");
306 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
309 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
310 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
311 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
314 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
316 GtkWidget *entry, *frame;
318 frame = gtk_frame_new(label);
319 entry = gtk_combo_box_new_text();
320 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
321 gtk_container_add(GTK_CONTAINER(frame), entry);
326 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
328 GtkWidget *entry, *frame;
330 frame = gtk_frame_new(label);
331 entry = gtk_entry_new();
332 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
333 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
334 gtk_container_add(GTK_CONTAINER(frame), entry);
339 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
341 GtkWidget *label_widget;
344 frame = gtk_frame_new(label);
345 label_widget = gtk_label_new(NULL);
346 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
347 gtk_container_add(GTK_CONTAINER(frame), label_widget);
352 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
354 GtkWidget *button, *box;
356 box = gtk_hbox_new(FALSE, 3);
357 gtk_container_add(GTK_CONTAINER(hbox), box);
359 button = gtk_spin_button_new_with_range(min, max, 1.0);
360 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
362 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
363 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
368 static void label_set_int_value(GtkWidget *entry, unsigned int val)
372 sprintf(tmp, "%u", val);
373 gtk_label_set_text(GTK_LABEL(entry), tmp);
376 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
380 sprintf(tmp, "%u", val);
381 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
384 static void show_info_dialog(struct gui *ui, const char *title,
387 GtkWidget *dialog, *content, *label;
389 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
390 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
391 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
393 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
394 label = gtk_label_new(message);
395 gtk_container_add(GTK_CONTAINER(content), label);
396 gtk_widget_show_all(dialog);
397 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
398 gtk_dialog_run(GTK_DIALOG(dialog));
399 gtk_widget_destroy(dialog);
402 static void set_menu_entry_text(struct gui *ui, const char *path,
407 w = gtk_ui_manager_get_widget(ui->uimanager, path);
409 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
411 fprintf(stderr, "gfio: can't find path %s\n", path);
415 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
419 w = gtk_ui_manager_get_widget(ui->uimanager, path);
421 gtk_widget_set_sensitive(w, show);
423 fprintf(stderr, "gfio: can't find path %s\n", path);
426 static void set_job_menu_visible(struct gui *ui, int visible)
428 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
431 static void set_view_results_visible(struct gui *ui, int visible)
433 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
436 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
438 if (s->tooltiptext[sensitive])
439 return s->tooltiptext[sensitive];
441 return s->tooltiptext[0];
444 static GtkWidget *add_button(GtkWidget *buttonbox,
445 struct button_spec *buttonspec, gpointer data)
447 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
448 gboolean sens = buttonspec->start_sensitive;
450 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
451 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
453 sens = buttonspec->start_sensitive;
454 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
455 gtk_widget_set_sensitive(button, sens);
460 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
465 for (i = 0; i < nbuttons; i++)
466 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
470 * Update sensitivity of job buttons and job menu items, based on the
471 * state of the client.
473 static void update_button_states(struct gui *ui, struct gui_entry *ge)
475 unsigned int connect_state, send_state, start_state, edit_state;
476 const char *connect_str = NULL;
482 sprintf(tmp, "Bad client state: %u\n", ge->state);
483 show_info_dialog(ui, "Error", tmp);
484 /* fall through to new state */
490 connect_str = "Connect";
494 case GE_STATE_CONNECTED:
497 connect_str = "Disconnect";
501 case GE_STATE_JOB_SENT:
504 connect_str = "Disconnect";
508 case GE_STATE_JOB_STARTED:
511 connect_str = "Disconnect";
515 case GE_STATE_JOB_RUNNING:
518 connect_str = "Disconnect";
522 case GE_STATE_JOB_DONE:
525 connect_str = "Connect";
531 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
532 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
533 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
534 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
535 gtk_widget_set_tooltip_text(ge->button[CONNECT_BUTTON], get_button_tooltip(&buttonspeclist[CONNECT_BUTTON], connect_state));
537 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
538 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
540 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
541 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
542 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
544 if (ge->client && ge->client->nr_results)
545 set_view_results_visible(ui, 1);
547 set_view_results_visible(ui, 0);
550 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
553 update_button_states(ge->ui, ge);
557 #define ALIGN_RIGHT 2
561 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
563 GtkCellRenderer *renderer;
564 GtkTreeViewColumn *col;
565 double xalign = 0.0; /* left as default */
566 PangoAlignment align;
569 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
570 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
572 visible = !(flags & INVISIBLE);
574 renderer = gtk_cell_renderer_text_new();
575 col = gtk_tree_view_column_new();
577 gtk_tree_view_column_set_title(col, title);
578 if (!(flags & UNSORTABLE))
579 gtk_tree_view_column_set_sort_column_id(col, index);
580 gtk_tree_view_column_set_resizable(col, TRUE);
581 gtk_tree_view_column_pack_start(col, renderer, TRUE);
582 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
583 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
585 case PANGO_ALIGN_LEFT:
588 case PANGO_ALIGN_CENTER:
591 case PANGO_ALIGN_RIGHT:
595 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
596 gtk_tree_view_column_set_visible(col, visible);
597 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
601 static void gfio_ui_setup_log(struct gui *ui)
603 GtkTreeSelection *selection;
605 GtkWidget *tree_view;
607 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
609 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
610 gtk_widget_set_can_focus(tree_view, FALSE);
612 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
613 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
614 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
615 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
617 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
618 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
619 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
620 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
622 ui->log_model = model;
623 ui->log_tree = tree_view;
626 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
629 double xdim, double ydim)
634 g = graph_new(xdim, ydim, gfio_graph_font);
635 graph_title(g, title);
636 graph_x_title(g, "Percentile");
638 for (i = 0; i < len; i++) {
641 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
642 graph_add_label(g, fbuf);
643 graph_add_data(g, fbuf, (double) ovals[i]);
649 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
655 GType types[FIO_IO_U_LIST_MAX_LEN];
656 GtkWidget *tree_view;
657 GtkTreeSelection *selection;
662 for (i = 0; i < len; i++)
663 types[i] = G_TYPE_INT;
665 model = gtk_list_store_newv(len, types);
667 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
668 gtk_widget_set_can_focus(tree_view, FALSE);
670 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
671 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
673 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
674 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
676 for (i = 0; i < len; i++) {
679 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
680 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
683 gtk_list_store_append(model, &iter);
685 for (i = 0; i < len; i++) {
687 ovals[i] = (ovals[i] + 999) / 1000;
688 gtk_list_store_set(model, &iter, i, ovals[i], -1);
694 static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
699 cr = gdk_cairo_create(w->window);
701 if (graph_has_tooltips(g)) {
702 g_object_set(w, "has-tooltip", TRUE, NULL);
703 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
706 cairo_set_source_rgb(cr, 0, 0, 0);
707 bar_graph_draw(g, cr);
713 static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
716 struct graph *g = data;
718 graph_set_size(g, w->allocation.width, w->allocation.height);
719 graph_set_size(g, w->allocation.width, w->allocation.height);
720 graph_set_position(g, 0, 0);
724 static void gfio_show_clat_percentiles(struct gfio_client *gc,
725 GtkWidget *vbox, struct thread_stat *ts,
728 unsigned int *io_u_plat = ts->io_u_plat[ddir];
729 unsigned long nr = ts->clat_stat[ddir].samples;
730 fio_fp64_t *plist = ts->percentile_list;
731 unsigned int *ovals, len, minv, maxv, scale_down;
733 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
734 struct gui_entry *ge = gc->ge;
737 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
742 * We default to usecs, but if the value range is such that we
743 * should scale down to msecs, do that.
745 if (minv > 2000 && maxv > 99999) {
753 sprintf(tmp, "Completion percentiles (%s)", base);
754 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
755 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
757 frame = gtk_frame_new(tmp);
758 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
760 completion_vbox = gtk_vbox_new(FALSE, 3);
761 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
762 hbox = gtk_hbox_new(FALSE, 3);
763 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
764 drawing_area = gtk_drawing_area_new();
765 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
766 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
767 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
768 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
769 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
771 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
777 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
778 unsigned long max, double mean, double dev)
780 const char *base = "(usec)";
781 GtkWidget *hbox, *label, *frame;
785 if (!usec_to_msec(&min, &max, &mean, &dev))
788 minp = num2str(min, 6, 1, 0);
789 maxp = num2str(max, 6, 1, 0);
791 sprintf(tmp, "%s %s", name, base);
792 frame = gtk_frame_new(tmp);
793 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
795 hbox = gtk_hbox_new(FALSE, 3);
796 gtk_container_add(GTK_CONTAINER(frame), hbox);
798 label = new_info_label_in_frame(hbox, "Minimum");
799 gtk_label_set_text(GTK_LABEL(label), minp);
800 label = new_info_label_in_frame(hbox, "Maximum");
801 gtk_label_set_text(GTK_LABEL(label), maxp);
802 label = new_info_label_in_frame(hbox, "Average");
803 sprintf(tmp, "%5.02f", mean);
804 gtk_label_set_text(GTK_LABEL(label), tmp);
805 label = new_info_label_in_frame(hbox, "Standard deviation");
806 sprintf(tmp, "%5.02f", dev);
807 gtk_label_set_text(GTK_LABEL(label), tmp);
818 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
819 struct group_run_stats *rs,
820 struct thread_stat *ts, int ddir)
822 const char *ddir_label[2] = { "Read", "Write" };
823 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
824 unsigned long min[3], max[3], runt;
825 unsigned long long bw, iops;
826 unsigned int flags = 0;
827 double mean[3], dev[3];
828 char *io_p, *bw_p, *iops_p;
831 if (!ts->runtime[ddir])
834 i2p = is_power_of_2(rs->kb_base);
835 runt = ts->runtime[ddir];
837 bw = (1000 * ts->io_bytes[ddir]) / runt;
838 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
839 bw_p = num2str(bw, 6, 1, i2p);
841 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
842 iops_p = num2str(iops, 6, 1, 0);
844 box = gtk_hbox_new(FALSE, 3);
845 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
847 frame = gtk_frame_new(ddir_label[ddir]);
848 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
850 main_vbox = gtk_vbox_new(FALSE, 3);
851 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
853 box = gtk_hbox_new(FALSE, 3);
854 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
856 label = new_info_label_in_frame(box, "IO");
857 gtk_label_set_text(GTK_LABEL(label), io_p);
858 label = new_info_label_in_frame(box, "Bandwidth");
859 gtk_label_set_text(GTK_LABEL(label), bw_p);
860 label = new_info_label_in_frame(box, "IOPS");
861 gtk_label_set_text(GTK_LABEL(label), iops_p);
862 label = new_info_label_in_frame(box, "Runtime (msec)");
863 label_set_int_value(label, ts->runtime[ddir]);
865 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
866 double p_of_agg = 100.0;
867 const char *bw_str = "KB";
871 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
872 if (p_of_agg > 100.0)
876 if (mean[0] > 999999.9) {
884 sprintf(tmp, "Bandwidth (%s)", bw_str);
885 frame = gtk_frame_new(tmp);
886 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
888 box = gtk_hbox_new(FALSE, 3);
889 gtk_container_add(GTK_CONTAINER(frame), box);
891 label = new_info_label_in_frame(box, "Minimum");
892 label_set_int_value(label, min[0]);
893 label = new_info_label_in_frame(box, "Maximum");
894 label_set_int_value(label, max[0]);
895 label = new_info_label_in_frame(box, "Percentage of jobs");
896 sprintf(tmp, "%3.2f%%", p_of_agg);
897 gtk_label_set_text(GTK_LABEL(label), tmp);
898 label = new_info_label_in_frame(box, "Average");
899 sprintf(tmp, "%5.02f", mean[0]);
900 gtk_label_set_text(GTK_LABEL(label), tmp);
901 label = new_info_label_in_frame(box, "Standard deviation");
902 sprintf(tmp, "%5.02f", dev[0]);
903 gtk_label_set_text(GTK_LABEL(label), tmp);
906 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
908 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
910 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
914 frame = gtk_frame_new("Latency");
915 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
917 vbox = gtk_vbox_new(FALSE, 3);
918 gtk_container_add(GTK_CONTAINER(frame), vbox);
920 if (flags & GFIO_SLAT)
921 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
922 if (flags & GFIO_CLAT)
923 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
924 if (flags & GFIO_LAT)
925 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
928 if (ts->clat_percentiles)
929 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
936 static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
939 double xdim, double ydim)
944 g = graph_new(xdim, ydim, gfio_graph_font);
945 graph_title(g, title);
946 graph_x_title(g, "Buckets");
948 for (i = 0; i < len; i++) {
949 graph_add_label(g, labels[i]);
950 graph_add_data(g, labels[i], lat[i]);
956 static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
959 GtkWidget *tree_view;
960 GtkTreeSelection *selection;
966 types = malloc(num * sizeof(GType));
968 for (i = 0; i < num; i++)
969 types[i] = G_TYPE_STRING;
971 model = gtk_list_store_newv(num, types);
975 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
976 gtk_widget_set_can_focus(tree_view, FALSE);
978 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
979 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
981 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
982 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
984 for (i = 0; i < num; i++)
985 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
987 gtk_list_store_append(model, &iter);
989 for (i = 0; i < num; i++) {
993 sprintf(fbuf, "0.00");
995 sprintf(fbuf, "%3.2f%%", lat[i]);
997 gtk_list_store_set(model, &iter, i, fbuf, -1);
1003 static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
1004 struct thread_stat *ts)
1006 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
1007 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
1008 "250u", "500u", "750u", "1m", "2m",
1009 "4m", "10m", "20m", "50m", "100m",
1010 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
1012 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
1013 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
1014 struct gui_entry *ge = gc->ge;
1016 stat_calc_lat_u(ts, io_u_lat);
1017 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
1020 * Found out which first bucket has entries, and which last bucket
1023 for (i = 0; i < total; i++) {
1024 if (io_u_lat[i] == 0.00)
1038 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1039 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
1041 frame = gtk_frame_new("Latency buckets");
1042 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1044 completion_vbox = gtk_vbox_new(FALSE, 3);
1045 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1046 hbox = gtk_hbox_new(FALSE, 3);
1047 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1049 drawing_area = gtk_drawing_area_new();
1050 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1051 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1052 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1053 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1054 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1056 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
1059 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1061 GtkWidget *box, *frame, *entry;
1062 double usr_cpu, sys_cpu;
1063 unsigned long runtime;
1066 runtime = ts->total_run_time;
1068 double runt = (double) runtime;
1070 usr_cpu = (double) ts->usr_time * 100 / runt;
1071 sys_cpu = (double) ts->sys_time * 100 / runt;
1077 frame = gtk_frame_new("OS resources");
1078 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1080 box = gtk_hbox_new(FALSE, 3);
1081 gtk_container_add(GTK_CONTAINER(frame), box);
1083 entry = new_info_entry_in_frame(box, "User CPU");
1084 sprintf(tmp, "%3.2f%%", usr_cpu);
1085 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1086 entry = new_info_entry_in_frame(box, "System CPU");
1087 sprintf(tmp, "%3.2f%%", sys_cpu);
1088 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1089 entry = new_info_entry_in_frame(box, "Context switches");
1090 entry_set_int_value(entry, ts->ctx);
1091 entry = new_info_entry_in_frame(box, "Major faults");
1092 entry_set_int_value(entry, ts->majf);
1093 entry = new_info_entry_in_frame(box, "Minor faults");
1094 entry_set_int_value(entry, ts->minf);
1096 static void gfio_add_sc_depths_tree(GtkListStore *model,
1097 struct thread_stat *ts, unsigned int len,
1100 double io_u_dist[FIO_IO_U_MAP_NR];
1102 /* Bits 0, and 3-8 */
1103 const int add_mask = 0x1f9;
1107 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1109 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1111 gtk_list_store_append(model, &iter);
1113 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1115 for (i = 1, j = 0; i < len; i++) {
1118 if (!(add_mask & (1UL << (i - 1))))
1119 sprintf(fbuf, "0.0%%");
1121 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1125 gtk_list_store_set(model, &iter, i, fbuf, -1);
1130 static void gfio_add_total_depths_tree(GtkListStore *model,
1131 struct thread_stat *ts, unsigned int len)
1133 double io_u_dist[FIO_IO_U_MAP_NR];
1135 /* Bits 1-6, and 8 */
1136 const int add_mask = 0x17e;
1139 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1141 gtk_list_store_append(model, &iter);
1143 gtk_list_store_set(model, &iter, 0, "Total", -1);
1145 for (i = 1, j = 0; i < len; i++) {
1148 if (!(add_mask & (1UL << (i - 1))))
1149 sprintf(fbuf, "0.0%%");
1151 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1155 gtk_list_store_set(model, &iter, i, fbuf, -1);
1160 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1162 GtkWidget *frame, *box, *tree_view;
1163 GtkTreeSelection *selection;
1164 GtkListStore *model;
1165 GType types[FIO_IO_U_MAP_NR + 1];
1167 #define NR_LABELS 10
1168 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1170 frame = gtk_frame_new("IO depths");
1171 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1173 box = gtk_hbox_new(FALSE, 3);
1174 gtk_container_add(GTK_CONTAINER(frame), box);
1176 for (i = 0; i < NR_LABELS; i++)
1177 types[i] = G_TYPE_STRING;
1179 model = gtk_list_store_newv(NR_LABELS, types);
1181 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1182 gtk_widget_set_can_focus(tree_view, FALSE);
1184 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1185 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1187 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1188 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1190 for (i = 0; i < NR_LABELS; i++)
1191 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1193 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1194 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1195 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1197 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1200 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1202 struct gui_entry *ge = (struct gui_entry *) data;
1204 gtk_widget_destroy(w);
1205 ge->results_window = NULL;
1206 ge->results_notebook = NULL;
1210 static void results_close(GtkWidget *w, gpointer *data)
1212 struct gui_entry *ge = (struct gui_entry *) data;
1214 gtk_widget_destroy(ge->results_window);
1217 static GtkActionEntry results_menu_items[] = {
1218 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1219 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1220 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1222 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1224 static const gchar *results_ui_string = " \
1226 <menubar name=\"MainMenu\"> \
1227 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1228 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1230 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1236 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1238 GtkActionGroup *action_group;
1242 ge->results_uimanager = gtk_ui_manager_new();
1244 action_group = gtk_action_group_new("ResultsMenu");
1245 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1247 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1248 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1250 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1252 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1256 static GtkWidget *get_results_window(struct gui_entry *ge)
1258 GtkWidget *win, *notebook, *vbox;
1260 if (ge->results_window)
1261 return ge->results_notebook;
1263 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1264 gtk_window_set_title(GTK_WINDOW(win), "Results");
1265 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1266 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1267 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1269 vbox = gtk_vbox_new(FALSE, 0);
1270 gtk_container_add(GTK_CONTAINER(win), vbox);
1272 ge->results_menu = get_results_menubar(win, ge);
1273 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1275 notebook = gtk_notebook_new();
1276 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1277 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1278 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1280 ge->results_window = win;
1281 ge->results_notebook = notebook;
1282 return ge->results_notebook;
1285 static void disk_util_destroy(GtkWidget *w, gpointer data)
1287 struct gui_entry *ge = (struct gui_entry *) data;
1289 ge->disk_util_vbox = NULL;
1290 gtk_widget_destroy(w);
1293 static int __gfio_disk_util_show(GtkWidget *res_notebook,
1294 struct gfio_client *gc, struct cmd_du_pdu *p)
1296 GtkWidget *box, *frame, *entry, *vbox;
1297 struct gui_entry *ge = gc->ge;
1301 res_notebook = get_results_window(ge);
1303 if (!ge->disk_util_vbox) {
1304 vbox = gtk_vbox_new(FALSE, 3);
1305 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), vbox, gtk_label_new("Disk utilization"));
1306 ge->disk_util_vbox = vbox;
1307 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1310 vbox = gtk_vbox_new(FALSE, 3);
1311 gtk_container_add(GTK_CONTAINER(ge->disk_util_vbox), vbox);
1313 frame = gtk_frame_new((char *) p->dus.name);
1314 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1316 box = gtk_vbox_new(FALSE, 3);
1317 gtk_container_add(GTK_CONTAINER(frame), box);
1319 frame = gtk_frame_new("Read");
1320 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1321 vbox = gtk_hbox_new(TRUE, 3);
1322 gtk_container_add(GTK_CONTAINER(frame), vbox);
1323 entry = new_info_entry_in_frame(vbox, "IOs");
1324 entry_set_int_value(entry, p->dus.ios[0]);
1325 entry = new_info_entry_in_frame(vbox, "Merges");
1326 entry_set_int_value(entry, p->dus.merges[0]);
1327 entry = new_info_entry_in_frame(vbox, "Sectors");
1328 entry_set_int_value(entry, p->dus.sectors[0]);
1329 entry = new_info_entry_in_frame(vbox, "Ticks");
1330 entry_set_int_value(entry, p->dus.ticks[0]);
1332 frame = gtk_frame_new("Write");
1333 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1334 vbox = gtk_hbox_new(TRUE, 3);
1335 gtk_container_add(GTK_CONTAINER(frame), vbox);
1336 entry = new_info_entry_in_frame(vbox, "IOs");
1337 entry_set_int_value(entry, p->dus.ios[1]);
1338 entry = new_info_entry_in_frame(vbox, "Merges");
1339 entry_set_int_value(entry, p->dus.merges[1]);
1340 entry = new_info_entry_in_frame(vbox, "Sectors");
1341 entry_set_int_value(entry, p->dus.sectors[1]);
1342 entry = new_info_entry_in_frame(vbox, "Ticks");
1343 entry_set_int_value(entry, p->dus.ticks[1]);
1345 frame = gtk_frame_new("Shared");
1346 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1347 vbox = gtk_hbox_new(TRUE, 3);
1348 gtk_container_add(GTK_CONTAINER(frame), vbox);
1349 entry = new_info_entry_in_frame(vbox, "IO ticks");
1350 entry_set_int_value(entry, p->dus.io_ticks);
1351 entry = new_info_entry_in_frame(vbox, "Time in queue");
1352 entry_set_int_value(entry, p->dus.time_in_queue);
1356 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1360 sprintf(tmp, "%3.2f%%", util);
1361 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1362 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1364 gtk_widget_show_all(ge->results_window);
1368 static int gfio_disk_util_show(struct gfio_client *gc)
1370 struct gui_entry *ge = gc->ge;
1371 GtkWidget *res_notebook;
1377 res_notebook = get_results_window(ge);
1379 for (i = 0; i < gc->nr_du; i++) {
1380 struct cmd_du_pdu *p = &gc->du[i];
1382 __gfio_disk_util_show(res_notebook, gc, p);
1385 gtk_widget_show_all(ge->results_window);
1389 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1390 struct group_run_stats *rs)
1392 unsigned int nr = gc->nr_results;
1394 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1395 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1396 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1400 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1401 struct thread_stat *ts,
1402 struct group_run_stats *rs)
1404 GtkWidget *box, *vbox, *entry, *scroll;
1406 scroll = gtk_scrolled_window_new(NULL, NULL);
1407 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1408 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1410 vbox = gtk_vbox_new(FALSE, 3);
1412 box = gtk_hbox_new(FALSE, 0);
1413 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1415 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1417 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1419 entry = new_info_entry_in_frame(box, "Name");
1420 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1421 if (strlen(ts->description)) {
1422 entry = new_info_entry_in_frame(box, "Description");
1423 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1425 entry = new_info_entry_in_frame(box, "Group ID");
1426 entry_set_int_value(entry, ts->groupid);
1427 entry = new_info_entry_in_frame(box, "Jobs");
1428 entry_set_int_value(entry, ts->members);
1429 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1430 entry_set_int_value(entry, ts->error);
1431 entry = new_info_entry_in_frame(box, "PID");
1432 entry_set_int_value(entry, ts->pid);
1434 if (ts->io_bytes[DDIR_READ])
1435 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1436 if (ts->io_bytes[DDIR_WRITE])
1437 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1439 gfio_show_latency_buckets(gc, vbox, ts);
1440 gfio_show_cpu_usage(vbox, ts);
1441 gfio_show_io_depths(vbox, ts);
1444 static void gfio_display_end_results(struct gfio_client *gc)
1446 struct gui_entry *ge = gc->ge;
1447 GtkWidget *res_notebook;
1450 res_notebook = get_results_window(ge);
1452 for (i = 0; i < gc->nr_results; i++) {
1453 struct end_results *e = &gc->results[i];
1455 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1458 if (gfio_disk_util_show(gc))
1459 gtk_widget_show_all(ge->results_window);
1462 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1463 struct group_run_stats *rs)
1465 struct gfio_client *gc = client->client_data;
1467 gfio_add_end_results(gc, ts, rs);
1469 gdk_threads_enter();
1470 gfio_display_end_results(gc);
1471 gdk_threads_leave();
1474 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1476 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1477 struct gui *ui = &main_ui;
1481 char tmp[64], timebuf[80];
1484 tm = localtime(&sec);
1485 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1486 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1488 gdk_threads_enter();
1490 gtk_list_store_append(ui->log_model, &iter);
1491 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1492 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1493 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1494 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1496 if (p->level == FIO_LOG_ERR)
1497 view_log(NULL, (gpointer) ui);
1499 gdk_threads_leave();
1502 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1504 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1505 struct gfio_client *gc = client->client_data;
1506 unsigned int nr = gc->nr_du;
1508 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1509 memcpy(&gc->du[nr], p, sizeof(*p));
1512 gdk_threads_enter();
1513 gfio_disk_util_show(gc);
1514 gdk_threads_leave();
1517 extern int sum_stat_clients;
1518 extern struct thread_stat client_ts;
1519 extern struct group_run_stats client_gs;
1521 static int sum_stat_nr;
1523 static void gfio_thread_status_op(struct fio_client *client,
1524 struct fio_net_cmd *cmd)
1526 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1528 gfio_display_ts(client, &p->ts, &p->rs);
1530 if (sum_stat_clients == 1)
1533 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1534 sum_group_stats(&client_gs, &p->rs);
1536 client_ts.members++;
1537 client_ts.thread_number = p->ts.thread_number;
1538 client_ts.groupid = p->ts.groupid;
1540 if (++sum_stat_nr == sum_stat_clients) {
1541 strcpy(client_ts.name, "All clients");
1542 gfio_display_ts(client, &client_ts, &client_gs);
1546 static void gfio_group_stats_op(struct fio_client *client,
1547 struct fio_net_cmd *cmd)
1549 /* We're ignoring group stats for now */
1552 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1555 struct gfio_graphs *g = data;
1557 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1558 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1559 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1560 graph_set_position(g->bandwidth_graph, 0, 0);
1564 static void draw_graph(struct graph *g, cairo_t *cr)
1566 line_graph_draw(g, cr);
1570 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1571 gboolean keyboard_mode, GtkTooltip *tooltip,
1574 struct gfio_graphs *g = data;
1575 const char *text = NULL;
1577 if (graph_contains_xy(g->iops_graph, x, y))
1578 text = graph_find_tooltip(g->iops_graph, x, y);
1579 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1580 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1583 gtk_tooltip_set_text(tooltip, text);
1590 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1592 struct gfio_graphs *g = p;
1595 cr = gdk_cairo_create(w->window);
1597 if (graph_has_tooltips(g->iops_graph) ||
1598 graph_has_tooltips(g->bandwidth_graph)) {
1599 g_object_set(w, "has-tooltip", TRUE, NULL);
1600 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1603 cairo_set_source_rgb(cr, 0, 0, 0);
1604 draw_graph(g->iops_graph, cr);
1605 draw_graph(g->bandwidth_graph, cr);
1612 * Client specific ETA
1614 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1616 struct gfio_client *gc = client->client_data;
1617 struct gui_entry *ge = gc->ge;
1618 static int eta_good;
1625 gdk_threads_enter();
1630 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1631 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1632 eta_to_str(eta_str, je->eta_sec);
1635 sprintf(tmp, "%u", je->nr_running);
1636 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1637 sprintf(tmp, "%u", je->files_open);
1638 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1641 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1642 if (je->m_rate || je->t_rate) {
1645 mr = num2str(je->m_rate, 4, 0, i2p);
1646 tr = num2str(je->t_rate, 4, 0, i2p);
1647 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1648 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1651 } else if (je->m_iops || je->t_iops)
1652 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1654 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1655 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1656 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1657 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1660 if (je->eta_sec != INT_MAX && je->nr_running) {
1664 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1665 strcpy(output, "-.-% done");
1669 sprintf(output, "%3.1f%% done", perc);
1672 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1673 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1675 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1676 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1678 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1679 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1680 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1681 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1683 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1684 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1685 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1686 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1695 char *dst = output + strlen(output);
1697 sprintf(dst, " - %s", eta_str);
1700 gfio_update_thread_status(ge, output, perc);
1701 gdk_threads_leave();
1705 * Update ETA in main window for all clients
1707 static void gfio_update_all_eta(struct jobs_eta *je)
1709 struct gui *ui = &main_ui;
1710 static int eta_good;
1716 gdk_threads_enter();
1721 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1722 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1723 eta_to_str(eta_str, je->eta_sec);
1727 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1728 if (je->m_rate || je->t_rate) {
1731 mr = num2str(je->m_rate, 4, 0, i2p);
1732 tr = num2str(je->t_rate, 4, 0, i2p);
1733 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1734 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1737 } else if (je->m_iops || je->t_iops)
1738 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1740 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1741 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1742 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1743 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1746 entry_set_int_value(ui->eta.jobs, je->nr_running);
1748 if (je->eta_sec != INT_MAX && je->nr_running) {
1752 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1753 strcpy(output, "-.-% done");
1757 sprintf(output, "%3.1f%% done", perc);
1760 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1761 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1763 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1764 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1766 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1767 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1768 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1769 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1771 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1772 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1773 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1774 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1783 char *dst = output + strlen(output);
1785 sprintf(dst, " - %s", eta_str);
1788 gfio_update_thread_status_all(output, perc);
1789 gdk_threads_leave();
1792 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1794 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1795 struct gfio_client *gc = client->client_data;
1796 struct gui_entry *ge = gc->ge;
1797 const char *os, *arch;
1800 os = fio_get_os_string(probe->os);
1804 arch = fio_get_arch_string(probe->arch);
1809 client->name = strdup((char *) probe->hostname);
1811 gdk_threads_enter();
1813 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1814 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1815 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1816 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1817 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1819 gfio_set_state(ge, GE_STATE_CONNECTED);
1821 gdk_threads_leave();
1824 static void gfio_update_thread_status(struct gui_entry *ge,
1825 char *status_message, double perc)
1827 static char message[100];
1828 const char *m = message;
1830 strncpy(message, status_message, sizeof(message) - 1);
1831 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1832 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1833 gtk_widget_queue_draw(main_ui.window);
1836 static void gfio_update_thread_status_all(char *status_message, double perc)
1838 struct gui *ui = &main_ui;
1839 static char message[100];
1840 const char *m = message;
1842 strncpy(message, status_message, sizeof(message) - 1);
1843 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1844 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1845 gtk_widget_queue_draw(ui->window);
1848 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1850 struct gfio_client *gc = client->client_data;
1852 gdk_threads_enter();
1853 gfio_set_state(gc->ge, GE_STATE_NEW);
1854 gdk_threads_leave();
1857 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1859 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1860 struct gfio_client *gc = client->client_data;
1861 struct thread_options *o = &gc->o;
1862 struct gui_entry *ge = gc->ge;
1865 p->thread_number = le32_to_cpu(p->thread_number);
1866 p->groupid = le32_to_cpu(p->groupid);
1867 convert_thread_options_to_cpu(o, &p->top);
1869 gdk_threads_enter();
1871 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1873 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1874 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1876 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1877 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1879 sprintf(tmp, "%u", o->iodepth);
1880 multitext_add_entry(&ge->eta.iodepth, tmp);
1882 multitext_set_entry(&ge->eta.iotype, 0);
1883 multitext_set_entry(&ge->eta.ioengine, 0);
1884 multitext_set_entry(&ge->eta.iodepth, 0);
1888 gfio_set_state(ge, GE_STATE_JOB_SENT);
1890 gdk_threads_leave();
1893 static void gfio_client_timed_out(struct fio_client *client)
1895 struct gfio_client *gc = client->client_data;
1898 gdk_threads_enter();
1900 gfio_set_state(gc->ge, GE_STATE_NEW);
1901 clear_ge_ui_info(gc->ge);
1903 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1904 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1906 gdk_threads_leave();
1909 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1911 struct gfio_client *gc = client->client_data;
1913 gdk_threads_enter();
1915 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1918 entry_set_int_value(gc->err_entry, client->error);
1920 gdk_threads_leave();
1923 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1925 struct gfio_client *gc = client->client_data;
1927 gdk_threads_enter();
1928 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1929 gdk_threads_leave();
1932 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1934 struct gfio_client *gc = client->client_data;
1936 gdk_threads_enter();
1937 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1938 gdk_threads_leave();
1941 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1943 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1947 struct client_ops gfio_client_ops = {
1948 .text = gfio_text_op,
1949 .disk_util = gfio_disk_util_op,
1950 .thread_status = gfio_thread_status_op,
1951 .group_stats = gfio_group_stats_op,
1952 .jobs_eta = gfio_update_client_eta,
1953 .eta = gfio_update_all_eta,
1954 .probe = gfio_probe_op,
1955 .quit = gfio_quit_op,
1956 .add_job = gfio_add_job_op,
1957 .timed_out = gfio_client_timed_out,
1958 .stop = gfio_client_stop,
1959 .start = gfio_client_start,
1960 .job_start = gfio_client_job_start,
1961 .iolog = gfio_client_iolog,
1962 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1963 .stay_connected = 1,
1964 .client_type = FIO_CLIENT_TYPE_GUI,
1968 * FIXME: need more handling here
1970 static void ge_destroy(struct gui_entry *ge)
1972 struct gfio_client *gc = ge->client;
1974 if (gc && gc->client) {
1975 if (ge->state >= GE_STATE_CONNECTED)
1976 fio_client_terminate(gc->client);
1978 fio_put_client(gc->client);
1981 flist_del(&ge->list);
1985 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1989 static void gfio_quit(struct gui *ui)
1991 struct gui_entry *ge;
1993 while (!flist_empty(&ui->list)) {
1994 ge = flist_entry(ui->list.next, struct gui_entry, list);
2001 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
2002 __attribute__((unused)) gpointer data)
2007 static void *job_thread(void *arg)
2009 struct gui *ui = arg;
2011 ui->handler_running = 1;
2012 fio_handle_clients(&gfio_client_ops);
2013 ui->handler_running = 0;
2017 static int send_job_files(struct gui_entry *ge)
2019 struct gfio_client *gc = ge->client;
2022 for (i = 0; i < ge->nr_job_files; i++) {
2023 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
2027 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
2028 report_error(error);
2029 g_error_free(error);
2034 free(ge->job_files[i]);
2035 ge->job_files[i] = NULL;
2037 while (i < ge->nr_job_files) {
2038 free(ge->job_files[i]);
2039 ge->job_files[i] = NULL;
2043 free(ge->job_files);
2044 ge->job_files = NULL;
2045 ge->nr_job_files = 0;
2049 static void *server_thread(void *arg)
2052 gfio_server_running = 1;
2053 fio_start_server(NULL);
2054 gfio_server_running = 0;
2058 static void gfio_start_server(void)
2060 struct gui *ui = &main_ui;
2062 if (!gfio_server_running) {
2063 gfio_server_running = 1;
2064 pthread_create(&ui->server_t, NULL, server_thread, NULL);
2065 pthread_detach(ui->server_t);
2069 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2072 struct gui_entry *ge = data;
2073 struct gfio_client *gc = ge->client;
2076 fio_start_client(gc->client);
2079 static void file_open(GtkWidget *w, gpointer data);
2081 static void connect_clicked(GtkWidget *widget, gpointer data)
2083 struct gui_entry *ge = data;
2084 struct gfio_client *gc = ge->client;
2086 if (ge->state == GE_STATE_NEW) {
2089 if (!ge->nr_job_files)
2090 file_open(widget, ge->ui);
2091 if (!ge->nr_job_files)
2096 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2097 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2098 ret = fio_client_connect(gc->client);
2100 if (!ge->ui->handler_running)
2101 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2102 gfio_set_state(ge, GE_STATE_CONNECTED);
2106 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2107 report_error(error);
2108 g_error_free(error);
2111 fio_client_terminate(gc->client);
2112 gfio_set_state(ge, GE_STATE_NEW);
2113 clear_ge_ui_info(ge);
2117 static void send_clicked(GtkWidget *widget, gpointer data)
2119 struct gui_entry *ge = data;
2121 if (send_job_files(ge)) {
2124 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);
2125 report_error(error);
2126 g_error_free(error);
2128 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2132 static void on_info_bar_response(GtkWidget *widget, gint response,
2135 struct gui *ui = &main_ui;
2137 if (response == GTK_RESPONSE_OK) {
2138 gtk_widget_destroy(widget);
2139 ui->error_info_bar = NULL;
2143 void report_error(GError *error)
2145 struct gui *ui = &main_ui;
2147 if (ui->error_info_bar == NULL) {
2148 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2151 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2152 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2155 ui->error_label = gtk_label_new(error->message);
2156 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2157 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2159 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2160 gtk_widget_show_all(ui->vbox);
2163 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2164 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2168 struct connection_widgets
2175 static void hostname_cb(GtkEntry *entry, gpointer data)
2177 struct connection_widgets *cw = data;
2178 int uses_net = 0, is_localhost = 0;
2183 * Check whether to display the 'auto start backend' box
2184 * or not. Show it if we are a localhost and using network,
2185 * or using a socket.
2187 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2188 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2193 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2194 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2195 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2196 !strcmp(text, "ip6-loopback"))
2200 if (!uses_net || is_localhost) {
2201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2202 gtk_widget_set_sensitive(cw->button, 1);
2204 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2205 gtk_widget_set_sensitive(cw->button, 0);
2209 static int get_connection_details(char **host, int *port, int *type,
2212 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2213 struct connection_widgets cw;
2216 dialog = gtk_dialog_new_with_buttons("Connection details",
2217 GTK_WINDOW(main_ui.window),
2218 GTK_DIALOG_DESTROY_WITH_PARENT,
2219 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2220 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2222 frame = gtk_frame_new("Hostname / socket name");
2223 /* gtk_dialog_get_content_area() is 2.14 and newer */
2224 vbox = GTK_DIALOG(dialog)->vbox;
2225 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2227 box = gtk_vbox_new(FALSE, 6);
2228 gtk_container_add(GTK_CONTAINER(frame), box);
2230 hbox = gtk_hbox_new(TRUE, 10);
2231 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2232 cw.hentry = gtk_entry_new();
2233 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2234 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2236 frame = gtk_frame_new("Port");
2237 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2238 box = gtk_vbox_new(FALSE, 10);
2239 gtk_container_add(GTK_CONTAINER(frame), box);
2241 hbox = gtk_hbox_new(TRUE, 4);
2242 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2243 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2245 frame = gtk_frame_new("Type");
2246 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2247 box = gtk_vbox_new(FALSE, 10);
2248 gtk_container_add(GTK_CONTAINER(frame), box);
2250 hbox = gtk_hbox_new(TRUE, 4);
2251 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2253 cw.combo = gtk_combo_box_new_text();
2254 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2255 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2256 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2257 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2259 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2261 frame = gtk_frame_new("Options");
2262 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2263 box = gtk_vbox_new(FALSE, 10);
2264 gtk_container_add(GTK_CONTAINER(frame), box);
2266 hbox = gtk_hbox_new(TRUE, 4);
2267 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2269 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2270 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2271 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.");
2272 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2275 * Connect edit signal, so we can show/not-show the auto start button
2277 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2278 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2280 gtk_widget_show_all(dialog);
2282 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2283 gtk_widget_destroy(dialog);
2287 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2288 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2290 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2291 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2292 *type = Fio_client_ipv4;
2293 else if (!strncmp(typeentry, "IPv6", 4))
2294 *type = Fio_client_ipv6;
2296 *type = Fio_client_socket;
2299 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2301 gtk_widget_destroy(dialog);
2305 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2307 struct gfio_client *gc;
2309 gc = malloc(sizeof(*gc));
2310 memset(gc, 0, sizeof(*gc));
2312 gc->client = fio_get_client(client);
2316 client->client_data = gc;
2319 static GtkWidget *new_client_page(struct gui_entry *ge);
2321 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2323 struct gui_entry *ge;
2325 ge = malloc(sizeof(*ge));
2326 memset(ge, 0, sizeof(*ge));
2327 ge->state = GE_STATE_NEW;
2328 INIT_FLIST_HEAD(&ge->list);
2329 flist_add_tail(&ge->list, &ui->list);
2334 static struct gui_entry *get_new_ge_with_tab(const char *name)
2336 struct gui_entry *ge;
2338 ge = alloc_new_gui_entry(&main_ui);
2340 ge->vbox = new_client_page(ge);
2341 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2343 ge->page_label = gtk_label_new(name);
2344 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2346 gtk_widget_show_all(main_ui.window);
2350 static void file_new(GtkWidget *w, gpointer data)
2352 struct gui *ui = (struct gui *) data;
2353 struct gui_entry *ge;
2355 ge = get_new_ge_with_tab("Untitled");
2356 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2360 * Return the 'ge' corresponding to the tab. If the active tab is the
2361 * main tab, open a new tab.
2363 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2365 struct flist_head *entry;
2366 struct gui_entry *ge;
2371 return get_new_ge_with_tab("Untitled");
2377 flist_for_each(entry, &main_ui.list) {
2378 ge = flist_entry(entry, struct gui_entry, list);
2379 if (ge->page_num == cur_page)
2386 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2391 * Main tab is tab 0, so any current page other than 0 holds
2394 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2396 return get_ge_from_page(cur_page, NULL);
2401 static void file_close(GtkWidget *w, gpointer data)
2403 struct gui *ui = (struct gui *) data;
2404 struct gui_entry *ge;
2407 * Can't close the main tab
2409 ge = get_ge_from_cur_tab(ui);
2411 gtk_widget_destroy(ge->vbox);
2415 if (!flist_empty(&ui->list)) {
2416 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2423 static void file_add_recent(struct gui *ui, const gchar *uri)
2427 memset(&grd, 0, sizeof(grd));
2428 grd.display_name = strdup("gfio");
2429 grd.description = strdup("Fio job file");
2430 grd.mime_type = strdup(GFIO_MIME);
2431 grd.app_name = strdup(g_get_application_name());
2432 grd.app_exec = strdup("gfio %f/%u");
2434 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2437 static gchar *get_filename_from_uri(const gchar *uri)
2439 if (strncmp(uri, "file://", 7))
2442 return strdup(uri + 7);
2445 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2448 struct fio_client *client;
2451 filename = get_filename_from_uri(uri);
2453 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2454 ge->job_files[ge->nr_job_files] = strdup(filename);
2457 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2461 error = g_error_new(g_quark_from_string("fio"), 1,
2462 "Failed to add client %s", host);
2463 report_error(error);
2464 g_error_free(error);
2468 gfio_client_added(ge, client);
2469 file_add_recent(ge->ui, uri);
2473 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2475 int port, type, server_start;
2476 struct gui_entry *ge;
2479 int ret, ge_is_new = 0;
2482 * Creates new tab if current tab is the main window, or the
2483 * current tab already has a client.
2485 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2486 ge = get_ge_from_page(cur_page, &ge_is_new);
2488 ge = get_new_ge_with_tab("Untitled");
2492 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2494 if (get_connection_details(&host, &port, &type, &server_start)) {
2496 gtk_widget_destroy(ge->vbox);
2501 ret = do_file_open(ge, uri, host, type, port);
2507 gfio_start_server();
2510 gtk_widget_destroy(ge->vbox);
2516 static void recent_open(GtkAction *action, gpointer data)
2518 struct gui *ui = (struct gui *) data;
2519 GtkRecentInfo *info;
2522 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2523 uri = gtk_recent_info_get_uri(info);
2525 do_file_open_with_tab(ui, uri);
2528 static void file_open(GtkWidget *w, gpointer data)
2530 struct gui *ui = data;
2532 GSList *filenames, *fn_glist;
2533 GtkFileFilter *filter;
2535 dialog = gtk_file_chooser_dialog_new("Open File",
2536 GTK_WINDOW(ui->window),
2537 GTK_FILE_CHOOSER_ACTION_OPEN,
2538 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2539 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2541 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2543 filter = gtk_file_filter_new();
2544 gtk_file_filter_add_pattern(filter, "*.fio");
2545 gtk_file_filter_add_pattern(filter, "*.job");
2546 gtk_file_filter_add_pattern(filter, "*.ini");
2547 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2548 gtk_file_filter_set_name(filter, "Fio job file");
2549 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2551 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2552 gtk_widget_destroy(dialog);
2556 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2558 gtk_widget_destroy(dialog);
2560 filenames = fn_glist;
2561 while (filenames != NULL) {
2562 if (do_file_open_with_tab(ui, filenames->data))
2564 filenames = g_slist_next(filenames);
2567 g_slist_free(fn_glist);
2570 static void file_save(GtkWidget *w, gpointer data)
2572 struct gui *ui = data;
2575 dialog = gtk_file_chooser_dialog_new("Save File",
2576 GTK_WINDOW(ui->window),
2577 GTK_FILE_CHOOSER_ACTION_SAVE,
2578 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2579 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2582 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2583 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2585 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2588 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2589 // save_job_file(filename);
2592 gtk_widget_destroy(dialog);
2595 static void view_log_destroy(GtkWidget *w, gpointer data)
2597 struct gui *ui = (struct gui *) data;
2599 gtk_widget_ref(ui->log_tree);
2600 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2601 gtk_widget_destroy(w);
2602 ui->log_view = NULL;
2605 static void view_log(GtkWidget *w, gpointer data)
2607 GtkWidget *win, *scroll, *vbox, *box;
2608 struct gui *ui = (struct gui *) data;
2613 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2614 gtk_window_set_title(GTK_WINDOW(win), "Log");
2615 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2617 scroll = gtk_scrolled_window_new(NULL, NULL);
2619 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2621 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2623 box = gtk_hbox_new(TRUE, 0);
2624 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2625 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2626 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2628 vbox = gtk_vbox_new(TRUE, 5);
2629 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2631 gtk_container_add(GTK_CONTAINER(win), vbox);
2632 gtk_widget_show_all(win);
2635 static void connect_job_entry(GtkWidget *w, gpointer data)
2637 struct gui *ui = (struct gui *) data;
2638 struct gui_entry *ge;
2640 ge = get_ge_from_cur_tab(ui);
2642 connect_clicked(w, ge);
2645 static void send_job_entry(GtkWidget *w, gpointer data)
2647 struct gui *ui = (struct gui *) data;
2648 struct gui_entry *ge;
2650 ge = get_ge_from_cur_tab(ui);
2652 send_clicked(w, ge);
2656 static void edit_job_entry(GtkWidget *w, gpointer data)
2660 static void start_job_entry(GtkWidget *w, gpointer data)
2662 struct gui *ui = (struct gui *) data;
2663 struct gui_entry *ge;
2665 ge = get_ge_from_cur_tab(ui);
2667 start_job_clicked(w, ge);
2670 static void view_results(GtkWidget *w, gpointer data)
2672 struct gui *ui = (struct gui *) data;
2673 struct gfio_client *gc;
2674 struct gui_entry *ge;
2676 ge = get_ge_from_cur_tab(ui);
2680 if (ge->results_window)
2684 if (gc && gc->nr_results)
2685 gfio_display_end_results(gc);
2688 static void __update_graph_limits(struct gfio_graphs *g)
2690 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2691 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2694 static void update_graph_limits(void)
2696 struct flist_head *entry;
2697 struct gui_entry *ge;
2699 __update_graph_limits(&main_ui.graphs);
2701 flist_for_each(entry, &main_ui.list) {
2702 ge = flist_entry(entry, struct gui_entry, list);
2703 __update_graph_limits(&ge->graphs);
2707 static void preferences(GtkWidget *w, gpointer data)
2709 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2710 GtkWidget *hbox, *spin, *entry, *spin_int;
2713 dialog = gtk_dialog_new_with_buttons("Preferences",
2714 GTK_WINDOW(main_ui.window),
2715 GTK_DIALOG_DESTROY_WITH_PARENT,
2716 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2717 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2720 frame = gtk_frame_new("Graphing");
2721 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2722 vbox = gtk_vbox_new(FALSE, 6);
2723 gtk_container_add(GTK_CONTAINER(frame), vbox);
2725 hbox = gtk_hbox_new(FALSE, 5);
2726 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2727 entry = gtk_label_new("Font face to use for graph labels");
2728 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2730 font = gtk_font_button_new();
2731 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2733 box = gtk_vbox_new(FALSE, 6);
2734 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2736 hbox = gtk_hbox_new(FALSE, 5);
2737 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2738 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2739 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2741 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2743 box = gtk_vbox_new(FALSE, 6);
2744 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2746 hbox = gtk_hbox_new(FALSE, 5);
2747 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2748 entry = gtk_label_new("Client ETA request interval (msec)");
2749 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2751 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2752 frame = gtk_frame_new("Debug logging");
2753 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2754 vbox = gtk_vbox_new(FALSE, 6);
2755 gtk_container_add(GTK_CONTAINER(frame), vbox);
2757 box = gtk_hbox_new(FALSE, 6);
2758 gtk_container_add(GTK_CONTAINER(vbox), box);
2760 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2762 for (i = 0; i < FD_DEBUG_MAX; i++) {
2764 box = gtk_hbox_new(FALSE, 6);
2765 gtk_container_add(GTK_CONTAINER(vbox), box);
2769 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2770 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2771 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2774 gtk_widget_show_all(dialog);
2776 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2777 gtk_widget_destroy(dialog);
2781 for (i = 0; i < FD_DEBUG_MAX; i++) {
2784 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2786 fio_debug |= (1UL << i);
2789 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2790 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2791 update_graph_limits();
2792 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2794 gtk_widget_destroy(dialog);
2797 static void about_dialog(GtkWidget *w, gpointer data)
2799 const char *authors[] = {
2800 "Jens Axboe <axboe@kernel.dk>",
2801 "Stephen Carmeron <stephenmcameron@gmail.com>",
2804 const char *license[] = {
2805 "Fio is free software; you can redistribute it and/or modify "
2806 "it under the terms of the GNU General Public License as published by "
2807 "the Free Software Foundation; either version 2 of the License, or "
2808 "(at your option) any later version.\n",
2809 "Fio is distributed in the hope that it will be useful, "
2810 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2811 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2812 "GNU General Public License for more details.\n",
2813 "You should have received a copy of the GNU General Public License "
2814 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2815 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2817 char *license_trans;
2819 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2820 license[2], "\n", NULL);
2822 gtk_show_about_dialog(NULL,
2823 "program-name", "gfio",
2824 "comments", "Gtk2 UI for fio",
2825 "license", license_trans,
2826 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2828 "version", fio_version_string,
2829 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2830 "logo-icon-name", "fio",
2832 "wrap-license", TRUE,
2835 g_free(license_trans);
2838 static GtkActionEntry menu_items[] = {
2839 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2840 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2841 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2842 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2843 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2844 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2845 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2846 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2847 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2848 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2849 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2850 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2851 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2852 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2853 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2854 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2855 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2857 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2859 static const gchar *ui_string = " \
2861 <menubar name=\"MainMenu\"> \
2862 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2863 <menuitem name=\"New\" action=\"NewFile\" /> \
2864 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2865 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2866 <separator name=\"Separator1\"/> \
2867 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2868 <separator name=\"Separator2\"/> \
2869 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2870 <separator name=\"Separator3\"/> \
2871 <placeholder name=\"FileRecentFiles\"/> \
2872 <separator name=\"Separator4\"/> \
2873 <menuitem name=\"Quit\" action=\"Quit\" /> \
2875 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2876 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2877 <separator name=\"Separator5\"/> \
2878 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2879 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2880 <separator name=\"Separator6\"/> \
2881 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2883 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2884 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2885 <separator name=\"Separator7\"/> \
2886 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2888 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2889 <menuitem name=\"About\" action=\"About\" /> \
2895 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2898 GtkActionGroup *action_group;
2901 action_group = gtk_action_group_new("Menu");
2902 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2904 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2905 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2907 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2909 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2912 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2913 GtkWidget *vbox, GtkUIManager *ui_manager)
2915 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2918 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2920 struct gui_entry *ge = (struct gui_entry *) data;
2923 index = gtk_combo_box_get_active(box);
2925 multitext_set_entry(&ge->eta.iotype, index);
2926 multitext_set_entry(&ge->eta.ioengine, index);
2927 multitext_set_entry(&ge->eta.iodepth, index);
2930 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2932 struct gui_entry *ge = (struct gui_entry *) data;
2934 multitext_free(&ge->eta.iotype);
2935 multitext_free(&ge->eta.ioengine);
2936 multitext_free(&ge->eta.iodepth);
2939 static GtkWidget *new_client_page(struct gui_entry *ge)
2941 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2942 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2944 main_vbox = gtk_vbox_new(FALSE, 3);
2946 top_align = gtk_alignment_new(0, 0, 1, 0);
2947 top_vbox = gtk_vbox_new(FALSE, 3);
2948 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2949 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2951 probe = gtk_frame_new("Job");
2952 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2953 probe_frame = gtk_vbox_new(FALSE, 3);
2954 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2956 probe_box = gtk_hbox_new(FALSE, 3);
2957 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2958 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2959 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2960 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2961 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2963 probe_box = gtk_hbox_new(FALSE, 3);
2964 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2966 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2967 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2968 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2969 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2970 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2971 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2972 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2973 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2975 probe_box = gtk_hbox_new(FALSE, 3);
2976 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2977 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2978 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2979 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2980 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2983 * Only add this if we have a commit rate
2986 probe_box = gtk_hbox_new(FALSE, 3);
2987 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2989 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2990 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2992 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2993 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2997 * Set up a drawing area and IOPS and bandwidth graphs
2999 ge->graphs.drawing_area = gtk_drawing_area_new();
3000 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
3001 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3002 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3003 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3004 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3005 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3006 G_CALLBACK(on_config_drawing_area), &ge->graphs);
3007 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3008 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3009 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3010 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3011 ge->graphs.drawing_area);
3012 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
3014 setup_graphs(&ge->graphs);
3017 * Set up alignments for widgets at the bottom of ui,
3018 * align bottom left, expand horizontally but not vertically
3020 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3021 ge->buttonbox = gtk_hbox_new(FALSE, 0);
3022 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3023 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3025 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3028 * Set up thread status progress bar
3030 ge->thread_status_pb = gtk_progress_bar_new();
3031 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3032 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3033 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3039 static GtkWidget *new_main_page(struct gui *ui)
3041 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
3042 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
3044 main_vbox = gtk_vbox_new(FALSE, 3);
3047 * Set up alignments for widgets at the top of ui,
3048 * align top left, expand horizontally but not vertically
3050 top_align = gtk_alignment_new(0, 0, 1, 0);
3051 top_vbox = gtk_vbox_new(FALSE, 0);
3052 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3053 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3055 probe = gtk_frame_new("Run statistics");
3056 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3057 probe_frame = gtk_vbox_new(FALSE, 3);
3058 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3060 probe_box = gtk_hbox_new(FALSE, 3);
3061 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3062 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3063 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3064 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3065 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3066 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3069 * Only add this if we have a commit rate
3072 probe_box = gtk_hbox_new(FALSE, 3);
3073 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3075 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3076 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3078 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3079 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3083 * Set up a drawing area and IOPS and bandwidth graphs
3085 ui->graphs.drawing_area = gtk_drawing_area_new();
3086 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3087 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3088 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3089 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3090 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3091 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3092 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3093 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3094 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3095 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3096 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3097 ui->graphs.drawing_area);
3098 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3101 setup_graphs(&ui->graphs);
3104 * Set up alignments for widgets at the bottom of ui,
3105 * align bottom left, expand horizontally but not vertically
3107 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3108 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3109 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3110 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3113 * Set up thread status progress bar
3115 ui->thread_status_pb = gtk_progress_bar_new();
3116 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3117 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3118 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3123 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3124 guint page, gpointer data)
3127 struct gui *ui = (struct gui *) data;
3128 struct gui_entry *ge;
3131 set_job_menu_visible(ui, 0);
3132 set_view_results_visible(ui, 0);
3136 set_job_menu_visible(ui, 1);
3137 ge = get_ge_from_page(page, NULL);
3139 update_button_states(ui, ge);
3144 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3146 time_t time_a = gtk_recent_info_get_visited(a);
3147 time_t time_b = gtk_recent_info_get_visited(b);
3149 return time_b - time_a;
3152 static void add_recent_file_items(struct gui *ui)
3154 const gchar *gfio = g_get_application_name();
3155 GList *items, *item;
3158 if (ui->recent_ui_id) {
3159 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3160 gtk_ui_manager_ensure_update(ui->uimanager);
3162 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3164 if (ui->actiongroup) {
3165 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3166 g_object_unref(ui->actiongroup);
3168 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3170 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3172 items = gtk_recent_manager_get_items(ui->recentmanager);
3173 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3175 for (item = items; item && item->data; item = g_list_next(item)) {
3176 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3181 if (!gtk_recent_info_has_application(info, gfio))
3185 * We only support local files for now
3187 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3190 action_name = g_strdup_printf("RecentFile%u", i++);
3191 label = gtk_recent_info_get_display_name(info);
3193 action = g_object_new(GTK_TYPE_ACTION,
3194 "name", action_name,
3195 "label", label, NULL);
3197 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3198 gtk_recent_info_ref(info),
3199 (GDestroyNotify) gtk_recent_info_unref);
3202 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3204 gtk_action_group_add_action(ui->actiongroup, action);
3205 g_object_unref(action);
3207 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3208 "/MainMenu/FileMenu/FileRecentFiles",
3210 GTK_UI_MANAGER_MENUITEM, FALSE);
3212 g_free(action_name);
3218 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3222 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3223 gint x, gint y, GtkSelectionData *data,
3224 guint info, guint time)
3226 struct gui *ui = &main_ui;
3231 source = gtk_drag_get_source_widget(ctx);
3232 if (source && widget == gtk_widget_get_toplevel(source)) {
3233 gtk_drag_finish(ctx, FALSE, FALSE, time);
3237 uris = gtk_selection_data_get_uris(data);
3239 gtk_drag_finish(ctx, FALSE, FALSE, time);
3245 if (do_file_open_with_tab(ui, uris[i]))
3250 gtk_drag_finish(ctx, TRUE, FALSE, time);
3254 static void init_ui(int *argc, char **argv[], struct gui *ui)
3256 GtkSettings *settings;
3259 /* Magical g*thread incantation, you just need this thread stuff.
3260 * Without it, the update that happens in gfio_update_thread_status
3261 * doesn't really happen in a timely fashion, you need expose events
3263 if (!g_thread_supported())
3264 g_thread_init(NULL);
3267 gtk_init(argc, argv);
3268 settings = gtk_settings_get_default();
3269 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3271 gdk_color_parse("white", &white);
3273 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3274 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3275 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3277 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3278 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3280 ui->vbox = gtk_vbox_new(FALSE, 0);
3281 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3283 ui->uimanager = gtk_ui_manager_new();
3284 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3285 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3287 ui->recentmanager = gtk_recent_manager_get_default();
3288 add_recent_file_items(ui);
3290 ui->notebook = gtk_notebook_new();
3291 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3292 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3293 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3294 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3296 vbox = new_main_page(ui);
3297 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3298 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3299 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3301 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3303 gfio_ui_setup_log(ui);
3305 gtk_widget_show_all(ui->window);
3308 int main(int argc, char *argv[], char *envp[])
3310 if (initialize_fio(envp))
3312 if (fio_init_options())
3315 memset(&main_ui, 0, sizeof(main_ui));
3316 INIT_FLIST_HEAD(&main_ui.list);
3318 init_ui(&argc, &argv, &main_ui);
3320 gdk_threads_enter();
3322 gdk_threads_leave();