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
38 static int gfio_server_running;
39 static const char *gfio_graph_font;
40 static unsigned int gfio_graph_limit = 100;
41 static GdkColor white;
43 static void view_log(GtkWidget *w, gpointer data);
45 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
47 static void connect_clicked(GtkWidget *widget, gpointer data);
48 static void start_job_clicked(GtkWidget *widget, gpointer data);
49 static void send_clicked(GtkWidget *widget, gpointer data);
51 static struct button_spec {
52 const char *buttontext;
54 const char *tooltiptext[2];
55 const int start_sensitive;
56 } buttonspeclist[] = {
58 .buttontext = "Connect",
60 .tooltiptext = { "Disconnect from host", "Connect to host" },
66 .tooltiptext = { "Send job description to host", NULL },
70 .buttontext = "Start Job",
71 .f = start_job_clicked,
72 .tooltiptext = { "Start the current job on the server", NULL },
77 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
78 static void gfio_update_thread_status_all(char *status_message, double perc);
79 void report_error(GError *error);
81 static struct graph *setup_iops_graph(void)
85 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
86 graph_title(g, "IOPS (IOs/sec)");
87 graph_x_title(g, "Time (secs)");
88 graph_add_label(g, "Read IOPS");
89 graph_add_label(g, "Write IOPS");
90 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
91 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
92 line_graph_set_data_count_limit(g, gfio_graph_limit);
93 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
97 static struct graph *setup_bandwidth_graph(void)
101 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
102 graph_title(g, "Bandwidth (bytes/sec)");
103 graph_x_title(g, "Time (secs)");
104 graph_add_label(g, "Read Bandwidth");
105 graph_add_label(g, "Write Bandwidth");
106 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
107 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
108 graph_set_base_offset(g, 1);
109 line_graph_set_data_count_limit(g, 100);
110 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
114 static void setup_graphs(struct gfio_graphs *g)
116 g->iops_graph = setup_iops_graph();
117 g->bandwidth_graph = setup_bandwidth_graph();
120 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
122 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
123 mt->text[mt->max_text] = strdup(text);
127 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
129 if (index >= mt->max_text)
131 if (!mt->text || !mt->text[index])
134 mt->cur_text = index;
135 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
138 static void multitext_update_entry(struct multitext_widget *mt,
139 unsigned int index, const char *text)
145 free(mt->text[index]);
147 mt->text[index] = strdup(text);
148 if (mt->cur_text == index)
149 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
152 static void multitext_free(struct multitext_widget *mt)
156 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
158 for (i = 0; i < mt->max_text; i++) {
168 static void clear_ge_ui_info(struct gui_entry *ge)
170 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
171 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
172 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
173 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
175 /* should we empty it... */
176 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
178 multitext_update_entry(&ge->eta.iotype, 0, "");
179 multitext_update_entry(&ge->eta.bs, 0, "");
180 multitext_update_entry(&ge->eta.ioengine, 0, "");
181 multitext_update_entry(&ge->eta.iodepth, 0, "");
182 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
183 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
185 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
187 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
190 static void show_info_dialog(struct gui *ui, const char *title,
193 GtkWidget *dialog, *content, *label;
195 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
196 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
197 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
199 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
200 label = gtk_label_new(message);
201 gtk_container_add(GTK_CONTAINER(content), label);
202 gtk_widget_show_all(dialog);
203 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
204 gtk_dialog_run(GTK_DIALOG(dialog));
205 gtk_widget_destroy(dialog);
208 static void set_menu_entry_text(struct gui *ui, const char *path,
213 w = gtk_ui_manager_get_widget(ui->uimanager, path);
215 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
217 fprintf(stderr, "gfio: can't find path %s\n", path);
221 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
225 w = gtk_ui_manager_get_widget(ui->uimanager, path);
227 gtk_widget_set_sensitive(w, show);
229 fprintf(stderr, "gfio: can't find path %s\n", path);
232 static void set_job_menu_visible(struct gui *ui, int visible)
234 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
237 static void set_view_results_visible(struct gui *ui, int visible)
239 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
242 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
244 if (s->tooltiptext[sensitive])
245 return s->tooltiptext[sensitive];
247 return s->tooltiptext[0];
250 static GtkWidget *add_button(GtkWidget *buttonbox,
251 struct button_spec *buttonspec, gpointer data)
253 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
254 gboolean sens = buttonspec->start_sensitive;
256 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
257 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
259 sens = buttonspec->start_sensitive;
260 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
261 gtk_widget_set_sensitive(button, sens);
266 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
271 for (i = 0; i < nbuttons; i++)
272 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
276 * Update sensitivity of job buttons and job menu items, based on the
277 * state of the client.
279 static void update_button_states(struct gui *ui, struct gui_entry *ge)
281 unsigned int connect_state, send_state, start_state, edit_state;
282 const char *connect_str = NULL;
288 sprintf(tmp, "Bad client state: %u\n", ge->state);
289 show_info_dialog(ui, "Error", tmp);
290 /* fall through to new state */
296 connect_str = "Connect";
300 case GE_STATE_CONNECTED:
303 connect_str = "Disconnect";
307 case GE_STATE_JOB_SENT:
310 connect_str = "Disconnect";
314 case GE_STATE_JOB_STARTED:
317 connect_str = "Disconnect";
321 case GE_STATE_JOB_RUNNING:
324 connect_str = "Disconnect";
328 case GE_STATE_JOB_DONE:
331 connect_str = "Connect";
337 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
338 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
339 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
340 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
341 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
343 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
344 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
346 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
347 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
348 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
350 if (ge->client && ge->client->nr_results)
351 set_view_results_visible(ui, 1);
353 set_view_results_visible(ui, 0);
356 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
359 update_button_states(ge->ui, ge);
363 #define ALIGN_RIGHT 2
367 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
369 GtkCellRenderer *renderer;
370 GtkTreeViewColumn *col;
371 double xalign = 0.0; /* left as default */
372 PangoAlignment align;
375 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
376 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
378 visible = !(flags & INVISIBLE);
380 renderer = gtk_cell_renderer_text_new();
381 col = gtk_tree_view_column_new();
383 gtk_tree_view_column_set_title(col, title);
384 if (!(flags & UNSORTABLE))
385 gtk_tree_view_column_set_sort_column_id(col, index);
386 gtk_tree_view_column_set_resizable(col, TRUE);
387 gtk_tree_view_column_pack_start(col, renderer, TRUE);
388 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
389 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
391 case PANGO_ALIGN_LEFT:
394 case PANGO_ALIGN_CENTER:
397 case PANGO_ALIGN_RIGHT:
401 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
402 gtk_tree_view_column_set_visible(col, visible);
403 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
407 static void gfio_ui_setup_log(struct gui *ui)
409 GtkTreeSelection *selection;
411 GtkWidget *tree_view;
413 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
415 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
416 gtk_widget_set_can_focus(tree_view, FALSE);
418 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
419 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
420 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
421 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
423 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
424 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
425 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
426 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
428 ui->log_model = model;
429 ui->log_tree = tree_view;
432 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
435 double xdim, double ydim)
440 g = graph_new(xdim, ydim, gfio_graph_font);
441 graph_title(g, title);
442 graph_x_title(g, "Percentile");
444 for (i = 0; i < len; i++) {
447 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
448 graph_add_label(g, fbuf);
449 graph_add_data(g, fbuf, (double) ovals[i]);
455 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
461 GType types[FIO_IO_U_LIST_MAX_LEN];
462 GtkWidget *tree_view;
463 GtkTreeSelection *selection;
468 for (i = 0; i < len; i++)
469 types[i] = G_TYPE_INT;
471 model = gtk_list_store_newv(len, types);
473 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
474 gtk_widget_set_can_focus(tree_view, FALSE);
476 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
477 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
479 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
480 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
482 for (i = 0; i < len; i++) {
485 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
486 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
489 gtk_list_store_append(model, &iter);
491 for (i = 0; i < len; i++) {
493 ovals[i] = (ovals[i] + 999) / 1000;
494 gtk_list_store_set(model, &iter, i, ovals[i], -1);
500 static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
505 cr = gdk_cairo_create(w->window);
507 if (graph_has_tooltips(g)) {
508 g_object_set(w, "has-tooltip", TRUE, NULL);
509 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
512 cairo_set_source_rgb(cr, 0, 0, 0);
513 bar_graph_draw(g, cr);
519 static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
522 struct graph *g = data;
524 graph_set_size(g, w->allocation.width, w->allocation.height);
525 graph_set_size(g, w->allocation.width, w->allocation.height);
526 graph_set_position(g, 0, 0);
530 static void gfio_show_clat_percentiles(struct gfio_client *gc,
531 GtkWidget *vbox, struct thread_stat *ts,
534 unsigned int *io_u_plat = ts->io_u_plat[ddir];
535 unsigned long nr = ts->clat_stat[ddir].samples;
536 fio_fp64_t *plist = ts->percentile_list;
537 unsigned int *ovals, len, minv, maxv, scale_down;
539 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
540 struct gui_entry *ge = gc->ge;
543 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
548 * We default to usecs, but if the value range is such that we
549 * should scale down to msecs, do that.
551 if (minv > 2000 && maxv > 99999) {
559 sprintf(tmp, "Completion percentiles (%s)", base);
560 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
561 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
563 frame = gtk_frame_new(tmp);
564 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
566 completion_vbox = gtk_vbox_new(FALSE, 3);
567 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
568 hbox = gtk_hbox_new(FALSE, 3);
569 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
570 drawing_area = gtk_drawing_area_new();
571 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
572 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
573 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
574 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
575 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
577 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
583 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
584 unsigned long max, double mean, double dev)
586 const char *base = "(usec)";
587 GtkWidget *hbox, *label, *frame;
591 if (!usec_to_msec(&min, &max, &mean, &dev))
594 minp = num2str(min, 6, 1, 0);
595 maxp = num2str(max, 6, 1, 0);
597 sprintf(tmp, "%s %s", name, base);
598 frame = gtk_frame_new(tmp);
599 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
601 hbox = gtk_hbox_new(FALSE, 3);
602 gtk_container_add(GTK_CONTAINER(frame), hbox);
604 label = new_info_label_in_frame(hbox, "Minimum");
605 gtk_label_set_text(GTK_LABEL(label), minp);
606 label = new_info_label_in_frame(hbox, "Maximum");
607 gtk_label_set_text(GTK_LABEL(label), maxp);
608 label = new_info_label_in_frame(hbox, "Average");
609 sprintf(tmp, "%5.02f", mean);
610 gtk_label_set_text(GTK_LABEL(label), tmp);
611 label = new_info_label_in_frame(hbox, "Standard deviation");
612 sprintf(tmp, "%5.02f", dev);
613 gtk_label_set_text(GTK_LABEL(label), tmp);
624 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
625 struct group_run_stats *rs,
626 struct thread_stat *ts, int ddir)
628 const char *ddir_label[2] = { "Read", "Write" };
629 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
630 unsigned long min[3], max[3], runt;
631 unsigned long long bw, iops;
632 unsigned int flags = 0;
633 double mean[3], dev[3];
634 char *io_p, *bw_p, *iops_p;
637 if (!ts->runtime[ddir])
640 i2p = is_power_of_2(rs->kb_base);
641 runt = ts->runtime[ddir];
643 bw = (1000 * ts->io_bytes[ddir]) / runt;
644 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
645 bw_p = num2str(bw, 6, 1, i2p);
647 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
648 iops_p = num2str(iops, 6, 1, 0);
650 box = gtk_hbox_new(FALSE, 3);
651 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
653 frame = gtk_frame_new(ddir_label[ddir]);
654 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
656 main_vbox = gtk_vbox_new(FALSE, 3);
657 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
659 box = gtk_hbox_new(FALSE, 3);
660 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
662 label = new_info_label_in_frame(box, "IO");
663 gtk_label_set_text(GTK_LABEL(label), io_p);
664 label = new_info_label_in_frame(box, "Bandwidth");
665 gtk_label_set_text(GTK_LABEL(label), bw_p);
666 label = new_info_label_in_frame(box, "IOPS");
667 gtk_label_set_text(GTK_LABEL(label), iops_p);
668 label = new_info_label_in_frame(box, "Runtime (msec)");
669 label_set_int_value(label, ts->runtime[ddir]);
671 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
672 double p_of_agg = 100.0;
673 const char *bw_str = "KB";
677 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
678 if (p_of_agg > 100.0)
682 if (mean[0] > 999999.9) {
690 sprintf(tmp, "Bandwidth (%s)", bw_str);
691 frame = gtk_frame_new(tmp);
692 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
694 box = gtk_hbox_new(FALSE, 3);
695 gtk_container_add(GTK_CONTAINER(frame), box);
697 label = new_info_label_in_frame(box, "Minimum");
698 label_set_int_value(label, min[0]);
699 label = new_info_label_in_frame(box, "Maximum");
700 label_set_int_value(label, max[0]);
701 label = new_info_label_in_frame(box, "Percentage of jobs");
702 sprintf(tmp, "%3.2f%%", p_of_agg);
703 gtk_label_set_text(GTK_LABEL(label), tmp);
704 label = new_info_label_in_frame(box, "Average");
705 sprintf(tmp, "%5.02f", mean[0]);
706 gtk_label_set_text(GTK_LABEL(label), tmp);
707 label = new_info_label_in_frame(box, "Standard deviation");
708 sprintf(tmp, "%5.02f", dev[0]);
709 gtk_label_set_text(GTK_LABEL(label), tmp);
712 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
714 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
716 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
720 frame = gtk_frame_new("Latency");
721 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
723 vbox = gtk_vbox_new(FALSE, 3);
724 gtk_container_add(GTK_CONTAINER(frame), vbox);
726 if (flags & GFIO_SLAT)
727 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
728 if (flags & GFIO_CLAT)
729 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
730 if (flags & GFIO_LAT)
731 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
734 if (ts->clat_percentiles)
735 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
742 static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
745 double xdim, double ydim)
750 g = graph_new(xdim, ydim, gfio_graph_font);
751 graph_title(g, title);
752 graph_x_title(g, "Buckets");
754 for (i = 0; i < len; i++) {
755 graph_add_label(g, labels[i]);
756 graph_add_data(g, labels[i], lat[i]);
762 static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
765 GtkWidget *tree_view;
766 GtkTreeSelection *selection;
772 types = malloc(num * sizeof(GType));
774 for (i = 0; i < num; i++)
775 types[i] = G_TYPE_STRING;
777 model = gtk_list_store_newv(num, types);
781 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
782 gtk_widget_set_can_focus(tree_view, FALSE);
784 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
785 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
787 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
788 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
790 for (i = 0; i < num; i++)
791 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
793 gtk_list_store_append(model, &iter);
795 for (i = 0; i < num; i++) {
799 sprintf(fbuf, "0.00");
801 sprintf(fbuf, "%3.2f%%", lat[i]);
803 gtk_list_store_set(model, &iter, i, fbuf, -1);
809 static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
810 struct thread_stat *ts)
812 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
813 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
814 "250u", "500u", "750u", "1m", "2m",
815 "4m", "10m", "20m", "50m", "100m",
816 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
818 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
819 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
820 struct gui_entry *ge = gc->ge;
822 stat_calc_lat_u(ts, io_u_lat);
823 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
826 * Found out which first bucket has entries, and which last bucket
829 for (i = 0; i < total; i++) {
830 if (io_u_lat[i] == 0.00)
844 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
845 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
847 frame = gtk_frame_new("Latency buckets");
848 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
850 completion_vbox = gtk_vbox_new(FALSE, 3);
851 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
852 hbox = gtk_hbox_new(FALSE, 3);
853 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
855 drawing_area = gtk_drawing_area_new();
856 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
857 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
858 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
859 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
860 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
862 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
865 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
867 GtkWidget *box, *frame, *entry;
868 double usr_cpu, sys_cpu;
869 unsigned long runtime;
872 runtime = ts->total_run_time;
874 double runt = (double) runtime;
876 usr_cpu = (double) ts->usr_time * 100 / runt;
877 sys_cpu = (double) ts->sys_time * 100 / runt;
883 frame = gtk_frame_new("OS resources");
884 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
886 box = gtk_hbox_new(FALSE, 3);
887 gtk_container_add(GTK_CONTAINER(frame), box);
889 entry = new_info_entry_in_frame(box, "User CPU");
890 sprintf(tmp, "%3.2f%%", usr_cpu);
891 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
892 entry = new_info_entry_in_frame(box, "System CPU");
893 sprintf(tmp, "%3.2f%%", sys_cpu);
894 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
895 entry = new_info_entry_in_frame(box, "Context switches");
896 entry_set_int_value(entry, ts->ctx);
897 entry = new_info_entry_in_frame(box, "Major faults");
898 entry_set_int_value(entry, ts->majf);
899 entry = new_info_entry_in_frame(box, "Minor faults");
900 entry_set_int_value(entry, ts->minf);
902 static void gfio_add_sc_depths_tree(GtkListStore *model,
903 struct thread_stat *ts, unsigned int len,
906 double io_u_dist[FIO_IO_U_MAP_NR];
908 /* Bits 0, and 3-8 */
909 const int add_mask = 0x1f9;
913 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
915 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
917 gtk_list_store_append(model, &iter);
919 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
921 for (i = 1, j = 0; i < len; i++) {
924 if (!(add_mask & (1UL << (i - 1))))
925 sprintf(fbuf, "0.0%%");
927 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
931 gtk_list_store_set(model, &iter, i, fbuf, -1);
936 static void gfio_add_total_depths_tree(GtkListStore *model,
937 struct thread_stat *ts, unsigned int len)
939 double io_u_dist[FIO_IO_U_MAP_NR];
941 /* Bits 1-6, and 8 */
942 const int add_mask = 0x17e;
945 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
947 gtk_list_store_append(model, &iter);
949 gtk_list_store_set(model, &iter, 0, "Total", -1);
951 for (i = 1, j = 0; i < len; i++) {
954 if (!(add_mask & (1UL << (i - 1))))
955 sprintf(fbuf, "0.0%%");
957 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
961 gtk_list_store_set(model, &iter, i, fbuf, -1);
966 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
968 GtkWidget *frame, *box, *tree_view;
969 GtkTreeSelection *selection;
971 GType types[FIO_IO_U_MAP_NR + 1];
974 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
976 frame = gtk_frame_new("IO depths");
977 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
979 box = gtk_hbox_new(FALSE, 3);
980 gtk_container_add(GTK_CONTAINER(frame), box);
982 for (i = 0; i < NR_LABELS; i++)
983 types[i] = G_TYPE_STRING;
985 model = gtk_list_store_newv(NR_LABELS, types);
987 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
988 gtk_widget_set_can_focus(tree_view, FALSE);
990 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
991 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
993 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
994 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
996 for (i = 0; i < NR_LABELS; i++)
997 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
999 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1000 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1001 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1003 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1006 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1008 struct gui_entry *ge = (struct gui_entry *) data;
1010 gtk_widget_destroy(w);
1011 ge->results_window = NULL;
1012 ge->results_notebook = NULL;
1016 static void results_close(GtkWidget *w, gpointer *data)
1018 struct gui_entry *ge = (struct gui_entry *) data;
1020 gtk_widget_destroy(ge->results_window);
1023 static GtkActionEntry results_menu_items[] = {
1024 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1025 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1026 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1028 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1030 static const gchar *results_ui_string = " \
1032 <menubar name=\"MainMenu\"> \
1033 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1034 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1036 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1042 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1044 GtkActionGroup *action_group;
1048 ge->results_uimanager = gtk_ui_manager_new();
1050 action_group = gtk_action_group_new("ResultsMenu");
1051 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1053 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1054 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1056 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1058 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1062 static GtkWidget *get_results_window(struct gui_entry *ge)
1064 GtkWidget *win, *notebook, *vbox;
1066 if (ge->results_window)
1067 return ge->results_notebook;
1069 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1070 gtk_window_set_title(GTK_WINDOW(win), "Results");
1071 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1072 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1073 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1075 vbox = gtk_vbox_new(FALSE, 0);
1076 gtk_container_add(GTK_CONTAINER(win), vbox);
1078 ge->results_menu = get_results_menubar(win, ge);
1079 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1081 notebook = gtk_notebook_new();
1082 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1083 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1084 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1086 ge->results_window = win;
1087 ge->results_notebook = notebook;
1088 return ge->results_notebook;
1091 static void disk_util_destroy(GtkWidget *w, gpointer data)
1093 struct gui_entry *ge = (struct gui_entry *) data;
1095 ge->disk_util_vbox = NULL;
1096 gtk_widget_destroy(w);
1099 static GtkWidget *get_scrolled_window(gint border_width)
1103 scroll = gtk_scrolled_window_new(NULL, NULL);
1104 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1105 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1110 static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1112 GtkWidget *vbox, *box, *scroll, *res_notebook;
1114 if (ge->disk_util_vbox)
1115 return ge->disk_util_vbox;
1117 scroll = get_scrolled_window(5);
1118 vbox = gtk_vbox_new(FALSE, 3);
1119 box = gtk_hbox_new(FALSE, 0);
1120 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1122 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1123 res_notebook = get_results_window(ge);
1125 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1126 ge->disk_util_vbox = box;
1127 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1129 return ge->disk_util_vbox;
1132 static int __gfio_disk_util_show(GtkWidget *res_notebook,
1133 struct gfio_client *gc, struct cmd_du_pdu *p)
1135 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
1136 struct gui_entry *ge = gc->ge;
1140 util_vbox = gfio_disk_util_get_vbox(ge);
1142 vbox = gtk_vbox_new(FALSE, 3);
1143 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
1145 frame = gtk_frame_new((char *) p->dus.name);
1146 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1148 box = gtk_vbox_new(FALSE, 3);
1149 gtk_container_add(GTK_CONTAINER(frame), box);
1151 frame = gtk_frame_new("Read");
1152 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1153 vbox = gtk_hbox_new(TRUE, 3);
1154 gtk_container_add(GTK_CONTAINER(frame), vbox);
1155 entry = new_info_entry_in_frame(vbox, "IOs");
1156 entry_set_int_value(entry, p->dus.ios[0]);
1157 entry = new_info_entry_in_frame(vbox, "Merges");
1158 entry_set_int_value(entry, p->dus.merges[0]);
1159 entry = new_info_entry_in_frame(vbox, "Sectors");
1160 entry_set_int_value(entry, p->dus.sectors[0]);
1161 entry = new_info_entry_in_frame(vbox, "Ticks");
1162 entry_set_int_value(entry, p->dus.ticks[0]);
1164 frame = gtk_frame_new("Write");
1165 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1166 vbox = gtk_hbox_new(TRUE, 3);
1167 gtk_container_add(GTK_CONTAINER(frame), vbox);
1168 entry = new_info_entry_in_frame(vbox, "IOs");
1169 entry_set_int_value(entry, p->dus.ios[1]);
1170 entry = new_info_entry_in_frame(vbox, "Merges");
1171 entry_set_int_value(entry, p->dus.merges[1]);
1172 entry = new_info_entry_in_frame(vbox, "Sectors");
1173 entry_set_int_value(entry, p->dus.sectors[1]);
1174 entry = new_info_entry_in_frame(vbox, "Ticks");
1175 entry_set_int_value(entry, p->dus.ticks[1]);
1177 frame = gtk_frame_new("Shared");
1178 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1179 vbox = gtk_hbox_new(TRUE, 3);
1180 gtk_container_add(GTK_CONTAINER(frame), vbox);
1181 entry = new_info_entry_in_frame(vbox, "IO ticks");
1182 entry_set_int_value(entry, p->dus.io_ticks);
1183 entry = new_info_entry_in_frame(vbox, "Time in queue");
1184 entry_set_int_value(entry, p->dus.time_in_queue);
1188 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1192 sprintf(tmp, "%3.2f%%", util);
1193 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1194 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1196 gtk_widget_show_all(ge->results_window);
1200 static int gfio_disk_util_show(struct gfio_client *gc)
1202 struct gui_entry *ge = gc->ge;
1203 GtkWidget *res_notebook;
1209 res_notebook = get_results_window(ge);
1211 for (i = 0; i < gc->nr_du; i++) {
1212 struct cmd_du_pdu *p = &gc->du[i];
1214 __gfio_disk_util_show(res_notebook, gc, p);
1217 gtk_widget_show_all(ge->results_window);
1221 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1222 struct group_run_stats *rs)
1224 unsigned int nr = gc->nr_results;
1226 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1227 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1228 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1232 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1233 struct thread_stat *ts,
1234 struct group_run_stats *rs)
1236 GtkWidget *box, *vbox, *entry, *scroll;
1238 scroll = gtk_scrolled_window_new(NULL, NULL);
1239 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1240 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1242 vbox = gtk_vbox_new(FALSE, 3);
1244 box = gtk_hbox_new(FALSE, 0);
1245 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1247 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1249 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1251 entry = new_info_entry_in_frame(box, "Name");
1252 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1253 if (strlen(ts->description)) {
1254 entry = new_info_entry_in_frame(box, "Description");
1255 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1257 entry = new_info_entry_in_frame(box, "Group ID");
1258 entry_set_int_value(entry, ts->groupid);
1259 entry = new_info_entry_in_frame(box, "Jobs");
1260 entry_set_int_value(entry, ts->members);
1261 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1262 entry_set_int_value(entry, ts->error);
1263 entry = new_info_entry_in_frame(box, "PID");
1264 entry_set_int_value(entry, ts->pid);
1266 if (ts->io_bytes[DDIR_READ])
1267 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1268 if (ts->io_bytes[DDIR_WRITE])
1269 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1271 gfio_show_latency_buckets(gc, vbox, ts);
1272 gfio_show_cpu_usage(vbox, ts);
1273 gfio_show_io_depths(vbox, ts);
1276 static void gfio_display_end_results(struct gfio_client *gc)
1278 struct gui_entry *ge = gc->ge;
1279 GtkWidget *res_notebook;
1282 res_notebook = get_results_window(ge);
1284 for (i = 0; i < gc->nr_results; i++) {
1285 struct end_results *e = &gc->results[i];
1287 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1290 if (gfio_disk_util_show(gc))
1291 gtk_widget_show_all(ge->results_window);
1294 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1295 struct group_run_stats *rs)
1297 struct gfio_client *gc = client->client_data;
1298 struct gui_entry *ge = gc->ge;
1300 gfio_add_end_results(gc, ts, rs);
1302 gdk_threads_enter();
1303 if (ge->results_window)
1304 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1306 gfio_display_end_results(gc);
1307 gdk_threads_leave();
1310 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1312 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1313 struct gui *ui = &main_ui;
1317 char tmp[64], timebuf[80];
1320 tm = localtime(&sec);
1321 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1322 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1324 gdk_threads_enter();
1326 gtk_list_store_append(ui->log_model, &iter);
1327 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1328 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1329 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1330 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1332 if (p->level == FIO_LOG_ERR)
1333 view_log(NULL, (gpointer) ui);
1335 gdk_threads_leave();
1338 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1340 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1341 struct gfio_client *gc = client->client_data;
1342 struct gui_entry *ge = gc->ge;
1343 unsigned int nr = gc->nr_du;
1345 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1346 memcpy(&gc->du[nr], p, sizeof(*p));
1349 gdk_threads_enter();
1350 if (ge->results_window)
1351 __gfio_disk_util_show(ge->results_notebook, gc, p);
1353 gfio_disk_util_show(gc);
1354 gdk_threads_leave();
1357 extern int sum_stat_clients;
1358 extern struct thread_stat client_ts;
1359 extern struct group_run_stats client_gs;
1361 static int sum_stat_nr;
1363 static void gfio_thread_status_op(struct fio_client *client,
1364 struct fio_net_cmd *cmd)
1366 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1368 gfio_display_ts(client, &p->ts, &p->rs);
1370 if (sum_stat_clients == 1)
1373 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1374 sum_group_stats(&client_gs, &p->rs);
1376 client_ts.members++;
1377 client_ts.thread_number = p->ts.thread_number;
1378 client_ts.groupid = p->ts.groupid;
1380 if (++sum_stat_nr == sum_stat_clients) {
1381 strcpy(client_ts.name, "All clients");
1382 gfio_display_ts(client, &client_ts, &client_gs);
1386 static void gfio_group_stats_op(struct fio_client *client,
1387 struct fio_net_cmd *cmd)
1389 /* We're ignoring group stats for now */
1392 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1395 struct gfio_graphs *g = data;
1397 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1398 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1399 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1400 graph_set_position(g->bandwidth_graph, 0, 0);
1404 static void draw_graph(struct graph *g, cairo_t *cr)
1406 line_graph_draw(g, cr);
1410 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1411 gboolean keyboard_mode, GtkTooltip *tooltip,
1414 struct gfio_graphs *g = data;
1415 const char *text = NULL;
1417 if (graph_contains_xy(g->iops_graph, x, y))
1418 text = graph_find_tooltip(g->iops_graph, x, y);
1419 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1420 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1423 gtk_tooltip_set_text(tooltip, text);
1430 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1432 struct gfio_graphs *g = p;
1435 cr = gdk_cairo_create(w->window);
1437 if (graph_has_tooltips(g->iops_graph) ||
1438 graph_has_tooltips(g->bandwidth_graph)) {
1439 g_object_set(w, "has-tooltip", TRUE, NULL);
1440 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1443 cairo_set_source_rgb(cr, 0, 0, 0);
1444 draw_graph(g->iops_graph, cr);
1445 draw_graph(g->bandwidth_graph, cr);
1452 * Client specific ETA
1454 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1456 struct gfio_client *gc = client->client_data;
1457 struct gui_entry *ge = gc->ge;
1458 static int eta_good;
1465 gdk_threads_enter();
1470 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1471 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1472 eta_to_str(eta_str, je->eta_sec);
1475 sprintf(tmp, "%u", je->nr_running);
1476 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1477 sprintf(tmp, "%u", je->files_open);
1478 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1481 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1482 if (je->m_rate || je->t_rate) {
1485 mr = num2str(je->m_rate, 4, 0, i2p);
1486 tr = num2str(je->t_rate, 4, 0, i2p);
1487 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1488 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1491 } else if (je->m_iops || je->t_iops)
1492 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1494 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1495 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1496 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1497 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1500 if (je->eta_sec != INT_MAX && je->nr_running) {
1504 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1505 strcpy(output, "-.-% done");
1509 sprintf(output, "%3.1f%% done", perc);
1512 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1513 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1515 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1516 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1518 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1519 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1520 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1521 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1523 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1524 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1525 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1526 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1535 char *dst = output + strlen(output);
1537 sprintf(dst, " - %s", eta_str);
1540 gfio_update_thread_status(ge, output, perc);
1541 gdk_threads_leave();
1545 * Update ETA in main window for all clients
1547 static void gfio_update_all_eta(struct jobs_eta *je)
1549 struct gui *ui = &main_ui;
1550 static int eta_good;
1556 gdk_threads_enter();
1561 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1562 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1563 eta_to_str(eta_str, je->eta_sec);
1567 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1568 if (je->m_rate || je->t_rate) {
1571 mr = num2str(je->m_rate, 4, 0, i2p);
1572 tr = num2str(je->t_rate, 4, 0, i2p);
1573 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1574 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1577 } else if (je->m_iops || je->t_iops)
1578 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1580 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1581 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1582 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1583 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1586 entry_set_int_value(ui->eta.jobs, je->nr_running);
1588 if (je->eta_sec != INT_MAX && je->nr_running) {
1592 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1593 strcpy(output, "-.-% done");
1597 sprintf(output, "%3.1f%% done", perc);
1600 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1601 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1603 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1604 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1606 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1607 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1608 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1609 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1611 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1612 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1613 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1614 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1623 char *dst = output + strlen(output);
1625 sprintf(dst, " - %s", eta_str);
1628 gfio_update_thread_status_all(output, perc);
1629 gdk_threads_leave();
1632 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1634 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1635 struct gfio_client *gc = client->client_data;
1636 struct gui_entry *ge = gc->ge;
1637 const char *os, *arch;
1640 os = fio_get_os_string(probe->os);
1644 arch = fio_get_arch_string(probe->arch);
1649 client->name = strdup((char *) probe->hostname);
1651 gdk_threads_enter();
1653 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1654 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1655 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1656 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1657 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1659 gfio_set_state(ge, GE_STATE_CONNECTED);
1661 gdk_threads_leave();
1664 static void gfio_update_thread_status(struct gui_entry *ge,
1665 char *status_message, double perc)
1667 static char message[100];
1668 const char *m = message;
1670 strncpy(message, status_message, sizeof(message) - 1);
1671 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1672 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1673 gtk_widget_queue_draw(main_ui.window);
1676 static void gfio_update_thread_status_all(char *status_message, double perc)
1678 struct gui *ui = &main_ui;
1679 static char message[100];
1680 const char *m = message;
1682 strncpy(message, status_message, sizeof(message) - 1);
1683 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1684 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1685 gtk_widget_queue_draw(ui->window);
1688 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1690 struct gfio_client *gc = client->client_data;
1692 gdk_threads_enter();
1693 gfio_set_state(gc->ge, GE_STATE_NEW);
1694 gdk_threads_leave();
1697 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1699 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1700 struct gfio_client *gc = client->client_data;
1701 struct thread_options *o = &gc->o;
1702 struct gui_entry *ge = gc->ge;
1703 char *c1, *c2, *c3, *c4;
1706 p->thread_number = le32_to_cpu(p->thread_number);
1707 p->groupid = le32_to_cpu(p->groupid);
1708 convert_thread_options_to_cpu(o, &p->top);
1710 gdk_threads_enter();
1712 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1714 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1715 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1717 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1718 multitext_add_entry(&ge->eta.iotype, tmp);
1720 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1721 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1722 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1723 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1724 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1729 multitext_add_entry(&ge->eta.bs, tmp);
1731 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1733 sprintf(tmp, "%u", o->iodepth);
1734 multitext_add_entry(&ge->eta.iodepth, tmp);
1736 multitext_set_entry(&ge->eta.iotype, 0);
1737 multitext_set_entry(&ge->eta.bs, 0);
1738 multitext_set_entry(&ge->eta.ioengine, 0);
1739 multitext_set_entry(&ge->eta.iodepth, 0);
1741 gfio_set_state(ge, GE_STATE_JOB_SENT);
1743 gdk_threads_leave();
1746 static void gfio_client_timed_out(struct fio_client *client)
1748 struct gfio_client *gc = client->client_data;
1751 gdk_threads_enter();
1753 gfio_set_state(gc->ge, GE_STATE_NEW);
1754 clear_ge_ui_info(gc->ge);
1756 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1757 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1759 gdk_threads_leave();
1762 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1764 struct gfio_client *gc = client->client_data;
1766 gdk_threads_enter();
1768 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1771 entry_set_int_value(gc->err_entry, client->error);
1773 gdk_threads_leave();
1776 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1778 struct gfio_client *gc = client->client_data;
1780 gdk_threads_enter();
1781 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1782 gdk_threads_leave();
1785 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1787 struct gfio_client *gc = client->client_data;
1789 gdk_threads_enter();
1790 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1791 gdk_threads_leave();
1794 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1796 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1800 struct client_ops gfio_client_ops = {
1801 .text = gfio_text_op,
1802 .disk_util = gfio_disk_util_op,
1803 .thread_status = gfio_thread_status_op,
1804 .group_stats = gfio_group_stats_op,
1805 .jobs_eta = gfio_update_client_eta,
1806 .eta = gfio_update_all_eta,
1807 .probe = gfio_probe_op,
1808 .quit = gfio_quit_op,
1809 .add_job = gfio_add_job_op,
1810 .timed_out = gfio_client_timed_out,
1811 .stop = gfio_client_stop,
1812 .start = gfio_client_start,
1813 .job_start = gfio_client_job_start,
1814 .iolog = gfio_client_iolog,
1815 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1816 .stay_connected = 1,
1817 .client_type = FIO_CLIENT_TYPE_GUI,
1821 * FIXME: need more handling here
1823 static void ge_destroy(struct gui_entry *ge)
1825 struct gfio_client *gc = ge->client;
1827 if (gc && gc->client) {
1828 if (ge->state >= GE_STATE_CONNECTED)
1829 fio_client_terminate(gc->client);
1831 fio_put_client(gc->client);
1834 flist_del(&ge->list);
1838 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1842 static void gfio_quit(struct gui *ui)
1844 struct gui_entry *ge;
1846 while (!flist_empty(&ui->list)) {
1847 ge = flist_entry(ui->list.next, struct gui_entry, list);
1854 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1855 __attribute__((unused)) gpointer data)
1860 static void *job_thread(void *arg)
1862 struct gui *ui = arg;
1864 ui->handler_running = 1;
1865 fio_handle_clients(&gfio_client_ops);
1866 ui->handler_running = 0;
1870 static int send_job_files(struct gui_entry *ge)
1872 struct gfio_client *gc = ge->client;
1875 for (i = 0; i < ge->nr_job_files; i++) {
1876 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1880 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1881 report_error(error);
1882 g_error_free(error);
1887 free(ge->job_files[i]);
1888 ge->job_files[i] = NULL;
1890 while (i < ge->nr_job_files) {
1891 free(ge->job_files[i]);
1892 ge->job_files[i] = NULL;
1896 free(ge->job_files);
1897 ge->job_files = NULL;
1898 ge->nr_job_files = 0;
1902 static void *server_thread(void *arg)
1905 gfio_server_running = 1;
1906 fio_start_server(NULL);
1907 gfio_server_running = 0;
1911 static void gfio_start_server(void)
1913 struct gui *ui = &main_ui;
1915 if (!gfio_server_running) {
1916 gfio_server_running = 1;
1917 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1918 pthread_detach(ui->server_t);
1922 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1925 struct gui_entry *ge = data;
1926 struct gfio_client *gc = ge->client;
1929 fio_start_client(gc->client);
1932 static void file_open(GtkWidget *w, gpointer data);
1934 static void connect_clicked(GtkWidget *widget, gpointer data)
1936 struct gui_entry *ge = data;
1937 struct gfio_client *gc = ge->client;
1939 if (ge->state == GE_STATE_NEW) {
1942 if (!ge->nr_job_files)
1943 file_open(widget, ge->ui);
1944 if (!ge->nr_job_files)
1949 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1950 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1951 ret = fio_client_connect(gc->client);
1953 if (!ge->ui->handler_running)
1954 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1955 gfio_set_state(ge, GE_STATE_CONNECTED);
1959 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1960 report_error(error);
1961 g_error_free(error);
1964 fio_client_terminate(gc->client);
1965 gfio_set_state(ge, GE_STATE_NEW);
1966 clear_ge_ui_info(ge);
1970 static void send_clicked(GtkWidget *widget, gpointer data)
1972 struct gui_entry *ge = data;
1974 if (send_job_files(ge)) {
1977 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);
1978 report_error(error);
1979 g_error_free(error);
1981 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
1985 static void on_info_bar_response(GtkWidget *widget, gint response,
1988 struct gui *ui = &main_ui;
1990 if (response == GTK_RESPONSE_OK) {
1991 gtk_widget_destroy(widget);
1992 ui->error_info_bar = NULL;
1996 void report_error(GError *error)
1998 struct gui *ui = &main_ui;
2000 if (ui->error_info_bar == NULL) {
2001 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2004 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2005 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2008 ui->error_label = gtk_label_new(error->message);
2009 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2010 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2012 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2013 gtk_widget_show_all(ui->vbox);
2016 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2017 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2021 struct connection_widgets
2028 static void hostname_cb(GtkEntry *entry, gpointer data)
2030 struct connection_widgets *cw = data;
2031 int uses_net = 0, is_localhost = 0;
2036 * Check whether to display the 'auto start backend' box
2037 * or not. Show it if we are a localhost and using network,
2038 * or using a socket.
2040 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2041 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2046 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2047 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2048 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2049 !strcmp(text, "ip6-loopback"))
2053 if (!uses_net || is_localhost) {
2054 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2055 gtk_widget_set_sensitive(cw->button, 1);
2057 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2058 gtk_widget_set_sensitive(cw->button, 0);
2062 static int get_connection_details(char **host, int *port, int *type,
2065 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2066 struct connection_widgets cw;
2069 dialog = gtk_dialog_new_with_buttons("Connection details",
2070 GTK_WINDOW(main_ui.window),
2071 GTK_DIALOG_DESTROY_WITH_PARENT,
2072 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2073 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2075 frame = gtk_frame_new("Hostname / socket name");
2076 /* gtk_dialog_get_content_area() is 2.14 and newer */
2077 vbox = GTK_DIALOG(dialog)->vbox;
2078 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2080 box = gtk_vbox_new(FALSE, 6);
2081 gtk_container_add(GTK_CONTAINER(frame), box);
2083 hbox = gtk_hbox_new(TRUE, 10);
2084 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2085 cw.hentry = gtk_entry_new();
2086 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2087 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2089 frame = gtk_frame_new("Port");
2090 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2091 box = gtk_vbox_new(FALSE, 10);
2092 gtk_container_add(GTK_CONTAINER(frame), box);
2094 hbox = gtk_hbox_new(TRUE, 4);
2095 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2096 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2098 frame = gtk_frame_new("Type");
2099 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2100 box = gtk_vbox_new(FALSE, 10);
2101 gtk_container_add(GTK_CONTAINER(frame), box);
2103 hbox = gtk_hbox_new(TRUE, 4);
2104 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2106 cw.combo = gtk_combo_box_new_text();
2107 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2108 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2109 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2110 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2112 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2114 frame = gtk_frame_new("Options");
2115 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2116 box = gtk_vbox_new(FALSE, 10);
2117 gtk_container_add(GTK_CONTAINER(frame), box);
2119 hbox = gtk_hbox_new(TRUE, 4);
2120 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2122 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2123 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2124 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.");
2125 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2128 * Connect edit signal, so we can show/not-show the auto start button
2130 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2131 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2133 gtk_widget_show_all(dialog);
2135 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2136 gtk_widget_destroy(dialog);
2140 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2141 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2143 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2144 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2145 *type = Fio_client_ipv4;
2146 else if (!strncmp(typeentry, "IPv6", 4))
2147 *type = Fio_client_ipv6;
2149 *type = Fio_client_socket;
2152 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2154 gtk_widget_destroy(dialog);
2158 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2160 struct gfio_client *gc;
2162 gc = malloc(sizeof(*gc));
2163 memset(gc, 0, sizeof(*gc));
2165 gc->client = fio_get_client(client);
2169 client->client_data = gc;
2172 static GtkWidget *new_client_page(struct gui_entry *ge);
2174 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2176 struct gui_entry *ge;
2178 ge = malloc(sizeof(*ge));
2179 memset(ge, 0, sizeof(*ge));
2180 ge->state = GE_STATE_NEW;
2181 INIT_FLIST_HEAD(&ge->list);
2182 flist_add_tail(&ge->list, &ui->list);
2187 static struct gui_entry *get_new_ge_with_tab(const char *name)
2189 struct gui_entry *ge;
2191 ge = alloc_new_gui_entry(&main_ui);
2193 ge->vbox = new_client_page(ge);
2194 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2196 ge->page_label = gtk_label_new(name);
2197 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2199 gtk_widget_show_all(main_ui.window);
2203 static void file_new(GtkWidget *w, gpointer data)
2205 struct gui *ui = (struct gui *) data;
2206 struct gui_entry *ge;
2208 ge = get_new_ge_with_tab("Untitled");
2209 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2213 * Return the 'ge' corresponding to the tab. If the active tab is the
2214 * main tab, open a new tab.
2216 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2218 struct flist_head *entry;
2219 struct gui_entry *ge;
2224 return get_new_ge_with_tab("Untitled");
2230 flist_for_each(entry, &main_ui.list) {
2231 ge = flist_entry(entry, struct gui_entry, list);
2232 if (ge->page_num == cur_page)
2239 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2244 * Main tab is tab 0, so any current page other than 0 holds
2247 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2249 return get_ge_from_page(cur_page, NULL);
2254 static void file_close(GtkWidget *w, gpointer data)
2256 struct gui *ui = (struct gui *) data;
2257 struct gui_entry *ge;
2260 * Can't close the main tab
2262 ge = get_ge_from_cur_tab(ui);
2264 gtk_widget_destroy(ge->vbox);
2268 if (!flist_empty(&ui->list)) {
2269 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2276 static void file_add_recent(struct gui *ui, const gchar *uri)
2280 memset(&grd, 0, sizeof(grd));
2281 grd.display_name = strdup("gfio");
2282 grd.description = strdup("Fio job file");
2283 grd.mime_type = strdup(GFIO_MIME);
2284 grd.app_name = strdup(g_get_application_name());
2285 grd.app_exec = strdup("gfio %f/%u");
2287 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2290 static gchar *get_filename_from_uri(const gchar *uri)
2292 if (strncmp(uri, "file://", 7))
2295 return strdup(uri + 7);
2298 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2301 struct fio_client *client;
2304 filename = get_filename_from_uri(uri);
2306 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2307 ge->job_files[ge->nr_job_files] = strdup(filename);
2310 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2314 error = g_error_new(g_quark_from_string("fio"), 1,
2315 "Failed to add client %s", host);
2316 report_error(error);
2317 g_error_free(error);
2321 gfio_client_added(ge, client);
2322 file_add_recent(ge->ui, uri);
2326 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2328 int port, type, server_start;
2329 struct gui_entry *ge;
2332 int ret, ge_is_new = 0;
2335 * Creates new tab if current tab is the main window, or the
2336 * current tab already has a client.
2338 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2339 ge = get_ge_from_page(cur_page, &ge_is_new);
2341 ge = get_new_ge_with_tab("Untitled");
2345 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2347 if (get_connection_details(&host, &port, &type, &server_start)) {
2349 gtk_widget_destroy(ge->vbox);
2354 ret = do_file_open(ge, uri, host, type, port);
2360 gfio_start_server();
2363 gtk_widget_destroy(ge->vbox);
2369 static void recent_open(GtkAction *action, gpointer data)
2371 struct gui *ui = (struct gui *) data;
2372 GtkRecentInfo *info;
2375 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2376 uri = gtk_recent_info_get_uri(info);
2378 do_file_open_with_tab(ui, uri);
2381 static void file_open(GtkWidget *w, gpointer data)
2383 struct gui *ui = data;
2385 GSList *filenames, *fn_glist;
2386 GtkFileFilter *filter;
2388 dialog = gtk_file_chooser_dialog_new("Open File",
2389 GTK_WINDOW(ui->window),
2390 GTK_FILE_CHOOSER_ACTION_OPEN,
2391 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2392 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2394 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2396 filter = gtk_file_filter_new();
2397 gtk_file_filter_add_pattern(filter, "*.fio");
2398 gtk_file_filter_add_pattern(filter, "*.job");
2399 gtk_file_filter_add_pattern(filter, "*.ini");
2400 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2401 gtk_file_filter_set_name(filter, "Fio job file");
2402 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2404 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2405 gtk_widget_destroy(dialog);
2409 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2411 gtk_widget_destroy(dialog);
2413 filenames = fn_glist;
2414 while (filenames != NULL) {
2415 if (do_file_open_with_tab(ui, filenames->data))
2417 filenames = g_slist_next(filenames);
2420 g_slist_free(fn_glist);
2423 static void file_save(GtkWidget *w, gpointer data)
2425 struct gui *ui = data;
2428 dialog = gtk_file_chooser_dialog_new("Save File",
2429 GTK_WINDOW(ui->window),
2430 GTK_FILE_CHOOSER_ACTION_SAVE,
2431 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2432 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2435 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2436 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2438 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2441 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2442 // save_job_file(filename);
2445 gtk_widget_destroy(dialog);
2448 static void view_log_destroy(GtkWidget *w, gpointer data)
2450 struct gui *ui = (struct gui *) data;
2452 gtk_widget_ref(ui->log_tree);
2453 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2454 gtk_widget_destroy(w);
2455 ui->log_view = NULL;
2458 static void view_log(GtkWidget *w, gpointer data)
2460 GtkWidget *win, *scroll, *vbox, *box;
2461 struct gui *ui = (struct gui *) data;
2466 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2467 gtk_window_set_title(GTK_WINDOW(win), "Log");
2468 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2470 scroll = gtk_scrolled_window_new(NULL, NULL);
2472 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2474 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2476 box = gtk_hbox_new(TRUE, 0);
2477 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2478 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2479 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2481 vbox = gtk_vbox_new(TRUE, 5);
2482 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2484 gtk_container_add(GTK_CONTAINER(win), vbox);
2485 gtk_widget_show_all(win);
2488 static void connect_job_entry(GtkWidget *w, gpointer data)
2490 struct gui *ui = (struct gui *) data;
2491 struct gui_entry *ge;
2493 ge = get_ge_from_cur_tab(ui);
2495 connect_clicked(w, ge);
2498 static void send_job_entry(GtkWidget *w, gpointer data)
2500 struct gui *ui = (struct gui *) data;
2501 struct gui_entry *ge;
2503 ge = get_ge_from_cur_tab(ui);
2505 send_clicked(w, ge);
2509 static void edit_job_entry(GtkWidget *w, gpointer data)
2511 struct gui *ui = (struct gui *) data;
2513 gopt_get_options_window(ui->window);
2516 static void start_job_entry(GtkWidget *w, gpointer data)
2518 struct gui *ui = (struct gui *) data;
2519 struct gui_entry *ge;
2521 ge = get_ge_from_cur_tab(ui);
2523 start_job_clicked(w, ge);
2526 static void view_results(GtkWidget *w, gpointer data)
2528 struct gui *ui = (struct gui *) data;
2529 struct gfio_client *gc;
2530 struct gui_entry *ge;
2532 ge = get_ge_from_cur_tab(ui);
2536 if (ge->results_window)
2540 if (gc && gc->nr_results)
2541 gfio_display_end_results(gc);
2544 static void __update_graph_limits(struct gfio_graphs *g)
2546 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2547 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2550 static void update_graph_limits(void)
2552 struct flist_head *entry;
2553 struct gui_entry *ge;
2555 __update_graph_limits(&main_ui.graphs);
2557 flist_for_each(entry, &main_ui.list) {
2558 ge = flist_entry(entry, struct gui_entry, list);
2559 __update_graph_limits(&ge->graphs);
2563 static void preferences(GtkWidget *w, gpointer data)
2565 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2566 GtkWidget *hbox, *spin, *entry, *spin_int;
2569 dialog = gtk_dialog_new_with_buttons("Preferences",
2570 GTK_WINDOW(main_ui.window),
2571 GTK_DIALOG_DESTROY_WITH_PARENT,
2572 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2573 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2576 frame = gtk_frame_new("Graphing");
2577 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2578 vbox = gtk_vbox_new(FALSE, 6);
2579 gtk_container_add(GTK_CONTAINER(frame), vbox);
2581 hbox = gtk_hbox_new(FALSE, 5);
2582 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2583 entry = gtk_label_new("Font face to use for graph labels");
2584 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2586 font = gtk_font_button_new();
2587 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2589 box = gtk_vbox_new(FALSE, 6);
2590 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2592 hbox = gtk_hbox_new(FALSE, 5);
2593 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2594 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2595 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2597 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2599 box = gtk_vbox_new(FALSE, 6);
2600 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2602 hbox = gtk_hbox_new(FALSE, 5);
2603 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2604 entry = gtk_label_new("Client ETA request interval (msec)");
2605 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2607 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2608 frame = gtk_frame_new("Debug logging");
2609 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2610 vbox = gtk_vbox_new(FALSE, 6);
2611 gtk_container_add(GTK_CONTAINER(frame), vbox);
2613 box = gtk_hbox_new(FALSE, 6);
2614 gtk_container_add(GTK_CONTAINER(vbox), box);
2616 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2618 for (i = 0; i < FD_DEBUG_MAX; i++) {
2620 box = gtk_hbox_new(FALSE, 6);
2621 gtk_container_add(GTK_CONTAINER(vbox), box);
2625 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2626 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2627 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2630 gtk_widget_show_all(dialog);
2632 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2633 gtk_widget_destroy(dialog);
2637 for (i = 0; i < FD_DEBUG_MAX; i++) {
2640 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2642 fio_debug |= (1UL << i);
2645 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2646 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2647 update_graph_limits();
2648 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2650 gtk_widget_destroy(dialog);
2653 static void about_dialog(GtkWidget *w, gpointer data)
2655 const char *authors[] = {
2656 "Jens Axboe <axboe@kernel.dk>",
2657 "Stephen Carmeron <stephenmcameron@gmail.com>",
2660 const char *license[] = {
2661 "Fio is free software; you can redistribute it and/or modify "
2662 "it under the terms of the GNU General Public License as published by "
2663 "the Free Software Foundation; either version 2 of the License, or "
2664 "(at your option) any later version.\n",
2665 "Fio is distributed in the hope that it will be useful, "
2666 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2667 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2668 "GNU General Public License for more details.\n",
2669 "You should have received a copy of the GNU General Public License "
2670 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2671 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2673 char *license_trans;
2675 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2676 license[2], "\n", NULL);
2678 gtk_show_about_dialog(NULL,
2679 "program-name", "gfio",
2680 "comments", "Gtk2 UI for fio",
2681 "license", license_trans,
2682 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2684 "version", fio_version_string,
2685 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2686 "logo-icon-name", "fio",
2688 "wrap-license", TRUE,
2691 g_free(license_trans);
2694 static GtkActionEntry menu_items[] = {
2695 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2696 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2697 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2698 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2699 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2700 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2701 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2702 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2703 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2704 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2705 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2706 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
2707 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2708 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2709 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2710 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2711 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2713 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2715 static const gchar *ui_string = " \
2717 <menubar name=\"MainMenu\"> \
2718 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2719 <menuitem name=\"New\" action=\"NewFile\" /> \
2720 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2721 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2722 <separator name=\"Separator1\"/> \
2723 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2724 <separator name=\"Separator2\"/> \
2725 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2726 <separator name=\"Separator3\"/> \
2727 <placeholder name=\"FileRecentFiles\"/> \
2728 <separator name=\"Separator4\"/> \
2729 <menuitem name=\"Quit\" action=\"Quit\" /> \
2731 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2732 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2733 <separator name=\"Separator5\"/> \
2734 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2735 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2736 <separator name=\"Separator6\"/> \
2737 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2739 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2740 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2741 <separator name=\"Separator7\"/> \
2742 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2744 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2745 <menuitem name=\"About\" action=\"About\" /> \
2751 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2754 GtkActionGroup *action_group;
2757 action_group = gtk_action_group_new("Menu");
2758 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2760 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2761 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2763 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2765 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2768 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2769 GtkWidget *vbox, GtkUIManager *ui_manager)
2771 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2774 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2776 struct gui_entry *ge = (struct gui_entry *) data;
2779 index = gtk_combo_box_get_active(box);
2781 multitext_set_entry(&ge->eta.iotype, index);
2782 multitext_set_entry(&ge->eta.bs, index);
2783 multitext_set_entry(&ge->eta.ioengine, index);
2784 multitext_set_entry(&ge->eta.iodepth, index);
2787 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2789 struct gui_entry *ge = (struct gui_entry *) data;
2791 multitext_free(&ge->eta.iotype);
2792 multitext_free(&ge->eta.bs);
2793 multitext_free(&ge->eta.ioengine);
2794 multitext_free(&ge->eta.iodepth);
2797 static GtkWidget *new_client_page(struct gui_entry *ge)
2799 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2800 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2802 main_vbox = gtk_vbox_new(FALSE, 3);
2804 top_align = gtk_alignment_new(0, 0, 1, 0);
2805 top_vbox = gtk_vbox_new(FALSE, 3);
2806 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2807 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2809 probe = gtk_frame_new("Job");
2810 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2811 probe_frame = gtk_vbox_new(FALSE, 3);
2812 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2814 probe_box = gtk_hbox_new(FALSE, 3);
2815 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2816 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2817 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2818 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2819 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2821 probe_box = gtk_hbox_new(FALSE, 3);
2822 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2824 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2825 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2826 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2827 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2828 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
2829 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2830 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2831 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2832 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2834 probe_box = gtk_hbox_new(FALSE, 3);
2835 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2836 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2837 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2838 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2839 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2842 * Only add this if we have a commit rate
2845 probe_box = gtk_hbox_new(FALSE, 3);
2846 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2848 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2849 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2851 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2852 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2856 * Set up a drawing area and IOPS and bandwidth graphs
2858 ge->graphs.drawing_area = gtk_drawing_area_new();
2859 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2860 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2861 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2862 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2863 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2864 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2865 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2866 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2867 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2868 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2869 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2870 ge->graphs.drawing_area);
2871 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2873 setup_graphs(&ge->graphs);
2876 * Set up alignments for widgets at the bottom of ui,
2877 * align bottom left, expand horizontally but not vertically
2879 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2880 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2881 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2882 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2884 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2887 * Set up thread status progress bar
2889 ge->thread_status_pb = gtk_progress_bar_new();
2890 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2891 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2892 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2898 static GtkWidget *new_main_page(struct gui *ui)
2900 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2901 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2903 main_vbox = gtk_vbox_new(FALSE, 3);
2906 * Set up alignments for widgets at the top of ui,
2907 * align top left, expand horizontally but not vertically
2909 top_align = gtk_alignment_new(0, 0, 1, 0);
2910 top_vbox = gtk_vbox_new(FALSE, 0);
2911 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2912 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2914 probe = gtk_frame_new("Run statistics");
2915 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2916 probe_frame = gtk_vbox_new(FALSE, 3);
2917 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2919 probe_box = gtk_hbox_new(FALSE, 3);
2920 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2921 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2922 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2923 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2924 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2925 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2928 * Only add this if we have a commit rate
2931 probe_box = gtk_hbox_new(FALSE, 3);
2932 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2934 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2935 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2937 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2938 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2942 * Set up a drawing area and IOPS and bandwidth graphs
2944 ui->graphs.drawing_area = gtk_drawing_area_new();
2945 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2946 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2947 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2948 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2949 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2950 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2951 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2952 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2953 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2954 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2955 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2956 ui->graphs.drawing_area);
2957 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
2960 setup_graphs(&ui->graphs);
2963 * Set up alignments for widgets at the bottom of ui,
2964 * align bottom left, expand horizontally but not vertically
2966 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2967 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2968 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2969 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2972 * Set up thread status progress bar
2974 ui->thread_status_pb = gtk_progress_bar_new();
2975 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2976 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2977 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2982 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2983 guint page, gpointer data)
2986 struct gui *ui = (struct gui *) data;
2987 struct gui_entry *ge;
2990 set_job_menu_visible(ui, 0);
2991 set_view_results_visible(ui, 0);
2995 set_job_menu_visible(ui, 1);
2996 ge = get_ge_from_page(page, NULL);
2998 update_button_states(ui, ge);
3003 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3005 time_t time_a = gtk_recent_info_get_visited(a);
3006 time_t time_b = gtk_recent_info_get_visited(b);
3008 return time_b - time_a;
3011 static void add_recent_file_items(struct gui *ui)
3013 const gchar *gfio = g_get_application_name();
3014 GList *items, *item;
3017 if (ui->recent_ui_id) {
3018 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3019 gtk_ui_manager_ensure_update(ui->uimanager);
3021 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3023 if (ui->actiongroup) {
3024 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3025 g_object_unref(ui->actiongroup);
3027 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3029 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3031 items = gtk_recent_manager_get_items(ui->recentmanager);
3032 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3034 for (item = items; item && item->data; item = g_list_next(item)) {
3035 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3040 if (!gtk_recent_info_has_application(info, gfio))
3044 * We only support local files for now
3046 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3049 action_name = g_strdup_printf("RecentFile%u", i++);
3050 label = gtk_recent_info_get_display_name(info);
3052 action = g_object_new(GTK_TYPE_ACTION,
3053 "name", action_name,
3054 "label", label, NULL);
3056 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3057 gtk_recent_info_ref(info),
3058 (GDestroyNotify) gtk_recent_info_unref);
3061 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3063 gtk_action_group_add_action(ui->actiongroup, action);
3064 g_object_unref(action);
3066 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3067 "/MainMenu/FileMenu/FileRecentFiles",
3069 GTK_UI_MANAGER_MENUITEM, FALSE);
3071 g_free(action_name);
3077 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3081 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3082 gint x, gint y, GtkSelectionData *data,
3083 guint info, guint time)
3085 struct gui *ui = &main_ui;
3090 source = gtk_drag_get_source_widget(ctx);
3091 if (source && widget == gtk_widget_get_toplevel(source)) {
3092 gtk_drag_finish(ctx, FALSE, FALSE, time);
3096 uris = gtk_selection_data_get_uris(data);
3098 gtk_drag_finish(ctx, FALSE, FALSE, time);
3104 if (do_file_open_with_tab(ui, uris[i]))
3109 gtk_drag_finish(ctx, TRUE, FALSE, time);
3113 static void init_ui(int *argc, char **argv[], struct gui *ui)
3115 GtkSettings *settings;
3118 /* Magical g*thread incantation, you just need this thread stuff.
3119 * Without it, the update that happens in gfio_update_thread_status
3120 * doesn't really happen in a timely fashion, you need expose events
3122 if (!g_thread_supported())
3123 g_thread_init(NULL);
3126 gtk_init(argc, argv);
3127 settings = gtk_settings_get_default();
3128 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3130 gdk_color_parse("white", &white);
3132 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3133 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3134 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3136 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3137 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3139 ui->vbox = gtk_vbox_new(FALSE, 0);
3140 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3142 ui->uimanager = gtk_ui_manager_new();
3143 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3144 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3146 ui->recentmanager = gtk_recent_manager_get_default();
3147 add_recent_file_items(ui);
3149 ui->notebook = gtk_notebook_new();
3150 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3151 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3152 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3153 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3155 vbox = new_main_page(ui);
3156 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3157 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3158 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3160 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3162 gfio_ui_setup_log(ui);
3164 gtk_widget_show_all(ui->window);
3167 int main(int argc, char *argv[], char *envp[])
3169 if (initialize_fio(envp))
3171 if (fio_init_options())
3174 memset(&main_ui, 0, sizeof(main_ui));
3175 INIT_FLIST_HEAD(&main_ui.list);
3177 init_ui(&argc, &argv, &main_ui);
3179 gdk_threads_enter();
3181 gdk_threads_leave();