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
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 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
46 static void connect_clicked(GtkWidget *widget, gpointer data);
47 static void start_job_clicked(GtkWidget *widget, gpointer data);
48 static void send_clicked(GtkWidget *widget, gpointer data);
50 static struct button_spec {
51 const char *buttontext;
53 const char *tooltiptext[2];
54 const int start_sensitive;
55 } buttonspeclist[] = {
57 .buttontext = "Connect",
59 .tooltiptext = { "Disconnect from host", "Connect to host" },
65 .tooltiptext = { "Send job description to host", NULL },
69 .buttontext = "Start Job",
70 .f = start_job_clicked,
71 .tooltiptext = { "Start the current job on the server", NULL },
76 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
77 static void gfio_update_thread_status_all(char *status_message, double perc);
78 void report_error(GError *error);
80 static struct graph *setup_iops_graph(void)
84 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
85 graph_title(g, "IOPS (IOs/sec)");
86 graph_x_title(g, "Time (secs)");
87 graph_add_label(g, "Read IOPS");
88 graph_add_label(g, "Write IOPS");
89 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
90 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
91 line_graph_set_data_count_limit(g, gfio_graph_limit);
92 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
96 static struct graph *setup_bandwidth_graph(void)
100 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
101 graph_title(g, "Bandwidth (bytes/sec)");
102 graph_x_title(g, "Time (secs)");
103 graph_add_label(g, "Read Bandwidth");
104 graph_add_label(g, "Write Bandwidth");
105 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
106 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
107 graph_set_base_offset(g, 1);
108 line_graph_set_data_count_limit(g, 100);
109 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
113 static void setup_graphs(struct gfio_graphs *g)
115 g->iops_graph = setup_iops_graph();
116 g->bandwidth_graph = setup_bandwidth_graph();
119 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
121 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
122 mt->text[mt->max_text] = strdup(text);
126 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
128 if (index >= mt->max_text)
130 if (!mt->text || !mt->text[index])
133 mt->cur_text = index;
134 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
137 static void multitext_update_entry(struct multitext_widget *mt,
138 unsigned int index, const char *text)
144 free(mt->text[index]);
146 mt->text[index] = strdup(text);
147 if (mt->cur_text == index)
148 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
151 static void multitext_free(struct multitext_widget *mt)
155 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
157 for (i = 0; i < mt->max_text; i++) {
167 static void clear_ge_ui_info(struct gui_entry *ge)
169 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
170 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
171 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
172 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
174 /* should we empty it... */
175 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
177 multitext_update_entry(&ge->eta.iotype, 0, "");
178 multitext_update_entry(&ge->eta.bs, 0, "");
179 multitext_update_entry(&ge->eta.ioengine, 0, "");
180 multitext_update_entry(&ge->eta.iodepth, 0, "");
181 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
182 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
183 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
185 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
189 static void show_info_dialog(struct gui *ui, const char *title,
192 GtkWidget *dialog, *content, *label;
194 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
195 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
196 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
198 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
199 label = gtk_label_new(message);
200 gtk_container_add(GTK_CONTAINER(content), label);
201 gtk_widget_show_all(dialog);
202 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
203 gtk_dialog_run(GTK_DIALOG(dialog));
204 gtk_widget_destroy(dialog);
207 static void set_menu_entry_text(struct gui *ui, const char *path,
212 w = gtk_ui_manager_get_widget(ui->uimanager, path);
214 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
216 fprintf(stderr, "gfio: can't find path %s\n", path);
220 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
224 w = gtk_ui_manager_get_widget(ui->uimanager, path);
226 gtk_widget_set_sensitive(w, show);
228 fprintf(stderr, "gfio: can't find path %s\n", path);
231 static void set_job_menu_visible(struct gui *ui, int visible)
233 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
236 static void set_view_results_visible(struct gui *ui, int visible)
238 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
241 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
243 if (s->tooltiptext[sensitive])
244 return s->tooltiptext[sensitive];
246 return s->tooltiptext[0];
249 static GtkWidget *add_button(GtkWidget *buttonbox,
250 struct button_spec *buttonspec, gpointer data)
252 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
253 gboolean sens = buttonspec->start_sensitive;
255 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
256 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
258 sens = buttonspec->start_sensitive;
259 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
260 gtk_widget_set_sensitive(button, sens);
265 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
270 for (i = 0; i < nbuttons; i++)
271 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
275 * Update sensitivity of job buttons and job menu items, based on the
276 * state of the client.
278 static void update_button_states(struct gui *ui, struct gui_entry *ge)
280 unsigned int connect_state, send_state, start_state, edit_state;
281 const char *connect_str = NULL;
287 sprintf(tmp, "Bad client state: %u\n", ge->state);
288 show_info_dialog(ui, "Error", tmp);
289 /* fall through to new state */
295 connect_str = "Connect";
299 case GE_STATE_CONNECTED:
302 connect_str = "Disconnect";
306 case GE_STATE_JOB_SENT:
309 connect_str = "Disconnect";
313 case GE_STATE_JOB_STARTED:
316 connect_str = "Disconnect";
320 case GE_STATE_JOB_RUNNING:
323 connect_str = "Disconnect";
327 case GE_STATE_JOB_DONE:
330 connect_str = "Connect";
336 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
337 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
338 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
339 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
340 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
342 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
343 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
345 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
346 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
347 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
349 if (ge->client && ge->client->nr_results)
350 set_view_results_visible(ui, 1);
352 set_view_results_visible(ui, 0);
355 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
358 update_button_states(ge->ui, ge);
362 #define ALIGN_RIGHT 2
366 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
368 GtkCellRenderer *renderer;
369 GtkTreeViewColumn *col;
370 double xalign = 0.0; /* left as default */
371 PangoAlignment align;
374 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
375 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
377 visible = !(flags & INVISIBLE);
379 renderer = gtk_cell_renderer_text_new();
380 col = gtk_tree_view_column_new();
382 gtk_tree_view_column_set_title(col, title);
383 if (!(flags & UNSORTABLE))
384 gtk_tree_view_column_set_sort_column_id(col, index);
385 gtk_tree_view_column_set_resizable(col, TRUE);
386 gtk_tree_view_column_pack_start(col, renderer, TRUE);
387 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
388 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
390 case PANGO_ALIGN_LEFT:
393 case PANGO_ALIGN_CENTER:
396 case PANGO_ALIGN_RIGHT:
400 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
401 gtk_tree_view_column_set_visible(col, visible);
402 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
406 static void gfio_ui_setup_log(struct gui *ui)
408 GtkTreeSelection *selection;
410 GtkWidget *tree_view;
412 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
414 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
415 gtk_widget_set_can_focus(tree_view, FALSE);
417 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
418 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
419 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
420 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
422 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
423 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
424 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
425 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
427 ui->log_model = model;
428 ui->log_tree = tree_view;
431 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
434 double xdim, double ydim)
439 g = graph_new(xdim, ydim, gfio_graph_font);
440 graph_title(g, title);
441 graph_x_title(g, "Percentile");
443 for (i = 0; i < len; i++) {
446 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
447 graph_add_label(g, fbuf);
448 graph_add_data(g, fbuf, (double) ovals[i]);
454 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
460 GType types[FIO_IO_U_LIST_MAX_LEN];
461 GtkWidget *tree_view;
462 GtkTreeSelection *selection;
467 for (i = 0; i < len; i++)
468 types[i] = G_TYPE_INT;
470 model = gtk_list_store_newv(len, types);
472 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
473 gtk_widget_set_can_focus(tree_view, FALSE);
475 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
476 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
478 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
479 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
481 for (i = 0; i < len; i++) {
484 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
485 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
488 gtk_list_store_append(model, &iter);
490 for (i = 0; i < len; i++) {
492 ovals[i] = (ovals[i] + 999) / 1000;
493 gtk_list_store_set(model, &iter, i, ovals[i], -1);
499 static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
504 cr = gdk_cairo_create(w->window);
506 if (graph_has_tooltips(g)) {
507 g_object_set(w, "has-tooltip", TRUE, NULL);
508 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
511 cairo_set_source_rgb(cr, 0, 0, 0);
512 bar_graph_draw(g, cr);
518 static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
521 struct graph *g = data;
523 graph_set_size(g, w->allocation.width, w->allocation.height);
524 graph_set_size(g, w->allocation.width, w->allocation.height);
525 graph_set_position(g, 0, 0);
529 static void gfio_show_clat_percentiles(struct gfio_client *gc,
530 GtkWidget *vbox, struct thread_stat *ts,
533 unsigned int *io_u_plat = ts->io_u_plat[ddir];
534 unsigned long nr = ts->clat_stat[ddir].samples;
535 fio_fp64_t *plist = ts->percentile_list;
536 unsigned int *ovals, len, minv, maxv, scale_down;
538 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
539 struct gui_entry *ge = gc->ge;
542 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
547 * We default to usecs, but if the value range is such that we
548 * should scale down to msecs, do that.
550 if (minv > 2000 && maxv > 99999) {
558 sprintf(tmp, "Completion percentiles (%s)", base);
559 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
560 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
562 frame = gtk_frame_new(tmp);
563 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
565 completion_vbox = gtk_vbox_new(FALSE, 3);
566 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
567 hbox = gtk_hbox_new(FALSE, 3);
568 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
569 drawing_area = gtk_drawing_area_new();
570 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
571 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
572 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
573 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
574 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
576 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
582 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
583 unsigned long max, double mean, double dev)
585 const char *base = "(usec)";
586 GtkWidget *hbox, *label, *frame;
590 if (!usec_to_msec(&min, &max, &mean, &dev))
593 minp = num2str(min, 6, 1, 0);
594 maxp = num2str(max, 6, 1, 0);
596 sprintf(tmp, "%s %s", name, base);
597 frame = gtk_frame_new(tmp);
598 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
600 hbox = gtk_hbox_new(FALSE, 3);
601 gtk_container_add(GTK_CONTAINER(frame), hbox);
603 label = new_info_label_in_frame(hbox, "Minimum");
604 gtk_label_set_text(GTK_LABEL(label), minp);
605 label = new_info_label_in_frame(hbox, "Maximum");
606 gtk_label_set_text(GTK_LABEL(label), maxp);
607 label = new_info_label_in_frame(hbox, "Average");
608 sprintf(tmp, "%5.02f", mean);
609 gtk_label_set_text(GTK_LABEL(label), tmp);
610 label = new_info_label_in_frame(hbox, "Standard deviation");
611 sprintf(tmp, "%5.02f", dev);
612 gtk_label_set_text(GTK_LABEL(label), tmp);
623 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
624 struct group_run_stats *rs,
625 struct thread_stat *ts, int ddir)
627 const char *ddir_label[2] = { "Read", "Write" };
628 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
629 unsigned long min[3], max[3], runt;
630 unsigned long long bw, iops;
631 unsigned int flags = 0;
632 double mean[3], dev[3];
633 char *io_p, *bw_p, *iops_p;
636 if (!ts->runtime[ddir])
639 i2p = is_power_of_2(rs->kb_base);
640 runt = ts->runtime[ddir];
642 bw = (1000 * ts->io_bytes[ddir]) / runt;
643 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
644 bw_p = num2str(bw, 6, 1, i2p);
646 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
647 iops_p = num2str(iops, 6, 1, 0);
649 box = gtk_hbox_new(FALSE, 3);
650 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
652 frame = gtk_frame_new(ddir_label[ddir]);
653 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
655 main_vbox = gtk_vbox_new(FALSE, 3);
656 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
658 box = gtk_hbox_new(FALSE, 3);
659 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
661 label = new_info_label_in_frame(box, "IO");
662 gtk_label_set_text(GTK_LABEL(label), io_p);
663 label = new_info_label_in_frame(box, "Bandwidth");
664 gtk_label_set_text(GTK_LABEL(label), bw_p);
665 label = new_info_label_in_frame(box, "IOPS");
666 gtk_label_set_text(GTK_LABEL(label), iops_p);
667 label = new_info_label_in_frame(box, "Runtime (msec)");
668 label_set_int_value(label, ts->runtime[ddir]);
670 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
671 double p_of_agg = 100.0;
672 const char *bw_str = "KB";
676 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
677 if (p_of_agg > 100.0)
681 if (mean[0] > 999999.9) {
689 sprintf(tmp, "Bandwidth (%s)", bw_str);
690 frame = gtk_frame_new(tmp);
691 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
693 box = gtk_hbox_new(FALSE, 3);
694 gtk_container_add(GTK_CONTAINER(frame), box);
696 label = new_info_label_in_frame(box, "Minimum");
697 label_set_int_value(label, min[0]);
698 label = new_info_label_in_frame(box, "Maximum");
699 label_set_int_value(label, max[0]);
700 label = new_info_label_in_frame(box, "Percentage of jobs");
701 sprintf(tmp, "%3.2f%%", p_of_agg);
702 gtk_label_set_text(GTK_LABEL(label), tmp);
703 label = new_info_label_in_frame(box, "Average");
704 sprintf(tmp, "%5.02f", mean[0]);
705 gtk_label_set_text(GTK_LABEL(label), tmp);
706 label = new_info_label_in_frame(box, "Standard deviation");
707 sprintf(tmp, "%5.02f", dev[0]);
708 gtk_label_set_text(GTK_LABEL(label), tmp);
711 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
713 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
715 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
719 frame = gtk_frame_new("Latency");
720 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
722 vbox = gtk_vbox_new(FALSE, 3);
723 gtk_container_add(GTK_CONTAINER(frame), vbox);
725 if (flags & GFIO_SLAT)
726 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
727 if (flags & GFIO_CLAT)
728 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
729 if (flags & GFIO_LAT)
730 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
733 if (ts->clat_percentiles)
734 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
741 static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
744 double xdim, double ydim)
749 g = graph_new(xdim, ydim, gfio_graph_font);
750 graph_title(g, title);
751 graph_x_title(g, "Buckets");
753 for (i = 0; i < len; i++) {
754 graph_add_label(g, labels[i]);
755 graph_add_data(g, labels[i], lat[i]);
761 static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
764 GtkWidget *tree_view;
765 GtkTreeSelection *selection;
771 types = malloc(num * sizeof(GType));
773 for (i = 0; i < num; i++)
774 types[i] = G_TYPE_STRING;
776 model = gtk_list_store_newv(num, types);
780 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
781 gtk_widget_set_can_focus(tree_view, FALSE);
783 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
784 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
786 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
787 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
789 for (i = 0; i < num; i++)
790 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
792 gtk_list_store_append(model, &iter);
794 for (i = 0; i < num; i++) {
798 sprintf(fbuf, "0.00");
800 sprintf(fbuf, "%3.2f%%", lat[i]);
802 gtk_list_store_set(model, &iter, i, fbuf, -1);
808 static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
809 struct thread_stat *ts)
811 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
812 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
813 "250u", "500u", "750u", "1m", "2m",
814 "4m", "10m", "20m", "50m", "100m",
815 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
817 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
818 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
819 struct gui_entry *ge = gc->ge;
821 stat_calc_lat_u(ts, io_u_lat);
822 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
825 * Found out which first bucket has entries, and which last bucket
828 for (i = 0; i < total; i++) {
829 if (io_u_lat[i] == 0.00)
843 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
844 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
846 frame = gtk_frame_new("Latency buckets");
847 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
849 completion_vbox = gtk_vbox_new(FALSE, 3);
850 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
851 hbox = gtk_hbox_new(FALSE, 3);
852 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
854 drawing_area = gtk_drawing_area_new();
855 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
856 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
857 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
858 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
859 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
861 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
864 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
866 GtkWidget *box, *frame, *entry;
867 double usr_cpu, sys_cpu;
868 unsigned long runtime;
871 runtime = ts->total_run_time;
873 double runt = (double) runtime;
875 usr_cpu = (double) ts->usr_time * 100 / runt;
876 sys_cpu = (double) ts->sys_time * 100 / runt;
882 frame = gtk_frame_new("OS resources");
883 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
885 box = gtk_hbox_new(FALSE, 3);
886 gtk_container_add(GTK_CONTAINER(frame), box);
888 entry = new_info_entry_in_frame(box, "User CPU");
889 sprintf(tmp, "%3.2f%%", usr_cpu);
890 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
891 entry = new_info_entry_in_frame(box, "System CPU");
892 sprintf(tmp, "%3.2f%%", sys_cpu);
893 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
894 entry = new_info_entry_in_frame(box, "Context switches");
895 entry_set_int_value(entry, ts->ctx);
896 entry = new_info_entry_in_frame(box, "Major faults");
897 entry_set_int_value(entry, ts->majf);
898 entry = new_info_entry_in_frame(box, "Minor faults");
899 entry_set_int_value(entry, ts->minf);
901 static void gfio_add_sc_depths_tree(GtkListStore *model,
902 struct thread_stat *ts, unsigned int len,
905 double io_u_dist[FIO_IO_U_MAP_NR];
907 /* Bits 0, and 3-8 */
908 const int add_mask = 0x1f9;
912 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
914 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
916 gtk_list_store_append(model, &iter);
918 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
920 for (i = 1, j = 0; i < len; i++) {
923 if (!(add_mask & (1UL << (i - 1))))
924 sprintf(fbuf, "0.0%%");
926 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
930 gtk_list_store_set(model, &iter, i, fbuf, -1);
935 static void gfio_add_total_depths_tree(GtkListStore *model,
936 struct thread_stat *ts, unsigned int len)
938 double io_u_dist[FIO_IO_U_MAP_NR];
940 /* Bits 1-6, and 8 */
941 const int add_mask = 0x17e;
944 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
946 gtk_list_store_append(model, &iter);
948 gtk_list_store_set(model, &iter, 0, "Total", -1);
950 for (i = 1, j = 0; i < len; i++) {
953 if (!(add_mask & (1UL << (i - 1))))
954 sprintf(fbuf, "0.0%%");
956 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
960 gtk_list_store_set(model, &iter, i, fbuf, -1);
965 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
967 GtkWidget *frame, *box, *tree_view;
968 GtkTreeSelection *selection;
970 GType types[FIO_IO_U_MAP_NR + 1];
973 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
975 frame = gtk_frame_new("IO depths");
976 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
978 box = gtk_hbox_new(FALSE, 3);
979 gtk_container_add(GTK_CONTAINER(frame), box);
981 for (i = 0; i < NR_LABELS; i++)
982 types[i] = G_TYPE_STRING;
984 model = gtk_list_store_newv(NR_LABELS, types);
986 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
987 gtk_widget_set_can_focus(tree_view, FALSE);
989 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
990 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
992 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
993 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
995 for (i = 0; i < NR_LABELS; i++)
996 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
998 gfio_add_total_depths_tree(model, ts, NR_LABELS);
999 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1000 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1002 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1005 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1007 struct gui_entry *ge = (struct gui_entry *) data;
1009 gtk_widget_destroy(w);
1010 ge->results_window = NULL;
1011 ge->results_notebook = NULL;
1015 static void results_close(GtkWidget *w, gpointer *data)
1017 struct gui_entry *ge = (struct gui_entry *) data;
1019 gtk_widget_destroy(ge->results_window);
1022 static GtkActionEntry results_menu_items[] = {
1023 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1024 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1025 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1027 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1029 static const gchar *results_ui_string = " \
1031 <menubar name=\"MainMenu\"> \
1032 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1033 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1035 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1041 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1043 GtkActionGroup *action_group;
1047 ge->results_uimanager = gtk_ui_manager_new();
1049 action_group = gtk_action_group_new("ResultsMenu");
1050 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1052 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1053 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1055 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1057 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1061 static GtkWidget *get_results_window(struct gui_entry *ge)
1063 GtkWidget *win, *notebook, *vbox;
1065 if (ge->results_window)
1066 return ge->results_notebook;
1068 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1069 gtk_window_set_title(GTK_WINDOW(win), "Results");
1070 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1071 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1072 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1074 vbox = gtk_vbox_new(FALSE, 0);
1075 gtk_container_add(GTK_CONTAINER(win), vbox);
1077 ge->results_menu = get_results_menubar(win, ge);
1078 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1080 notebook = gtk_notebook_new();
1081 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1082 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1083 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1085 ge->results_window = win;
1086 ge->results_notebook = notebook;
1087 return ge->results_notebook;
1090 static void disk_util_destroy(GtkWidget *w, gpointer data)
1092 struct gui_entry *ge = (struct gui_entry *) data;
1094 ge->disk_util_vbox = NULL;
1095 gtk_widget_destroy(w);
1098 static GtkWidget *get_scrolled_window(gint border_width)
1102 scroll = gtk_scrolled_window_new(NULL, NULL);
1103 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1104 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1109 static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1111 GtkWidget *vbox, *box, *scroll, *res_notebook;
1113 if (ge->disk_util_vbox)
1114 return ge->disk_util_vbox;
1116 scroll = get_scrolled_window(5);
1117 vbox = gtk_vbox_new(FALSE, 3);
1118 box = gtk_hbox_new(FALSE, 0);
1119 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1121 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1122 res_notebook = get_results_window(ge);
1124 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1125 ge->disk_util_vbox = box;
1126 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1128 return ge->disk_util_vbox;
1131 static int __gfio_disk_util_show(GtkWidget *res_notebook,
1132 struct gfio_client *gc, struct cmd_du_pdu *p)
1134 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
1135 struct gui_entry *ge = gc->ge;
1139 util_vbox = gfio_disk_util_get_vbox(ge);
1141 vbox = gtk_vbox_new(FALSE, 3);
1142 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
1144 frame = gtk_frame_new((char *) p->dus.name);
1145 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1147 box = gtk_vbox_new(FALSE, 3);
1148 gtk_container_add(GTK_CONTAINER(frame), box);
1150 frame = gtk_frame_new("Read");
1151 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1152 vbox = gtk_hbox_new(TRUE, 3);
1153 gtk_container_add(GTK_CONTAINER(frame), vbox);
1154 entry = new_info_entry_in_frame(vbox, "IOs");
1155 entry_set_int_value(entry, p->dus.ios[0]);
1156 entry = new_info_entry_in_frame(vbox, "Merges");
1157 entry_set_int_value(entry, p->dus.merges[0]);
1158 entry = new_info_entry_in_frame(vbox, "Sectors");
1159 entry_set_int_value(entry, p->dus.sectors[0]);
1160 entry = new_info_entry_in_frame(vbox, "Ticks");
1161 entry_set_int_value(entry, p->dus.ticks[0]);
1163 frame = gtk_frame_new("Write");
1164 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1165 vbox = gtk_hbox_new(TRUE, 3);
1166 gtk_container_add(GTK_CONTAINER(frame), vbox);
1167 entry = new_info_entry_in_frame(vbox, "IOs");
1168 entry_set_int_value(entry, p->dus.ios[1]);
1169 entry = new_info_entry_in_frame(vbox, "Merges");
1170 entry_set_int_value(entry, p->dus.merges[1]);
1171 entry = new_info_entry_in_frame(vbox, "Sectors");
1172 entry_set_int_value(entry, p->dus.sectors[1]);
1173 entry = new_info_entry_in_frame(vbox, "Ticks");
1174 entry_set_int_value(entry, p->dus.ticks[1]);
1176 frame = gtk_frame_new("Shared");
1177 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1178 vbox = gtk_hbox_new(TRUE, 3);
1179 gtk_container_add(GTK_CONTAINER(frame), vbox);
1180 entry = new_info_entry_in_frame(vbox, "IO ticks");
1181 entry_set_int_value(entry, p->dus.io_ticks);
1182 entry = new_info_entry_in_frame(vbox, "Time in queue");
1183 entry_set_int_value(entry, p->dus.time_in_queue);
1187 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1191 sprintf(tmp, "%3.2f%%", util);
1192 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1193 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1195 gtk_widget_show_all(ge->results_window);
1199 static int gfio_disk_util_show(struct gfio_client *gc)
1201 struct gui_entry *ge = gc->ge;
1202 GtkWidget *res_notebook;
1208 res_notebook = get_results_window(ge);
1210 for (i = 0; i < gc->nr_du; i++) {
1211 struct cmd_du_pdu *p = &gc->du[i];
1213 __gfio_disk_util_show(res_notebook, gc, p);
1216 gtk_widget_show_all(ge->results_window);
1220 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1221 struct group_run_stats *rs)
1223 unsigned int nr = gc->nr_results;
1225 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1226 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1227 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1231 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1232 struct thread_stat *ts,
1233 struct group_run_stats *rs)
1235 GtkWidget *box, *vbox, *entry, *scroll;
1237 scroll = gtk_scrolled_window_new(NULL, NULL);
1238 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1239 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1241 vbox = gtk_vbox_new(FALSE, 3);
1243 box = gtk_hbox_new(FALSE, 0);
1244 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1246 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1248 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1250 entry = new_info_entry_in_frame(box, "Name");
1251 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1252 if (strlen(ts->description)) {
1253 entry = new_info_entry_in_frame(box, "Description");
1254 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1256 entry = new_info_entry_in_frame(box, "Group ID");
1257 entry_set_int_value(entry, ts->groupid);
1258 entry = new_info_entry_in_frame(box, "Jobs");
1259 entry_set_int_value(entry, ts->members);
1260 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1261 entry_set_int_value(entry, ts->error);
1262 entry = new_info_entry_in_frame(box, "PID");
1263 entry_set_int_value(entry, ts->pid);
1265 if (ts->io_bytes[DDIR_READ])
1266 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1267 if (ts->io_bytes[DDIR_WRITE])
1268 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1270 gfio_show_latency_buckets(gc, vbox, ts);
1271 gfio_show_cpu_usage(vbox, ts);
1272 gfio_show_io_depths(vbox, ts);
1275 static void gfio_display_end_results(struct gfio_client *gc)
1277 struct gui_entry *ge = gc->ge;
1278 GtkWidget *res_notebook;
1281 res_notebook = get_results_window(ge);
1283 for (i = 0; i < gc->nr_results; i++) {
1284 struct end_results *e = &gc->results[i];
1286 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1289 if (gfio_disk_util_show(gc))
1290 gtk_widget_show_all(ge->results_window);
1293 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1294 struct group_run_stats *rs)
1296 struct gfio_client *gc = client->client_data;
1297 struct gui_entry *ge = gc->ge;
1299 gfio_add_end_results(gc, ts, rs);
1301 gdk_threads_enter();
1302 if (ge->results_window)
1303 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1305 gfio_display_end_results(gc);
1306 gdk_threads_leave();
1309 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1311 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1312 struct gui *ui = &main_ui;
1316 char tmp[64], timebuf[80];
1319 tm = localtime(&sec);
1320 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1321 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1323 gdk_threads_enter();
1325 gtk_list_store_append(ui->log_model, &iter);
1326 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1327 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1328 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1329 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1331 if (p->level == FIO_LOG_ERR)
1332 view_log(NULL, (gpointer) ui);
1334 gdk_threads_leave();
1337 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1339 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1340 struct gfio_client *gc = client->client_data;
1341 struct gui_entry *ge = gc->ge;
1342 unsigned int nr = gc->nr_du;
1344 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1345 memcpy(&gc->du[nr], p, sizeof(*p));
1348 gdk_threads_enter();
1349 if (ge->results_window)
1350 __gfio_disk_util_show(ge->results_notebook, gc, p);
1352 gfio_disk_util_show(gc);
1353 gdk_threads_leave();
1356 extern int sum_stat_clients;
1357 extern struct thread_stat client_ts;
1358 extern struct group_run_stats client_gs;
1360 static int sum_stat_nr;
1362 static void gfio_thread_status_op(struct fio_client *client,
1363 struct fio_net_cmd *cmd)
1365 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1367 gfio_display_ts(client, &p->ts, &p->rs);
1369 if (sum_stat_clients == 1)
1372 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1373 sum_group_stats(&client_gs, &p->rs);
1375 client_ts.members++;
1376 client_ts.thread_number = p->ts.thread_number;
1377 client_ts.groupid = p->ts.groupid;
1379 if (++sum_stat_nr == sum_stat_clients) {
1380 strcpy(client_ts.name, "All clients");
1381 gfio_display_ts(client, &client_ts, &client_gs);
1385 static void gfio_group_stats_op(struct fio_client *client,
1386 struct fio_net_cmd *cmd)
1388 /* We're ignoring group stats for now */
1391 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1394 struct gfio_graphs *g = data;
1396 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1397 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1398 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1399 graph_set_position(g->bandwidth_graph, 0, 0);
1403 static void draw_graph(struct graph *g, cairo_t *cr)
1405 line_graph_draw(g, cr);
1409 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1410 gboolean keyboard_mode, GtkTooltip *tooltip,
1413 struct gfio_graphs *g = data;
1414 const char *text = NULL;
1416 if (graph_contains_xy(g->iops_graph, x, y))
1417 text = graph_find_tooltip(g->iops_graph, x, y);
1418 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1419 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1422 gtk_tooltip_set_text(tooltip, text);
1429 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1431 struct gfio_graphs *g = p;
1434 cr = gdk_cairo_create(w->window);
1436 if (graph_has_tooltips(g->iops_graph) ||
1437 graph_has_tooltips(g->bandwidth_graph)) {
1438 g_object_set(w, "has-tooltip", TRUE, NULL);
1439 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1442 cairo_set_source_rgb(cr, 0, 0, 0);
1443 draw_graph(g->iops_graph, cr);
1444 draw_graph(g->bandwidth_graph, cr);
1451 * Client specific ETA
1453 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1455 struct gfio_client *gc = client->client_data;
1456 struct gui_entry *ge = gc->ge;
1457 static int eta_good;
1464 gdk_threads_enter();
1469 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1470 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1471 eta_to_str(eta_str, je->eta_sec);
1474 sprintf(tmp, "%u", je->nr_running);
1475 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1476 sprintf(tmp, "%u", je->files_open);
1477 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1480 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1481 if (je->m_rate || je->t_rate) {
1484 mr = num2str(je->m_rate, 4, 0, i2p);
1485 tr = num2str(je->t_rate, 4, 0, i2p);
1486 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1487 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1490 } else if (je->m_iops || je->t_iops)
1491 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1493 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1494 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1495 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1496 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1499 if (je->eta_sec != INT_MAX && je->nr_running) {
1503 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1504 strcpy(output, "-.-% done");
1508 sprintf(output, "%3.1f%% done", perc);
1511 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1512 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1514 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1515 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1517 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1518 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1519 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1520 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1522 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1523 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1524 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1525 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1534 char *dst = output + strlen(output);
1536 sprintf(dst, " - %s", eta_str);
1539 gfio_update_thread_status(ge, output, perc);
1540 gdk_threads_leave();
1544 * Update ETA in main window for all clients
1546 static void gfio_update_all_eta(struct jobs_eta *je)
1548 struct gui *ui = &main_ui;
1549 static int eta_good;
1555 gdk_threads_enter();
1560 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1561 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1562 eta_to_str(eta_str, je->eta_sec);
1566 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1567 if (je->m_rate || je->t_rate) {
1570 mr = num2str(je->m_rate, 4, 0, i2p);
1571 tr = num2str(je->t_rate, 4, 0, i2p);
1572 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1573 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1576 } else if (je->m_iops || je->t_iops)
1577 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1579 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1580 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1581 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1582 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1585 entry_set_int_value(ui->eta.jobs, je->nr_running);
1587 if (je->eta_sec != INT_MAX && je->nr_running) {
1591 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1592 strcpy(output, "-.-% done");
1596 sprintf(output, "%3.1f%% done", perc);
1599 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1600 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1602 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1603 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1605 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1606 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1607 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1608 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1610 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1611 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1612 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1613 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1622 char *dst = output + strlen(output);
1624 sprintf(dst, " - %s", eta_str);
1627 gfio_update_thread_status_all(output, perc);
1628 gdk_threads_leave();
1631 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1633 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1634 struct gfio_client *gc = client->client_data;
1635 struct gui_entry *ge = gc->ge;
1636 const char *os, *arch;
1639 os = fio_get_os_string(probe->os);
1643 arch = fio_get_arch_string(probe->arch);
1648 client->name = strdup((char *) probe->hostname);
1650 gdk_threads_enter();
1652 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1653 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1654 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1655 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1656 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1658 gfio_set_state(ge, GE_STATE_CONNECTED);
1660 gdk_threads_leave();
1663 static void gfio_update_thread_status(struct gui_entry *ge,
1664 char *status_message, double perc)
1666 static char message[100];
1667 const char *m = message;
1669 strncpy(message, status_message, sizeof(message) - 1);
1670 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1671 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1672 gtk_widget_queue_draw(main_ui.window);
1675 static void gfio_update_thread_status_all(char *status_message, double perc)
1677 struct gui *ui = &main_ui;
1678 static char message[100];
1679 const char *m = message;
1681 strncpy(message, status_message, sizeof(message) - 1);
1682 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1683 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1684 gtk_widget_queue_draw(ui->window);
1687 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1689 struct gfio_client *gc = client->client_data;
1691 gdk_threads_enter();
1692 gfio_set_state(gc->ge, GE_STATE_NEW);
1693 gdk_threads_leave();
1696 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1698 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1699 struct gfio_client *gc = client->client_data;
1700 struct thread_options *o = &gc->o;
1701 struct gui_entry *ge = gc->ge;
1702 char *c1, *c2, *c3, *c4;
1705 p->thread_number = le32_to_cpu(p->thread_number);
1706 p->groupid = le32_to_cpu(p->groupid);
1707 convert_thread_options_to_cpu(o, &p->top);
1709 gdk_threads_enter();
1711 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1713 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1714 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1716 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1717 multitext_add_entry(&ge->eta.iotype, tmp);
1719 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1720 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1721 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1722 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1723 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1728 multitext_add_entry(&ge->eta.bs, tmp);
1730 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1732 sprintf(tmp, "%u", o->iodepth);
1733 multitext_add_entry(&ge->eta.iodepth, tmp);
1735 multitext_set_entry(&ge->eta.iotype, 0);
1736 multitext_set_entry(&ge->eta.bs, 0);
1737 multitext_set_entry(&ge->eta.ioengine, 0);
1738 multitext_set_entry(&ge->eta.iodepth, 0);
1740 gfio_set_state(ge, GE_STATE_JOB_SENT);
1742 gdk_threads_leave();
1745 static void gfio_client_timed_out(struct fio_client *client)
1747 struct gfio_client *gc = client->client_data;
1750 gdk_threads_enter();
1752 gfio_set_state(gc->ge, GE_STATE_NEW);
1753 clear_ge_ui_info(gc->ge);
1755 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1756 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1758 gdk_threads_leave();
1761 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1763 struct gfio_client *gc = client->client_data;
1765 gdk_threads_enter();
1767 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1770 entry_set_int_value(gc->err_entry, client->error);
1772 gdk_threads_leave();
1775 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1777 struct gfio_client *gc = client->client_data;
1779 gdk_threads_enter();
1780 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1781 gdk_threads_leave();
1784 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1786 struct gfio_client *gc = client->client_data;
1788 gdk_threads_enter();
1789 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1790 gdk_threads_leave();
1793 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1795 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1799 struct client_ops gfio_client_ops = {
1800 .text = gfio_text_op,
1801 .disk_util = gfio_disk_util_op,
1802 .thread_status = gfio_thread_status_op,
1803 .group_stats = gfio_group_stats_op,
1804 .jobs_eta = gfio_update_client_eta,
1805 .eta = gfio_update_all_eta,
1806 .probe = gfio_probe_op,
1807 .quit = gfio_quit_op,
1808 .add_job = gfio_add_job_op,
1809 .timed_out = gfio_client_timed_out,
1810 .stop = gfio_client_stop,
1811 .start = gfio_client_start,
1812 .job_start = gfio_client_job_start,
1813 .iolog = gfio_client_iolog,
1814 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1815 .stay_connected = 1,
1816 .client_type = FIO_CLIENT_TYPE_GUI,
1820 * FIXME: need more handling here
1822 static void ge_destroy(struct gui_entry *ge)
1824 struct gfio_client *gc = ge->client;
1826 if (gc && gc->client) {
1827 if (ge->state >= GE_STATE_CONNECTED)
1828 fio_client_terminate(gc->client);
1830 fio_put_client(gc->client);
1833 flist_del(&ge->list);
1837 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1841 static void gfio_quit(struct gui *ui)
1843 struct gui_entry *ge;
1845 while (!flist_empty(&ui->list)) {
1846 ge = flist_entry(ui->list.next, struct gui_entry, list);
1853 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1854 __attribute__((unused)) gpointer data)
1859 static void *job_thread(void *arg)
1861 struct gui *ui = arg;
1863 ui->handler_running = 1;
1864 fio_handle_clients(&gfio_client_ops);
1865 ui->handler_running = 0;
1869 static int send_job_files(struct gui_entry *ge)
1871 struct gfio_client *gc = ge->client;
1874 for (i = 0; i < ge->nr_job_files; i++) {
1875 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1879 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1880 report_error(error);
1881 g_error_free(error);
1886 free(ge->job_files[i]);
1887 ge->job_files[i] = NULL;
1889 while (i < ge->nr_job_files) {
1890 free(ge->job_files[i]);
1891 ge->job_files[i] = NULL;
1895 free(ge->job_files);
1896 ge->job_files = NULL;
1897 ge->nr_job_files = 0;
1901 static void *server_thread(void *arg)
1904 gfio_server_running = 1;
1905 fio_start_server(NULL);
1906 gfio_server_running = 0;
1910 static void gfio_start_server(void)
1912 struct gui *ui = &main_ui;
1914 if (!gfio_server_running) {
1915 gfio_server_running = 1;
1916 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1917 pthread_detach(ui->server_t);
1921 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1924 struct gui_entry *ge = data;
1925 struct gfio_client *gc = ge->client;
1928 fio_start_client(gc->client);
1931 static void file_open(GtkWidget *w, gpointer data);
1933 static void connect_clicked(GtkWidget *widget, gpointer data)
1935 struct gui_entry *ge = data;
1936 struct gfio_client *gc = ge->client;
1938 if (ge->state == GE_STATE_NEW) {
1941 if (!ge->nr_job_files)
1942 file_open(widget, ge->ui);
1943 if (!ge->nr_job_files)
1948 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1949 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1950 ret = fio_client_connect(gc->client);
1952 if (!ge->ui->handler_running)
1953 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1954 gfio_set_state(ge, GE_STATE_CONNECTED);
1958 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1959 report_error(error);
1960 g_error_free(error);
1963 fio_client_terminate(gc->client);
1964 gfio_set_state(ge, GE_STATE_NEW);
1965 clear_ge_ui_info(ge);
1969 static void send_clicked(GtkWidget *widget, gpointer data)
1971 struct gui_entry *ge = data;
1973 if (send_job_files(ge)) {
1976 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);
1977 report_error(error);
1978 g_error_free(error);
1980 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
1984 static void on_info_bar_response(GtkWidget *widget, gint response,
1987 struct gui *ui = &main_ui;
1989 if (response == GTK_RESPONSE_OK) {
1990 gtk_widget_destroy(widget);
1991 ui->error_info_bar = NULL;
1995 void report_error(GError *error)
1997 struct gui *ui = &main_ui;
1999 if (ui->error_info_bar == NULL) {
2000 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2003 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2004 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2007 ui->error_label = gtk_label_new(error->message);
2008 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2009 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2011 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2012 gtk_widget_show_all(ui->vbox);
2015 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2016 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2020 struct connection_widgets
2027 static void hostname_cb(GtkEntry *entry, gpointer data)
2029 struct connection_widgets *cw = data;
2030 int uses_net = 0, is_localhost = 0;
2035 * Check whether to display the 'auto start backend' box
2036 * or not. Show it if we are a localhost and using network,
2037 * or using a socket.
2039 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2040 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2045 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2046 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2047 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2048 !strcmp(text, "ip6-loopback"))
2052 if (!uses_net || is_localhost) {
2053 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2054 gtk_widget_set_sensitive(cw->button, 1);
2056 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2057 gtk_widget_set_sensitive(cw->button, 0);
2061 static int get_connection_details(char **host, int *port, int *type,
2064 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2065 struct connection_widgets cw;
2068 dialog = gtk_dialog_new_with_buttons("Connection details",
2069 GTK_WINDOW(main_ui.window),
2070 GTK_DIALOG_DESTROY_WITH_PARENT,
2071 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2072 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2074 frame = gtk_frame_new("Hostname / socket name");
2075 /* gtk_dialog_get_content_area() is 2.14 and newer */
2076 vbox = GTK_DIALOG(dialog)->vbox;
2077 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2079 box = gtk_vbox_new(FALSE, 6);
2080 gtk_container_add(GTK_CONTAINER(frame), box);
2082 hbox = gtk_hbox_new(TRUE, 10);
2083 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2084 cw.hentry = gtk_entry_new();
2085 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2086 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2088 frame = gtk_frame_new("Port");
2089 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2090 box = gtk_vbox_new(FALSE, 10);
2091 gtk_container_add(GTK_CONTAINER(frame), box);
2093 hbox = gtk_hbox_new(TRUE, 4);
2094 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2095 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2097 frame = gtk_frame_new("Type");
2098 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2099 box = gtk_vbox_new(FALSE, 10);
2100 gtk_container_add(GTK_CONTAINER(frame), box);
2102 hbox = gtk_hbox_new(TRUE, 4);
2103 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2105 cw.combo = gtk_combo_box_new_text();
2106 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2107 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2108 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2109 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2111 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2113 frame = gtk_frame_new("Options");
2114 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2115 box = gtk_vbox_new(FALSE, 10);
2116 gtk_container_add(GTK_CONTAINER(frame), box);
2118 hbox = gtk_hbox_new(TRUE, 4);
2119 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2121 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2122 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2123 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.");
2124 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2127 * Connect edit signal, so we can show/not-show the auto start button
2129 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2130 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2132 gtk_widget_show_all(dialog);
2134 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2135 gtk_widget_destroy(dialog);
2139 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2140 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2142 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2143 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2144 *type = Fio_client_ipv4;
2145 else if (!strncmp(typeentry, "IPv6", 4))
2146 *type = Fio_client_ipv6;
2148 *type = Fio_client_socket;
2151 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2153 gtk_widget_destroy(dialog);
2157 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2159 struct gfio_client *gc;
2161 gc = malloc(sizeof(*gc));
2162 memset(gc, 0, sizeof(*gc));
2164 gc->client = fio_get_client(client);
2168 client->client_data = gc;
2171 static GtkWidget *new_client_page(struct gui_entry *ge);
2173 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2175 struct gui_entry *ge;
2177 ge = malloc(sizeof(*ge));
2178 memset(ge, 0, sizeof(*ge));
2179 ge->state = GE_STATE_NEW;
2180 INIT_FLIST_HEAD(&ge->list);
2181 flist_add_tail(&ge->list, &ui->list);
2186 static struct gui_entry *get_new_ge_with_tab(const char *name)
2188 struct gui_entry *ge;
2190 ge = alloc_new_gui_entry(&main_ui);
2192 ge->vbox = new_client_page(ge);
2193 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2195 ge->page_label = gtk_label_new(name);
2196 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2198 gtk_widget_show_all(main_ui.window);
2202 static void file_new(GtkWidget *w, gpointer data)
2204 struct gui *ui = (struct gui *) data;
2205 struct gui_entry *ge;
2207 ge = get_new_ge_with_tab("Untitled");
2208 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2212 * Return the 'ge' corresponding to the tab. If the active tab is the
2213 * main tab, open a new tab.
2215 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2217 struct flist_head *entry;
2218 struct gui_entry *ge;
2223 return get_new_ge_with_tab("Untitled");
2229 flist_for_each(entry, &main_ui.list) {
2230 ge = flist_entry(entry, struct gui_entry, list);
2231 if (ge->page_num == cur_page)
2238 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2243 * Main tab is tab 0, so any current page other than 0 holds
2246 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2248 return get_ge_from_page(cur_page, NULL);
2253 static void file_close(GtkWidget *w, gpointer data)
2255 struct gui *ui = (struct gui *) data;
2256 struct gui_entry *ge;
2259 * Can't close the main tab
2261 ge = get_ge_from_cur_tab(ui);
2263 gtk_widget_destroy(ge->vbox);
2267 if (!flist_empty(&ui->list)) {
2268 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2275 static void file_add_recent(struct gui *ui, const gchar *uri)
2279 memset(&grd, 0, sizeof(grd));
2280 grd.display_name = strdup("gfio");
2281 grd.description = strdup("Fio job file");
2282 grd.mime_type = strdup(GFIO_MIME);
2283 grd.app_name = strdup(g_get_application_name());
2284 grd.app_exec = strdup("gfio %f/%u");
2286 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2289 static gchar *get_filename_from_uri(const gchar *uri)
2291 if (strncmp(uri, "file://", 7))
2294 return strdup(uri + 7);
2297 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2300 struct fio_client *client;
2303 filename = get_filename_from_uri(uri);
2305 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2306 ge->job_files[ge->nr_job_files] = strdup(filename);
2309 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2313 error = g_error_new(g_quark_from_string("fio"), 1,
2314 "Failed to add client %s", host);
2315 report_error(error);
2316 g_error_free(error);
2320 gfio_client_added(ge, client);
2321 file_add_recent(ge->ui, uri);
2325 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2327 int port, type, server_start;
2328 struct gui_entry *ge;
2331 int ret, ge_is_new = 0;
2334 * Creates new tab if current tab is the main window, or the
2335 * current tab already has a client.
2337 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2338 ge = get_ge_from_page(cur_page, &ge_is_new);
2340 ge = get_new_ge_with_tab("Untitled");
2344 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2346 if (get_connection_details(&host, &port, &type, &server_start)) {
2348 gtk_widget_destroy(ge->vbox);
2353 ret = do_file_open(ge, uri, host, type, port);
2359 gfio_start_server();
2362 gtk_widget_destroy(ge->vbox);
2368 static void recent_open(GtkAction *action, gpointer data)
2370 struct gui *ui = (struct gui *) data;
2371 GtkRecentInfo *info;
2374 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2375 uri = gtk_recent_info_get_uri(info);
2377 do_file_open_with_tab(ui, uri);
2380 static void file_open(GtkWidget *w, gpointer data)
2382 struct gui *ui = data;
2384 GSList *filenames, *fn_glist;
2385 GtkFileFilter *filter;
2387 dialog = gtk_file_chooser_dialog_new("Open File",
2388 GTK_WINDOW(ui->window),
2389 GTK_FILE_CHOOSER_ACTION_OPEN,
2390 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2391 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2393 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2395 filter = gtk_file_filter_new();
2396 gtk_file_filter_add_pattern(filter, "*.fio");
2397 gtk_file_filter_add_pattern(filter, "*.job");
2398 gtk_file_filter_add_pattern(filter, "*.ini");
2399 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2400 gtk_file_filter_set_name(filter, "Fio job file");
2401 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2403 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2404 gtk_widget_destroy(dialog);
2408 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2410 gtk_widget_destroy(dialog);
2412 filenames = fn_glist;
2413 while (filenames != NULL) {
2414 if (do_file_open_with_tab(ui, filenames->data))
2416 filenames = g_slist_next(filenames);
2419 g_slist_free(fn_glist);
2422 static void file_save(GtkWidget *w, gpointer data)
2424 struct gui *ui = data;
2427 dialog = gtk_file_chooser_dialog_new("Save File",
2428 GTK_WINDOW(ui->window),
2429 GTK_FILE_CHOOSER_ACTION_SAVE,
2430 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2431 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2434 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2435 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2437 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2440 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2441 // save_job_file(filename);
2444 gtk_widget_destroy(dialog);
2447 static void view_log_destroy(GtkWidget *w, gpointer data)
2449 struct gui *ui = (struct gui *) data;
2451 gtk_widget_ref(ui->log_tree);
2452 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2453 gtk_widget_destroy(w);
2454 ui->log_view = NULL;
2457 static void view_log(GtkWidget *w, gpointer data)
2459 GtkWidget *win, *scroll, *vbox, *box;
2460 struct gui *ui = (struct gui *) data;
2465 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2466 gtk_window_set_title(GTK_WINDOW(win), "Log");
2467 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2469 scroll = gtk_scrolled_window_new(NULL, NULL);
2471 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2473 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2475 box = gtk_hbox_new(TRUE, 0);
2476 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2477 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2478 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2480 vbox = gtk_vbox_new(TRUE, 5);
2481 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2483 gtk_container_add(GTK_CONTAINER(win), vbox);
2484 gtk_widget_show_all(win);
2487 static void connect_job_entry(GtkWidget *w, gpointer data)
2489 struct gui *ui = (struct gui *) data;
2490 struct gui_entry *ge;
2492 ge = get_ge_from_cur_tab(ui);
2494 connect_clicked(w, ge);
2497 static void send_job_entry(GtkWidget *w, gpointer data)
2499 struct gui *ui = (struct gui *) data;
2500 struct gui_entry *ge;
2502 ge = get_ge_from_cur_tab(ui);
2504 send_clicked(w, ge);
2508 static void edit_job_entry(GtkWidget *w, gpointer data)
2512 static void start_job_entry(GtkWidget *w, gpointer data)
2514 struct gui *ui = (struct gui *) data;
2515 struct gui_entry *ge;
2517 ge = get_ge_from_cur_tab(ui);
2519 start_job_clicked(w, ge);
2522 static void view_results(GtkWidget *w, gpointer data)
2524 struct gui *ui = (struct gui *) data;
2525 struct gfio_client *gc;
2526 struct gui_entry *ge;
2528 ge = get_ge_from_cur_tab(ui);
2532 if (ge->results_window)
2536 if (gc && gc->nr_results)
2537 gfio_display_end_results(gc);
2540 static void __update_graph_limits(struct gfio_graphs *g)
2542 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2543 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2546 static void update_graph_limits(void)
2548 struct flist_head *entry;
2549 struct gui_entry *ge;
2551 __update_graph_limits(&main_ui.graphs);
2553 flist_for_each(entry, &main_ui.list) {
2554 ge = flist_entry(entry, struct gui_entry, list);
2555 __update_graph_limits(&ge->graphs);
2559 static void preferences(GtkWidget *w, gpointer data)
2561 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2562 GtkWidget *hbox, *spin, *entry, *spin_int;
2565 dialog = gtk_dialog_new_with_buttons("Preferences",
2566 GTK_WINDOW(main_ui.window),
2567 GTK_DIALOG_DESTROY_WITH_PARENT,
2568 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2569 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2572 frame = gtk_frame_new("Graphing");
2573 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2574 vbox = gtk_vbox_new(FALSE, 6);
2575 gtk_container_add(GTK_CONTAINER(frame), vbox);
2577 hbox = gtk_hbox_new(FALSE, 5);
2578 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2579 entry = gtk_label_new("Font face to use for graph labels");
2580 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2582 font = gtk_font_button_new();
2583 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2585 box = gtk_vbox_new(FALSE, 6);
2586 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2588 hbox = gtk_hbox_new(FALSE, 5);
2589 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2590 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2591 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2593 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2595 box = gtk_vbox_new(FALSE, 6);
2596 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2598 hbox = gtk_hbox_new(FALSE, 5);
2599 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2600 entry = gtk_label_new("Client ETA request interval (msec)");
2601 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2603 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2604 frame = gtk_frame_new("Debug logging");
2605 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2606 vbox = gtk_vbox_new(FALSE, 6);
2607 gtk_container_add(GTK_CONTAINER(frame), vbox);
2609 box = gtk_hbox_new(FALSE, 6);
2610 gtk_container_add(GTK_CONTAINER(vbox), box);
2612 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2614 for (i = 0; i < FD_DEBUG_MAX; i++) {
2616 box = gtk_hbox_new(FALSE, 6);
2617 gtk_container_add(GTK_CONTAINER(vbox), box);
2621 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2622 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2623 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2626 gtk_widget_show_all(dialog);
2628 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2629 gtk_widget_destroy(dialog);
2633 for (i = 0; i < FD_DEBUG_MAX; i++) {
2636 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2638 fio_debug |= (1UL << i);
2641 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2642 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2643 update_graph_limits();
2644 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2646 gtk_widget_destroy(dialog);
2649 static void about_dialog(GtkWidget *w, gpointer data)
2651 const char *authors[] = {
2652 "Jens Axboe <axboe@kernel.dk>",
2653 "Stephen Carmeron <stephenmcameron@gmail.com>",
2656 const char *license[] = {
2657 "Fio is free software; you can redistribute it and/or modify "
2658 "it under the terms of the GNU General Public License as published by "
2659 "the Free Software Foundation; either version 2 of the License, or "
2660 "(at your option) any later version.\n",
2661 "Fio is distributed in the hope that it will be useful, "
2662 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2663 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2664 "GNU General Public License for more details.\n",
2665 "You should have received a copy of the GNU General Public License "
2666 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2667 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2669 char *license_trans;
2671 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2672 license[2], "\n", NULL);
2674 gtk_show_about_dialog(NULL,
2675 "program-name", "gfio",
2676 "comments", "Gtk2 UI for fio",
2677 "license", license_trans,
2678 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2680 "version", fio_version_string,
2681 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2682 "logo-icon-name", "fio",
2684 "wrap-license", TRUE,
2687 g_free(license_trans);
2690 static GtkActionEntry menu_items[] = {
2691 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2692 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2693 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2694 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2695 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2696 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2697 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2698 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2699 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2700 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2701 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2702 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2703 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2704 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2705 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2706 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2707 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2709 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2711 static const gchar *ui_string = " \
2713 <menubar name=\"MainMenu\"> \
2714 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2715 <menuitem name=\"New\" action=\"NewFile\" /> \
2716 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2717 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2718 <separator name=\"Separator1\"/> \
2719 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2720 <separator name=\"Separator2\"/> \
2721 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2722 <separator name=\"Separator3\"/> \
2723 <placeholder name=\"FileRecentFiles\"/> \
2724 <separator name=\"Separator4\"/> \
2725 <menuitem name=\"Quit\" action=\"Quit\" /> \
2727 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2728 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2729 <separator name=\"Separator5\"/> \
2730 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2731 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2732 <separator name=\"Separator6\"/> \
2733 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2735 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2736 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2737 <separator name=\"Separator7\"/> \
2738 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2740 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2741 <menuitem name=\"About\" action=\"About\" /> \
2747 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2750 GtkActionGroup *action_group;
2753 action_group = gtk_action_group_new("Menu");
2754 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2756 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2757 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2759 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2761 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2764 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2765 GtkWidget *vbox, GtkUIManager *ui_manager)
2767 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2770 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2772 struct gui_entry *ge = (struct gui_entry *) data;
2775 index = gtk_combo_box_get_active(box);
2777 multitext_set_entry(&ge->eta.iotype, index);
2778 multitext_set_entry(&ge->eta.bs, index);
2779 multitext_set_entry(&ge->eta.ioengine, index);
2780 multitext_set_entry(&ge->eta.iodepth, index);
2783 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2785 struct gui_entry *ge = (struct gui_entry *) data;
2787 multitext_free(&ge->eta.iotype);
2788 multitext_free(&ge->eta.bs);
2789 multitext_free(&ge->eta.ioengine);
2790 multitext_free(&ge->eta.iodepth);
2793 static GtkWidget *new_client_page(struct gui_entry *ge)
2795 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2796 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2798 main_vbox = gtk_vbox_new(FALSE, 3);
2800 top_align = gtk_alignment_new(0, 0, 1, 0);
2801 top_vbox = gtk_vbox_new(FALSE, 3);
2802 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2803 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2805 probe = gtk_frame_new("Job");
2806 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2807 probe_frame = gtk_vbox_new(FALSE, 3);
2808 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2810 probe_box = gtk_hbox_new(FALSE, 3);
2811 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2812 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2813 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2814 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2815 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2817 probe_box = gtk_hbox_new(FALSE, 3);
2818 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2820 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2821 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2822 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2823 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2824 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
2825 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2826 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2827 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2828 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2830 probe_box = gtk_hbox_new(FALSE, 3);
2831 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2832 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2833 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2834 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2835 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2838 * Only add this if we have a commit rate
2841 probe_box = gtk_hbox_new(FALSE, 3);
2842 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2844 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2845 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2847 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2848 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2852 * Set up a drawing area and IOPS and bandwidth graphs
2854 ge->graphs.drawing_area = gtk_drawing_area_new();
2855 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2856 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2857 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2858 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2859 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2860 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2861 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2862 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2863 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2864 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2865 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2866 ge->graphs.drawing_area);
2867 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2869 setup_graphs(&ge->graphs);
2872 * Set up alignments for widgets at the bottom of ui,
2873 * align bottom left, expand horizontally but not vertically
2875 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2876 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2877 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2878 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2880 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2883 * Set up thread status progress bar
2885 ge->thread_status_pb = gtk_progress_bar_new();
2886 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2887 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2888 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2894 static GtkWidget *new_main_page(struct gui *ui)
2896 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2897 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2899 main_vbox = gtk_vbox_new(FALSE, 3);
2902 * Set up alignments for widgets at the top of ui,
2903 * align top left, expand horizontally but not vertically
2905 top_align = gtk_alignment_new(0, 0, 1, 0);
2906 top_vbox = gtk_vbox_new(FALSE, 0);
2907 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2908 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2910 probe = gtk_frame_new("Run statistics");
2911 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2912 probe_frame = gtk_vbox_new(FALSE, 3);
2913 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2915 probe_box = gtk_hbox_new(FALSE, 3);
2916 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2917 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2918 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2919 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2920 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2921 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2924 * Only add this if we have a commit rate
2927 probe_box = gtk_hbox_new(FALSE, 3);
2928 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2930 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2931 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2933 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2934 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2938 * Set up a drawing area and IOPS and bandwidth graphs
2940 ui->graphs.drawing_area = gtk_drawing_area_new();
2941 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2942 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2943 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2944 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2945 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2946 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2947 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2948 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2949 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2950 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2951 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2952 ui->graphs.drawing_area);
2953 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
2956 setup_graphs(&ui->graphs);
2959 * Set up alignments for widgets at the bottom of ui,
2960 * align bottom left, expand horizontally but not vertically
2962 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2963 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2964 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2965 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2968 * Set up thread status progress bar
2970 ui->thread_status_pb = gtk_progress_bar_new();
2971 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2972 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2973 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2978 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2979 guint page, gpointer data)
2982 struct gui *ui = (struct gui *) data;
2983 struct gui_entry *ge;
2986 set_job_menu_visible(ui, 0);
2987 set_view_results_visible(ui, 0);
2991 set_job_menu_visible(ui, 1);
2992 ge = get_ge_from_page(page, NULL);
2994 update_button_states(ui, ge);
2999 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3001 time_t time_a = gtk_recent_info_get_visited(a);
3002 time_t time_b = gtk_recent_info_get_visited(b);
3004 return time_b - time_a;
3007 static void add_recent_file_items(struct gui *ui)
3009 const gchar *gfio = g_get_application_name();
3010 GList *items, *item;
3013 if (ui->recent_ui_id) {
3014 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3015 gtk_ui_manager_ensure_update(ui->uimanager);
3017 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3019 if (ui->actiongroup) {
3020 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3021 g_object_unref(ui->actiongroup);
3023 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3025 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3027 items = gtk_recent_manager_get_items(ui->recentmanager);
3028 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3030 for (item = items; item && item->data; item = g_list_next(item)) {
3031 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3036 if (!gtk_recent_info_has_application(info, gfio))
3040 * We only support local files for now
3042 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3045 action_name = g_strdup_printf("RecentFile%u", i++);
3046 label = gtk_recent_info_get_display_name(info);
3048 action = g_object_new(GTK_TYPE_ACTION,
3049 "name", action_name,
3050 "label", label, NULL);
3052 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3053 gtk_recent_info_ref(info),
3054 (GDestroyNotify) gtk_recent_info_unref);
3057 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3059 gtk_action_group_add_action(ui->actiongroup, action);
3060 g_object_unref(action);
3062 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3063 "/MainMenu/FileMenu/FileRecentFiles",
3065 GTK_UI_MANAGER_MENUITEM, FALSE);
3067 g_free(action_name);
3073 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3077 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3078 gint x, gint y, GtkSelectionData *data,
3079 guint info, guint time)
3081 struct gui *ui = &main_ui;
3086 source = gtk_drag_get_source_widget(ctx);
3087 if (source && widget == gtk_widget_get_toplevel(source)) {
3088 gtk_drag_finish(ctx, FALSE, FALSE, time);
3092 uris = gtk_selection_data_get_uris(data);
3094 gtk_drag_finish(ctx, FALSE, FALSE, time);
3100 if (do_file_open_with_tab(ui, uris[i]))
3105 gtk_drag_finish(ctx, TRUE, FALSE, time);
3109 static void init_ui(int *argc, char **argv[], struct gui *ui)
3111 GtkSettings *settings;
3114 /* Magical g*thread incantation, you just need this thread stuff.
3115 * Without it, the update that happens in gfio_update_thread_status
3116 * doesn't really happen in a timely fashion, you need expose events
3118 if (!g_thread_supported())
3119 g_thread_init(NULL);
3122 gtk_init(argc, argv);
3123 settings = gtk_settings_get_default();
3124 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3126 gdk_color_parse("white", &white);
3128 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3129 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3130 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3132 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3133 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3135 ui->vbox = gtk_vbox_new(FALSE, 0);
3136 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3138 ui->uimanager = gtk_ui_manager_new();
3139 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3140 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3142 ui->recentmanager = gtk_recent_manager_get_default();
3143 add_recent_file_items(ui);
3145 ui->notebook = gtk_notebook_new();
3146 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3147 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3148 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3149 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3151 vbox = new_main_page(ui);
3152 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3153 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3154 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3156 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3158 gfio_ui_setup_log(ui);
3160 gtk_widget_show_all(ui->window);
3163 int main(int argc, char *argv[], char *envp[])
3165 if (initialize_fio(envp))
3167 if (fio_init_options())
3170 memset(&main_ui, 0, sizeof(main_ui));
3171 INIT_FLIST_HEAD(&main_ui.list);
3173 init_ui(&argc, &argv, &main_ui);
3175 gdk_threads_enter();
3177 gdk_threads_leave();