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 static 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 clear_ge_ui_info(struct gui_entry *ge)
122 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
123 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
124 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
125 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
127 /* should we empty it... */
128 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
130 multitext_update_entry(&ge->eta.iotype, 0, "");
131 multitext_update_entry(&ge->eta.bs, 0, "");
132 multitext_update_entry(&ge->eta.ioengine, 0, "");
133 multitext_update_entry(&ge->eta.iodepth, 0, "");
134 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
135 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
136 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
137 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
138 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
139 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
142 static void show_info_dialog(struct gui *ui, const char *title,
145 GtkWidget *dialog, *content, *label;
147 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
148 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
149 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
151 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
152 label = gtk_label_new(message);
153 gtk_container_add(GTK_CONTAINER(content), label);
154 gtk_widget_show_all(dialog);
155 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
156 gtk_dialog_run(GTK_DIALOG(dialog));
157 gtk_widget_destroy(dialog);
160 static void set_menu_entry_text(struct gui *ui, const char *path,
165 w = gtk_ui_manager_get_widget(ui->uimanager, path);
167 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
169 fprintf(stderr, "gfio: can't find path %s\n", path);
173 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
177 w = gtk_ui_manager_get_widget(ui->uimanager, path);
179 gtk_widget_set_sensitive(w, show);
181 fprintf(stderr, "gfio: can't find path %s\n", path);
184 static void set_job_menu_visible(struct gui *ui, int visible)
186 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
189 static void set_view_results_visible(struct gui *ui, int visible)
191 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
194 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
196 if (s->tooltiptext[sensitive])
197 return s->tooltiptext[sensitive];
199 return s->tooltiptext[0];
202 static GtkWidget *add_button(GtkWidget *buttonbox,
203 struct button_spec *buttonspec, gpointer data)
205 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
206 gboolean sens = buttonspec->start_sensitive;
208 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
209 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
211 sens = buttonspec->start_sensitive;
212 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
213 gtk_widget_set_sensitive(button, sens);
218 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
223 for (i = 0; i < nbuttons; i++)
224 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
228 * Update sensitivity of job buttons and job menu items, based on the
229 * state of the client.
231 static void update_button_states(struct gui *ui, struct gui_entry *ge)
233 unsigned int connect_state, send_state, start_state, edit_state;
234 const char *connect_str = NULL;
240 sprintf(tmp, "Bad client state: %u\n", ge->state);
241 show_info_dialog(ui, "Error", tmp);
242 /* fall through to new state */
248 connect_str = "Connect";
252 case GE_STATE_CONNECTED:
255 connect_str = "Disconnect";
259 case GE_STATE_JOB_SENT:
262 connect_str = "Disconnect";
266 case GE_STATE_JOB_STARTED:
269 connect_str = "Disconnect";
273 case GE_STATE_JOB_RUNNING:
276 connect_str = "Disconnect";
280 case GE_STATE_JOB_DONE:
283 connect_str = "Connect";
289 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
290 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
291 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
292 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
293 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
295 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
296 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
298 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
299 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
300 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
302 if (ge->client && ge->client->nr_results)
303 set_view_results_visible(ui, 1);
305 set_view_results_visible(ui, 0);
308 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
311 update_button_states(ge->ui, ge);
314 static void gfio_ui_setup_log(struct gui *ui)
316 GtkTreeSelection *selection;
318 GtkWidget *tree_view;
320 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
322 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
323 gtk_widget_set_can_focus(tree_view, FALSE);
325 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
326 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
327 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
328 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
330 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
331 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
332 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
333 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
335 ui->log_model = model;
336 ui->log_tree = tree_view;
339 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
342 double xdim, double ydim)
347 g = graph_new(xdim, ydim, gfio_graph_font);
348 graph_title(g, title);
349 graph_x_title(g, "Percentile");
351 for (i = 0; i < len; i++) {
354 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
355 graph_add_label(g, fbuf);
356 graph_add_data(g, fbuf, (double) ovals[i]);
362 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
368 GType types[FIO_IO_U_LIST_MAX_LEN];
369 GtkWidget *tree_view;
370 GtkTreeSelection *selection;
375 for (i = 0; i < len; i++)
376 types[i] = G_TYPE_INT;
378 model = gtk_list_store_newv(len, types);
380 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
381 gtk_widget_set_can_focus(tree_view, FALSE);
383 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
384 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
386 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
387 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
389 for (i = 0; i < len; i++) {
392 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
393 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
396 gtk_list_store_append(model, &iter);
398 for (i = 0; i < len; i++) {
400 ovals[i] = (ovals[i] + 999) / 1000;
401 gtk_list_store_set(model, &iter, i, ovals[i], -1);
407 static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
412 cr = gdk_cairo_create(w->window);
414 if (graph_has_tooltips(g)) {
415 g_object_set(w, "has-tooltip", TRUE, NULL);
416 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
419 cairo_set_source_rgb(cr, 0, 0, 0);
420 bar_graph_draw(g, cr);
426 static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
429 struct graph *g = data;
431 graph_set_size(g, w->allocation.width, w->allocation.height);
432 graph_set_size(g, w->allocation.width, w->allocation.height);
433 graph_set_position(g, 0, 0);
437 static void gfio_show_clat_percentiles(struct gfio_client *gc,
438 GtkWidget *vbox, struct thread_stat *ts,
441 unsigned int *io_u_plat = ts->io_u_plat[ddir];
442 unsigned long nr = ts->clat_stat[ddir].samples;
443 fio_fp64_t *plist = ts->percentile_list;
444 unsigned int *ovals, len, minv, maxv, scale_down;
446 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
447 struct gui_entry *ge = gc->ge;
450 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
455 * We default to usecs, but if the value range is such that we
456 * should scale down to msecs, do that.
458 if (minv > 2000 && maxv > 99999) {
466 sprintf(tmp, "Completion percentiles (%s)", base);
467 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
468 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
470 frame = gtk_frame_new(tmp);
471 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
473 completion_vbox = gtk_vbox_new(FALSE, 3);
474 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
475 hbox = gtk_hbox_new(FALSE, 3);
476 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
477 drawing_area = gtk_drawing_area_new();
478 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
479 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
480 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
481 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
482 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
484 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
490 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
491 unsigned long max, double mean, double dev)
493 const char *base = "(usec)";
494 GtkWidget *hbox, *label, *frame;
498 if (!usec_to_msec(&min, &max, &mean, &dev))
501 minp = num2str(min, 6, 1, 0);
502 maxp = num2str(max, 6, 1, 0);
504 sprintf(tmp, "%s %s", name, base);
505 frame = gtk_frame_new(tmp);
506 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
508 hbox = gtk_hbox_new(FALSE, 3);
509 gtk_container_add(GTK_CONTAINER(frame), hbox);
511 label = new_info_label_in_frame(hbox, "Minimum");
512 gtk_label_set_text(GTK_LABEL(label), minp);
513 label = new_info_label_in_frame(hbox, "Maximum");
514 gtk_label_set_text(GTK_LABEL(label), maxp);
515 label = new_info_label_in_frame(hbox, "Average");
516 sprintf(tmp, "%5.02f", mean);
517 gtk_label_set_text(GTK_LABEL(label), tmp);
518 label = new_info_label_in_frame(hbox, "Standard deviation");
519 sprintf(tmp, "%5.02f", dev);
520 gtk_label_set_text(GTK_LABEL(label), tmp);
531 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
532 struct group_run_stats *rs,
533 struct thread_stat *ts, int ddir)
535 const char *ddir_label[2] = { "Read", "Write" };
536 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
537 unsigned long min[3], max[3], runt;
538 unsigned long long bw, iops;
539 unsigned int flags = 0;
540 double mean[3], dev[3];
541 char *io_p, *bw_p, *iops_p;
544 if (!ts->runtime[ddir])
547 i2p = is_power_of_2(rs->kb_base);
548 runt = ts->runtime[ddir];
550 bw = (1000 * ts->io_bytes[ddir]) / runt;
551 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
552 bw_p = num2str(bw, 6, 1, i2p);
554 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
555 iops_p = num2str(iops, 6, 1, 0);
557 box = gtk_hbox_new(FALSE, 3);
558 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
560 frame = gtk_frame_new(ddir_label[ddir]);
561 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
563 main_vbox = gtk_vbox_new(FALSE, 3);
564 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
566 box = gtk_hbox_new(FALSE, 3);
567 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
569 label = new_info_label_in_frame(box, "IO");
570 gtk_label_set_text(GTK_LABEL(label), io_p);
571 label = new_info_label_in_frame(box, "Bandwidth");
572 gtk_label_set_text(GTK_LABEL(label), bw_p);
573 label = new_info_label_in_frame(box, "IOPS");
574 gtk_label_set_text(GTK_LABEL(label), iops_p);
575 label = new_info_label_in_frame(box, "Runtime (msec)");
576 label_set_int_value(label, ts->runtime[ddir]);
578 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
579 double p_of_agg = 100.0;
580 const char *bw_str = "KB";
584 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
585 if (p_of_agg > 100.0)
589 if (mean[0] > 999999.9) {
597 sprintf(tmp, "Bandwidth (%s)", bw_str);
598 frame = gtk_frame_new(tmp);
599 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
601 box = gtk_hbox_new(FALSE, 3);
602 gtk_container_add(GTK_CONTAINER(frame), box);
604 label = new_info_label_in_frame(box, "Minimum");
605 label_set_int_value(label, min[0]);
606 label = new_info_label_in_frame(box, "Maximum");
607 label_set_int_value(label, max[0]);
608 label = new_info_label_in_frame(box, "Percentage of jobs");
609 sprintf(tmp, "%3.2f%%", p_of_agg);
610 gtk_label_set_text(GTK_LABEL(label), tmp);
611 label = new_info_label_in_frame(box, "Average");
612 sprintf(tmp, "%5.02f", mean[0]);
613 gtk_label_set_text(GTK_LABEL(label), tmp);
614 label = new_info_label_in_frame(box, "Standard deviation");
615 sprintf(tmp, "%5.02f", dev[0]);
616 gtk_label_set_text(GTK_LABEL(label), tmp);
619 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
621 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
623 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
627 frame = gtk_frame_new("Latency");
628 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
630 vbox = gtk_vbox_new(FALSE, 3);
631 gtk_container_add(GTK_CONTAINER(frame), vbox);
633 if (flags & GFIO_SLAT)
634 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
635 if (flags & GFIO_CLAT)
636 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
637 if (flags & GFIO_LAT)
638 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
641 if (ts->clat_percentiles)
642 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
649 static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
652 double xdim, double ydim)
657 g = graph_new(xdim, ydim, gfio_graph_font);
658 graph_title(g, title);
659 graph_x_title(g, "Buckets");
661 for (i = 0; i < len; i++) {
662 graph_add_label(g, labels[i]);
663 graph_add_data(g, labels[i], lat[i]);
669 static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
672 GtkWidget *tree_view;
673 GtkTreeSelection *selection;
679 types = malloc(num * sizeof(GType));
681 for (i = 0; i < num; i++)
682 types[i] = G_TYPE_STRING;
684 model = gtk_list_store_newv(num, types);
688 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
689 gtk_widget_set_can_focus(tree_view, FALSE);
691 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
692 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
694 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
695 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
697 for (i = 0; i < num; i++)
698 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
700 gtk_list_store_append(model, &iter);
702 for (i = 0; i < num; i++) {
706 sprintf(fbuf, "0.00");
708 sprintf(fbuf, "%3.2f%%", lat[i]);
710 gtk_list_store_set(model, &iter, i, fbuf, -1);
716 static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
717 struct thread_stat *ts)
719 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
720 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
721 "250u", "500u", "750u", "1m", "2m",
722 "4m", "10m", "20m", "50m", "100m",
723 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
725 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
726 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
727 struct gui_entry *ge = gc->ge;
729 stat_calc_lat_u(ts, io_u_lat);
730 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
733 * Found out which first bucket has entries, and which last bucket
736 for (i = 0; i < total; i++) {
737 if (io_u_lat[i] == 0.00)
751 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
752 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
754 frame = gtk_frame_new("Latency buckets");
755 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
757 completion_vbox = gtk_vbox_new(FALSE, 3);
758 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
759 hbox = gtk_hbox_new(FALSE, 3);
760 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
762 drawing_area = gtk_drawing_area_new();
763 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
764 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
765 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
766 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
767 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
769 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
772 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
774 GtkWidget *box, *frame, *entry;
775 double usr_cpu, sys_cpu;
776 unsigned long runtime;
779 runtime = ts->total_run_time;
781 double runt = (double) runtime;
783 usr_cpu = (double) ts->usr_time * 100 / runt;
784 sys_cpu = (double) ts->sys_time * 100 / runt;
790 frame = gtk_frame_new("OS resources");
791 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
793 box = gtk_hbox_new(FALSE, 3);
794 gtk_container_add(GTK_CONTAINER(frame), box);
796 entry = new_info_entry_in_frame(box, "User CPU");
797 sprintf(tmp, "%3.2f%%", usr_cpu);
798 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
799 entry = new_info_entry_in_frame(box, "System CPU");
800 sprintf(tmp, "%3.2f%%", sys_cpu);
801 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
802 entry = new_info_entry_in_frame(box, "Context switches");
803 entry_set_int_value(entry, ts->ctx);
804 entry = new_info_entry_in_frame(box, "Major faults");
805 entry_set_int_value(entry, ts->majf);
806 entry = new_info_entry_in_frame(box, "Minor faults");
807 entry_set_int_value(entry, ts->minf);
809 static void gfio_add_sc_depths_tree(GtkListStore *model,
810 struct thread_stat *ts, unsigned int len,
813 double io_u_dist[FIO_IO_U_MAP_NR];
815 /* Bits 0, and 3-8 */
816 const int add_mask = 0x1f9;
820 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
822 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
824 gtk_list_store_append(model, &iter);
826 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
828 for (i = 1, j = 0; i < len; i++) {
831 if (!(add_mask & (1UL << (i - 1))))
832 sprintf(fbuf, "0.0%%");
834 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
838 gtk_list_store_set(model, &iter, i, fbuf, -1);
843 static void gfio_add_total_depths_tree(GtkListStore *model,
844 struct thread_stat *ts, unsigned int len)
846 double io_u_dist[FIO_IO_U_MAP_NR];
848 /* Bits 1-6, and 8 */
849 const int add_mask = 0x17e;
852 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
854 gtk_list_store_append(model, &iter);
856 gtk_list_store_set(model, &iter, 0, "Total", -1);
858 for (i = 1, j = 0; i < len; i++) {
861 if (!(add_mask & (1UL << (i - 1))))
862 sprintf(fbuf, "0.0%%");
864 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
868 gtk_list_store_set(model, &iter, i, fbuf, -1);
873 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
875 GtkWidget *frame, *box, *tree_view;
876 GtkTreeSelection *selection;
878 GType types[FIO_IO_U_MAP_NR + 1];
881 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
883 frame = gtk_frame_new("IO depths");
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 for (i = 0; i < NR_LABELS; i++)
890 types[i] = G_TYPE_STRING;
892 model = gtk_list_store_newv(NR_LABELS, types);
894 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
895 gtk_widget_set_can_focus(tree_view, FALSE);
897 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
898 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
900 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
901 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
903 for (i = 0; i < NR_LABELS; i++)
904 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
906 gfio_add_total_depths_tree(model, ts, NR_LABELS);
907 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
908 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
910 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
913 static gboolean results_window_delete(GtkWidget *w, gpointer data)
915 struct gui_entry *ge = (struct gui_entry *) data;
917 gtk_widget_destroy(w);
918 ge->results_window = NULL;
919 ge->results_notebook = NULL;
923 static void results_close(GtkWidget *w, gpointer *data)
925 struct gui_entry *ge = (struct gui_entry *) data;
927 gtk_widget_destroy(ge->results_window);
930 static GtkActionEntry results_menu_items[] = {
931 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
932 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
933 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
935 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
937 static const gchar *results_ui_string = " \
939 <menubar name=\"MainMenu\"> \
940 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
941 <menuitem name=\"Close\" action=\"CloseFile\" /> \
943 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
949 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
951 GtkActionGroup *action_group;
955 ge->results_uimanager = gtk_ui_manager_new();
957 action_group = gtk_action_group_new("ResultsMenu");
958 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
960 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
961 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
963 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
965 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
969 static GtkWidget *get_results_window(struct gui_entry *ge)
971 GtkWidget *win, *notebook, *vbox;
973 if (ge->results_window)
974 return ge->results_notebook;
976 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
977 gtk_window_set_title(GTK_WINDOW(win), "Results");
978 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
979 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
980 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
982 vbox = gtk_vbox_new(FALSE, 0);
983 gtk_container_add(GTK_CONTAINER(win), vbox);
985 ge->results_menu = get_results_menubar(win, ge);
986 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
988 notebook = gtk_notebook_new();
989 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
990 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
991 gtk_container_add(GTK_CONTAINER(vbox), notebook);
993 ge->results_window = win;
994 ge->results_notebook = notebook;
995 return ge->results_notebook;
998 static void disk_util_destroy(GtkWidget *w, gpointer data)
1000 struct gui_entry *ge = (struct gui_entry *) data;
1002 ge->disk_util_vbox = NULL;
1003 gtk_widget_destroy(w);
1006 static GtkWidget *get_scrolled_window(gint border_width)
1010 scroll = gtk_scrolled_window_new(NULL, NULL);
1011 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1012 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1017 static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1019 GtkWidget *vbox, *box, *scroll, *res_notebook;
1021 if (ge->disk_util_vbox)
1022 return ge->disk_util_vbox;
1024 scroll = get_scrolled_window(5);
1025 vbox = gtk_vbox_new(FALSE, 3);
1026 box = gtk_hbox_new(FALSE, 0);
1027 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1029 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1030 res_notebook = get_results_window(ge);
1032 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1033 ge->disk_util_vbox = box;
1034 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1036 return ge->disk_util_vbox;
1039 static int __gfio_disk_util_show(GtkWidget *res_notebook,
1040 struct gfio_client *gc, struct cmd_du_pdu *p)
1042 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
1043 struct gui_entry *ge = gc->ge;
1047 util_vbox = gfio_disk_util_get_vbox(ge);
1049 vbox = gtk_vbox_new(FALSE, 3);
1050 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
1052 frame = gtk_frame_new((char *) p->dus.name);
1053 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1055 box = gtk_vbox_new(FALSE, 3);
1056 gtk_container_add(GTK_CONTAINER(frame), box);
1058 frame = gtk_frame_new("Read");
1059 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1060 vbox = gtk_hbox_new(TRUE, 3);
1061 gtk_container_add(GTK_CONTAINER(frame), vbox);
1062 entry = new_info_entry_in_frame(vbox, "IOs");
1063 entry_set_int_value(entry, p->dus.ios[0]);
1064 entry = new_info_entry_in_frame(vbox, "Merges");
1065 entry_set_int_value(entry, p->dus.merges[0]);
1066 entry = new_info_entry_in_frame(vbox, "Sectors");
1067 entry_set_int_value(entry, p->dus.sectors[0]);
1068 entry = new_info_entry_in_frame(vbox, "Ticks");
1069 entry_set_int_value(entry, p->dus.ticks[0]);
1071 frame = gtk_frame_new("Write");
1072 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1073 vbox = gtk_hbox_new(TRUE, 3);
1074 gtk_container_add(GTK_CONTAINER(frame), vbox);
1075 entry = new_info_entry_in_frame(vbox, "IOs");
1076 entry_set_int_value(entry, p->dus.ios[1]);
1077 entry = new_info_entry_in_frame(vbox, "Merges");
1078 entry_set_int_value(entry, p->dus.merges[1]);
1079 entry = new_info_entry_in_frame(vbox, "Sectors");
1080 entry_set_int_value(entry, p->dus.sectors[1]);
1081 entry = new_info_entry_in_frame(vbox, "Ticks");
1082 entry_set_int_value(entry, p->dus.ticks[1]);
1084 frame = gtk_frame_new("Shared");
1085 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1086 vbox = gtk_hbox_new(TRUE, 3);
1087 gtk_container_add(GTK_CONTAINER(frame), vbox);
1088 entry = new_info_entry_in_frame(vbox, "IO ticks");
1089 entry_set_int_value(entry, p->dus.io_ticks);
1090 entry = new_info_entry_in_frame(vbox, "Time in queue");
1091 entry_set_int_value(entry, p->dus.time_in_queue);
1095 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1099 sprintf(tmp, "%3.2f%%", util);
1100 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1101 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1103 gtk_widget_show_all(ge->results_window);
1107 static int gfio_disk_util_show(struct gfio_client *gc)
1109 struct gui_entry *ge = gc->ge;
1110 GtkWidget *res_notebook;
1116 res_notebook = get_results_window(ge);
1118 for (i = 0; i < gc->nr_du; i++) {
1119 struct cmd_du_pdu *p = &gc->du[i];
1121 __gfio_disk_util_show(res_notebook, gc, p);
1124 gtk_widget_show_all(ge->results_window);
1128 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1129 struct group_run_stats *rs)
1131 unsigned int nr = gc->nr_results;
1133 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1134 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1135 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1139 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1140 struct thread_stat *ts,
1141 struct group_run_stats *rs)
1143 GtkWidget *box, *vbox, *entry, *scroll;
1145 scroll = gtk_scrolled_window_new(NULL, NULL);
1146 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1147 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1149 vbox = gtk_vbox_new(FALSE, 3);
1151 box = gtk_hbox_new(FALSE, 0);
1152 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1154 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1156 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1158 entry = new_info_entry_in_frame(box, "Name");
1159 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1160 if (strlen(ts->description)) {
1161 entry = new_info_entry_in_frame(box, "Description");
1162 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1164 entry = new_info_entry_in_frame(box, "Group ID");
1165 entry_set_int_value(entry, ts->groupid);
1166 entry = new_info_entry_in_frame(box, "Jobs");
1167 entry_set_int_value(entry, ts->members);
1168 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1169 entry_set_int_value(entry, ts->error);
1170 entry = new_info_entry_in_frame(box, "PID");
1171 entry_set_int_value(entry, ts->pid);
1173 if (ts->io_bytes[DDIR_READ])
1174 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1175 if (ts->io_bytes[DDIR_WRITE])
1176 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1178 gfio_show_latency_buckets(gc, vbox, ts);
1179 gfio_show_cpu_usage(vbox, ts);
1180 gfio_show_io_depths(vbox, ts);
1183 static void gfio_display_end_results(struct gfio_client *gc)
1185 struct gui_entry *ge = gc->ge;
1186 GtkWidget *res_notebook;
1189 res_notebook = get_results_window(ge);
1191 for (i = 0; i < gc->nr_results; i++) {
1192 struct end_results *e = &gc->results[i];
1194 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1197 if (gfio_disk_util_show(gc))
1198 gtk_widget_show_all(ge->results_window);
1201 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1202 struct group_run_stats *rs)
1204 struct gfio_client *gc = client->client_data;
1205 struct gui_entry *ge = gc->ge;
1207 gfio_add_end_results(gc, ts, rs);
1209 gdk_threads_enter();
1210 if (ge->results_window)
1211 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1213 gfio_display_end_results(gc);
1214 gdk_threads_leave();
1217 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1219 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1220 struct gui *ui = &main_ui;
1224 char tmp[64], timebuf[80];
1227 tm = localtime(&sec);
1228 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1229 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1231 gdk_threads_enter();
1233 gtk_list_store_append(ui->log_model, &iter);
1234 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1235 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1236 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1237 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1239 if (p->level == FIO_LOG_ERR)
1240 view_log(NULL, (gpointer) ui);
1242 gdk_threads_leave();
1245 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1247 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1248 struct gfio_client *gc = client->client_data;
1249 struct gui_entry *ge = gc->ge;
1250 unsigned int nr = gc->nr_du;
1252 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1253 memcpy(&gc->du[nr], p, sizeof(*p));
1256 gdk_threads_enter();
1257 if (ge->results_window)
1258 __gfio_disk_util_show(ge->results_notebook, gc, p);
1260 gfio_disk_util_show(gc);
1261 gdk_threads_leave();
1264 extern int sum_stat_clients;
1265 extern struct thread_stat client_ts;
1266 extern struct group_run_stats client_gs;
1268 static int sum_stat_nr;
1270 static void gfio_thread_status_op(struct fio_client *client,
1271 struct fio_net_cmd *cmd)
1273 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1275 gfio_display_ts(client, &p->ts, &p->rs);
1277 if (sum_stat_clients == 1)
1280 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1281 sum_group_stats(&client_gs, &p->rs);
1283 client_ts.members++;
1284 client_ts.thread_number = p->ts.thread_number;
1285 client_ts.groupid = p->ts.groupid;
1287 if (++sum_stat_nr == sum_stat_clients) {
1288 strcpy(client_ts.name, "All clients");
1289 gfio_display_ts(client, &client_ts, &client_gs);
1293 static void gfio_group_stats_op(struct fio_client *client,
1294 struct fio_net_cmd *cmd)
1296 /* We're ignoring group stats for now */
1299 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1302 struct gfio_graphs *g = data;
1304 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1305 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1306 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1307 graph_set_position(g->bandwidth_graph, 0, 0);
1311 static void draw_graph(struct graph *g, cairo_t *cr)
1313 line_graph_draw(g, cr);
1317 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1318 gboolean keyboard_mode, GtkTooltip *tooltip,
1321 struct gfio_graphs *g = data;
1322 const char *text = NULL;
1324 if (graph_contains_xy(g->iops_graph, x, y))
1325 text = graph_find_tooltip(g->iops_graph, x, y);
1326 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1327 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1330 gtk_tooltip_set_text(tooltip, text);
1337 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1339 struct gfio_graphs *g = p;
1342 cr = gdk_cairo_create(w->window);
1344 if (graph_has_tooltips(g->iops_graph) ||
1345 graph_has_tooltips(g->bandwidth_graph)) {
1346 g_object_set(w, "has-tooltip", TRUE, NULL);
1347 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1350 cairo_set_source_rgb(cr, 0, 0, 0);
1351 draw_graph(g->iops_graph, cr);
1352 draw_graph(g->bandwidth_graph, cr);
1359 * Client specific ETA
1361 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1363 struct gfio_client *gc = client->client_data;
1364 struct gui_entry *ge = gc->ge;
1365 static int eta_good;
1372 gdk_threads_enter();
1377 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1378 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1379 eta_to_str(eta_str, je->eta_sec);
1382 sprintf(tmp, "%u", je->nr_running);
1383 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1384 sprintf(tmp, "%u", je->files_open);
1385 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1388 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1389 if (je->m_rate || je->t_rate) {
1392 mr = num2str(je->m_rate, 4, 0, i2p);
1393 tr = num2str(je->t_rate, 4, 0, i2p);
1394 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1395 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1398 } else if (je->m_iops || je->t_iops)
1399 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1401 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1402 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1403 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1404 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1407 if (je->eta_sec != INT_MAX && je->nr_running) {
1411 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1412 strcpy(output, "-.-% done");
1416 sprintf(output, "%3.1f%% done", perc);
1419 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1420 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1422 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1423 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1425 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1426 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1427 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1428 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1430 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1431 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1432 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1433 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1442 char *dst = output + strlen(output);
1444 sprintf(dst, " - %s", eta_str);
1447 gfio_update_thread_status(ge, output, perc);
1448 gdk_threads_leave();
1452 * Update ETA in main window for all clients
1454 static void gfio_update_all_eta(struct jobs_eta *je)
1456 struct gui *ui = &main_ui;
1457 static int eta_good;
1463 gdk_threads_enter();
1468 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1469 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1470 eta_to_str(eta_str, je->eta_sec);
1474 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1475 if (je->m_rate || je->t_rate) {
1478 mr = num2str(je->m_rate, 4, 0, i2p);
1479 tr = num2str(je->t_rate, 4, 0, i2p);
1480 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1481 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1484 } else if (je->m_iops || je->t_iops)
1485 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1487 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1488 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1489 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1490 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1493 entry_set_int_value(ui->eta.jobs, je->nr_running);
1495 if (je->eta_sec != INT_MAX && je->nr_running) {
1499 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1500 strcpy(output, "-.-% done");
1504 sprintf(output, "%3.1f%% done", perc);
1507 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1508 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1510 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1511 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1513 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1514 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1515 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1516 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1518 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1519 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1520 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1521 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1530 char *dst = output + strlen(output);
1532 sprintf(dst, " - %s", eta_str);
1535 gfio_update_thread_status_all(output, perc);
1536 gdk_threads_leave();
1539 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1541 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1542 struct gfio_client *gc = client->client_data;
1543 struct gui_entry *ge = gc->ge;
1544 const char *os, *arch;
1547 os = fio_get_os_string(probe->os);
1551 arch = fio_get_arch_string(probe->arch);
1556 client->name = strdup((char *) probe->hostname);
1558 gdk_threads_enter();
1560 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1561 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1562 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1563 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1564 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1566 gfio_set_state(ge, GE_STATE_CONNECTED);
1568 gdk_threads_leave();
1571 static void gfio_update_thread_status(struct gui_entry *ge,
1572 char *status_message, double perc)
1574 static char message[100];
1575 const char *m = message;
1577 strncpy(message, status_message, sizeof(message) - 1);
1578 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1579 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1580 gtk_widget_queue_draw(main_ui.window);
1583 static void gfio_update_thread_status_all(char *status_message, double perc)
1585 struct gui *ui = &main_ui;
1586 static char message[100];
1587 const char *m = message;
1589 strncpy(message, status_message, sizeof(message) - 1);
1590 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1591 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1592 gtk_widget_queue_draw(ui->window);
1595 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1597 struct gfio_client *gc = client->client_data;
1599 gdk_threads_enter();
1600 gfio_set_state(gc->ge, GE_STATE_NEW);
1601 gdk_threads_leave();
1604 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1606 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1607 struct gfio_client *gc = client->client_data;
1608 struct thread_options *o = &gc->o;
1609 struct gui_entry *ge = gc->ge;
1610 char *c1, *c2, *c3, *c4;
1613 p->thread_number = le32_to_cpu(p->thread_number);
1614 p->groupid = le32_to_cpu(p->groupid);
1615 convert_thread_options_to_cpu(o, &p->top);
1617 gdk_threads_enter();
1619 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1621 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1622 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1624 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1625 multitext_add_entry(&ge->eta.iotype, tmp);
1627 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1628 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1629 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1630 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1631 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1636 multitext_add_entry(&ge->eta.bs, tmp);
1638 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1640 sprintf(tmp, "%u", o->iodepth);
1641 multitext_add_entry(&ge->eta.iodepth, tmp);
1643 multitext_set_entry(&ge->eta.iotype, 0);
1644 multitext_set_entry(&ge->eta.bs, 0);
1645 multitext_set_entry(&ge->eta.ioengine, 0);
1646 multitext_set_entry(&ge->eta.iodepth, 0);
1648 gfio_set_state(ge, GE_STATE_JOB_SENT);
1650 gdk_threads_leave();
1653 static void gfio_client_timed_out(struct fio_client *client)
1655 struct gfio_client *gc = client->client_data;
1658 gdk_threads_enter();
1660 gfio_set_state(gc->ge, GE_STATE_NEW);
1661 clear_ge_ui_info(gc->ge);
1663 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1664 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1666 gdk_threads_leave();
1669 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1671 struct gfio_client *gc = client->client_data;
1673 gdk_threads_enter();
1675 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1678 entry_set_int_value(gc->err_entry, client->error);
1680 gdk_threads_leave();
1683 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1685 struct gfio_client *gc = client->client_data;
1687 gdk_threads_enter();
1688 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1689 gdk_threads_leave();
1692 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1694 struct gfio_client *gc = client->client_data;
1696 gdk_threads_enter();
1697 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1698 gdk_threads_leave();
1701 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1703 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1707 struct client_ops gfio_client_ops = {
1708 .text = gfio_text_op,
1709 .disk_util = gfio_disk_util_op,
1710 .thread_status = gfio_thread_status_op,
1711 .group_stats = gfio_group_stats_op,
1712 .jobs_eta = gfio_update_client_eta,
1713 .eta = gfio_update_all_eta,
1714 .probe = gfio_probe_op,
1715 .quit = gfio_quit_op,
1716 .add_job = gfio_add_job_op,
1717 .timed_out = gfio_client_timed_out,
1718 .stop = gfio_client_stop,
1719 .start = gfio_client_start,
1720 .job_start = gfio_client_job_start,
1721 .iolog = gfio_client_iolog,
1722 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1723 .stay_connected = 1,
1724 .client_type = FIO_CLIENT_TYPE_GUI,
1728 * FIXME: need more handling here
1730 static void ge_destroy(struct gui_entry *ge)
1732 struct gfio_client *gc = ge->client;
1734 if (gc && gc->client) {
1735 if (ge->state >= GE_STATE_CONNECTED)
1736 fio_client_terminate(gc->client);
1738 fio_put_client(gc->client);
1741 flist_del(&ge->list);
1745 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1749 static void gfio_quit(struct gui *ui)
1751 struct gui_entry *ge;
1753 while (!flist_empty(&ui->list)) {
1754 ge = flist_entry(ui->list.next, struct gui_entry, list);
1761 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1762 __attribute__((unused)) gpointer data)
1767 static void *job_thread(void *arg)
1769 struct gui *ui = arg;
1771 ui->handler_running = 1;
1772 fio_handle_clients(&gfio_client_ops);
1773 ui->handler_running = 0;
1777 static int send_job_files(struct gui_entry *ge)
1779 struct gfio_client *gc = ge->client;
1782 for (i = 0; i < ge->nr_job_files; i++) {
1783 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1787 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1788 report_error(error);
1789 g_error_free(error);
1794 free(ge->job_files[i]);
1795 ge->job_files[i] = NULL;
1797 while (i < ge->nr_job_files) {
1798 free(ge->job_files[i]);
1799 ge->job_files[i] = NULL;
1803 free(ge->job_files);
1804 ge->job_files = NULL;
1805 ge->nr_job_files = 0;
1809 static void *server_thread(void *arg)
1812 gfio_server_running = 1;
1813 fio_start_server(NULL);
1814 gfio_server_running = 0;
1818 static void gfio_start_server(void)
1820 struct gui *ui = &main_ui;
1822 if (!gfio_server_running) {
1823 gfio_server_running = 1;
1824 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1825 pthread_detach(ui->server_t);
1829 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1832 struct gui_entry *ge = data;
1833 struct gfio_client *gc = ge->client;
1836 fio_start_client(gc->client);
1839 static void file_open(GtkWidget *w, gpointer data);
1841 static void connect_clicked(GtkWidget *widget, gpointer data)
1843 struct gui_entry *ge = data;
1844 struct gfio_client *gc = ge->client;
1846 if (ge->state == GE_STATE_NEW) {
1849 if (!ge->nr_job_files)
1850 file_open(widget, ge->ui);
1851 if (!ge->nr_job_files)
1856 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1857 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1858 ret = fio_client_connect(gc->client);
1860 if (!ge->ui->handler_running)
1861 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1862 gfio_set_state(ge, GE_STATE_CONNECTED);
1866 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1867 report_error(error);
1868 g_error_free(error);
1871 fio_client_terminate(gc->client);
1872 gfio_set_state(ge, GE_STATE_NEW);
1873 clear_ge_ui_info(ge);
1877 static void send_clicked(GtkWidget *widget, gpointer data)
1879 struct gui_entry *ge = data;
1881 if (send_job_files(ge)) {
1884 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);
1885 report_error(error);
1886 g_error_free(error);
1888 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
1892 static void on_info_bar_response(GtkWidget *widget, gint response,
1895 struct gui *ui = &main_ui;
1897 if (response == GTK_RESPONSE_OK) {
1898 gtk_widget_destroy(widget);
1899 ui->error_info_bar = NULL;
1903 static void report_error(GError *error)
1905 struct gui *ui = &main_ui;
1907 if (ui->error_info_bar == NULL) {
1908 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1911 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1912 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1915 ui->error_label = gtk_label_new(error->message);
1916 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1917 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1919 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1920 gtk_widget_show_all(ui->vbox);
1923 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1924 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1928 struct connection_widgets
1935 static void hostname_cb(GtkEntry *entry, gpointer data)
1937 struct connection_widgets *cw = data;
1938 int uses_net = 0, is_localhost = 0;
1943 * Check whether to display the 'auto start backend' box
1944 * or not. Show it if we are a localhost and using network,
1945 * or using a socket.
1947 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1948 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1953 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1954 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1955 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1956 !strcmp(text, "ip6-loopback"))
1960 if (!uses_net || is_localhost) {
1961 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1962 gtk_widget_set_sensitive(cw->button, 1);
1964 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1965 gtk_widget_set_sensitive(cw->button, 0);
1969 static int get_connection_details(char **host, int *port, int *type,
1972 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1973 struct connection_widgets cw;
1976 dialog = gtk_dialog_new_with_buttons("Connection details",
1977 GTK_WINDOW(main_ui.window),
1978 GTK_DIALOG_DESTROY_WITH_PARENT,
1979 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1980 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1982 frame = gtk_frame_new("Hostname / socket name");
1983 /* gtk_dialog_get_content_area() is 2.14 and newer */
1984 vbox = GTK_DIALOG(dialog)->vbox;
1985 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1987 box = gtk_vbox_new(FALSE, 6);
1988 gtk_container_add(GTK_CONTAINER(frame), box);
1990 hbox = gtk_hbox_new(TRUE, 10);
1991 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1992 cw.hentry = gtk_entry_new();
1993 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1994 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1996 frame = gtk_frame_new("Port");
1997 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1998 box = gtk_vbox_new(FALSE, 10);
1999 gtk_container_add(GTK_CONTAINER(frame), box);
2001 hbox = gtk_hbox_new(TRUE, 4);
2002 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2003 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2005 frame = gtk_frame_new("Type");
2006 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2007 box = gtk_vbox_new(FALSE, 10);
2008 gtk_container_add(GTK_CONTAINER(frame), box);
2010 hbox = gtk_hbox_new(TRUE, 4);
2011 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2013 cw.combo = gtk_combo_box_new_text();
2014 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2015 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2016 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2017 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2019 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2021 frame = gtk_frame_new("Options");
2022 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2023 box = gtk_vbox_new(FALSE, 10);
2024 gtk_container_add(GTK_CONTAINER(frame), box);
2026 hbox = gtk_hbox_new(TRUE, 4);
2027 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2029 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2030 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2031 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.");
2032 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2035 * Connect edit signal, so we can show/not-show the auto start button
2037 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2038 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2040 gtk_widget_show_all(dialog);
2042 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2043 gtk_widget_destroy(dialog);
2047 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2048 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2050 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2051 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2052 *type = Fio_client_ipv4;
2053 else if (!strncmp(typeentry, "IPv6", 4))
2054 *type = Fio_client_ipv6;
2056 *type = Fio_client_socket;
2059 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2061 gtk_widget_destroy(dialog);
2065 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2067 struct gfio_client *gc;
2069 gc = malloc(sizeof(*gc));
2070 memset(gc, 0, sizeof(*gc));
2072 gc->client = fio_get_client(client);
2076 client->client_data = gc;
2079 static GtkWidget *new_client_page(struct gui_entry *ge);
2081 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2083 struct gui_entry *ge;
2085 ge = malloc(sizeof(*ge));
2086 memset(ge, 0, sizeof(*ge));
2087 ge->state = GE_STATE_NEW;
2088 INIT_FLIST_HEAD(&ge->list);
2089 flist_add_tail(&ge->list, &ui->list);
2094 static struct gui_entry *get_new_ge_with_tab(const char *name)
2096 struct gui_entry *ge;
2098 ge = alloc_new_gui_entry(&main_ui);
2100 ge->vbox = new_client_page(ge);
2101 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2103 ge->page_label = gtk_label_new(name);
2104 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2106 gtk_widget_show_all(main_ui.window);
2110 static void file_new(GtkWidget *w, gpointer data)
2112 struct gui *ui = (struct gui *) data;
2113 struct gui_entry *ge;
2115 ge = get_new_ge_with_tab("Untitled");
2116 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2120 * Return the 'ge' corresponding to the tab. If the active tab is the
2121 * main tab, open a new tab.
2123 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2125 struct flist_head *entry;
2126 struct gui_entry *ge;
2131 return get_new_ge_with_tab("Untitled");
2137 flist_for_each(entry, &main_ui.list) {
2138 ge = flist_entry(entry, struct gui_entry, list);
2139 if (ge->page_num == cur_page)
2146 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2151 * Main tab is tab 0, so any current page other than 0 holds
2154 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2156 return get_ge_from_page(cur_page, NULL);
2161 static void file_close(GtkWidget *w, gpointer data)
2163 struct gui *ui = (struct gui *) data;
2164 struct gui_entry *ge;
2167 * Can't close the main tab
2169 ge = get_ge_from_cur_tab(ui);
2171 gtk_widget_destroy(ge->vbox);
2175 if (!flist_empty(&ui->list)) {
2176 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2183 static void file_add_recent(struct gui *ui, const gchar *uri)
2187 memset(&grd, 0, sizeof(grd));
2188 grd.display_name = strdup("gfio");
2189 grd.description = strdup("Fio job file");
2190 grd.mime_type = strdup(GFIO_MIME);
2191 grd.app_name = strdup(g_get_application_name());
2192 grd.app_exec = strdup("gfio %f/%u");
2194 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2197 static gchar *get_filename_from_uri(const gchar *uri)
2199 if (strncmp(uri, "file://", 7))
2202 return strdup(uri + 7);
2205 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2208 struct fio_client *client;
2211 filename = get_filename_from_uri(uri);
2213 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2214 ge->job_files[ge->nr_job_files] = strdup(filename);
2217 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2221 error = g_error_new(g_quark_from_string("fio"), 1,
2222 "Failed to add client %s", host);
2223 report_error(error);
2224 g_error_free(error);
2228 gfio_client_added(ge, client);
2229 file_add_recent(ge->ui, uri);
2233 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2235 int port, type, server_start;
2236 struct gui_entry *ge;
2239 int ret, ge_is_new = 0;
2242 * Creates new tab if current tab is the main window, or the
2243 * current tab already has a client.
2245 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2246 ge = get_ge_from_page(cur_page, &ge_is_new);
2248 ge = get_new_ge_with_tab("Untitled");
2252 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2254 if (get_connection_details(&host, &port, &type, &server_start)) {
2256 gtk_widget_destroy(ge->vbox);
2261 ret = do_file_open(ge, uri, host, type, port);
2267 gfio_start_server();
2270 gtk_widget_destroy(ge->vbox);
2276 static void recent_open(GtkAction *action, gpointer data)
2278 struct gui *ui = (struct gui *) data;
2279 GtkRecentInfo *info;
2282 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2283 uri = gtk_recent_info_get_uri(info);
2285 do_file_open_with_tab(ui, uri);
2288 static void file_open(GtkWidget *w, gpointer data)
2290 struct gui *ui = data;
2292 GSList *filenames, *fn_glist;
2293 GtkFileFilter *filter;
2295 dialog = gtk_file_chooser_dialog_new("Open File",
2296 GTK_WINDOW(ui->window),
2297 GTK_FILE_CHOOSER_ACTION_OPEN,
2298 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2299 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2301 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2303 filter = gtk_file_filter_new();
2304 gtk_file_filter_add_pattern(filter, "*.fio");
2305 gtk_file_filter_add_pattern(filter, "*.job");
2306 gtk_file_filter_add_pattern(filter, "*.ini");
2307 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2308 gtk_file_filter_set_name(filter, "Fio job file");
2309 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2311 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2312 gtk_widget_destroy(dialog);
2316 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2318 gtk_widget_destroy(dialog);
2320 filenames = fn_glist;
2321 while (filenames != NULL) {
2322 if (do_file_open_with_tab(ui, filenames->data))
2324 filenames = g_slist_next(filenames);
2327 g_slist_free(fn_glist);
2330 static void file_save(GtkWidget *w, gpointer data)
2332 struct gui *ui = data;
2335 dialog = gtk_file_chooser_dialog_new("Save File",
2336 GTK_WINDOW(ui->window),
2337 GTK_FILE_CHOOSER_ACTION_SAVE,
2338 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2339 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2342 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2343 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2345 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2348 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2349 // save_job_file(filename);
2352 gtk_widget_destroy(dialog);
2355 static void view_log_destroy(GtkWidget *w, gpointer data)
2357 struct gui *ui = (struct gui *) data;
2359 gtk_widget_ref(ui->log_tree);
2360 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2361 gtk_widget_destroy(w);
2362 ui->log_view = NULL;
2365 static void view_log(GtkWidget *w, gpointer data)
2367 GtkWidget *win, *scroll, *vbox, *box;
2368 struct gui *ui = (struct gui *) data;
2373 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2374 gtk_window_set_title(GTK_WINDOW(win), "Log");
2375 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2377 scroll = gtk_scrolled_window_new(NULL, NULL);
2379 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2381 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2383 box = gtk_hbox_new(TRUE, 0);
2384 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2385 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2386 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2388 vbox = gtk_vbox_new(TRUE, 5);
2389 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2391 gtk_container_add(GTK_CONTAINER(win), vbox);
2392 gtk_widget_show_all(win);
2395 static void connect_job_entry(GtkWidget *w, gpointer data)
2397 struct gui *ui = (struct gui *) data;
2398 struct gui_entry *ge;
2400 ge = get_ge_from_cur_tab(ui);
2402 connect_clicked(w, ge);
2405 static void send_job_entry(GtkWidget *w, gpointer data)
2407 struct gui *ui = (struct gui *) data;
2408 struct gui_entry *ge;
2410 ge = get_ge_from_cur_tab(ui);
2412 send_clicked(w, ge);
2416 static void edit_job_entry(GtkWidget *w, gpointer data)
2418 struct gui *ui = (struct gui *) data;
2419 struct gui_entry *ge;
2421 ge = get_ge_from_cur_tab(ui);
2422 if (ge && ge->client)
2423 gopt_get_options_window(ui->window, &ge->client->o);
2426 static void start_job_entry(GtkWidget *w, gpointer data)
2428 struct gui *ui = (struct gui *) data;
2429 struct gui_entry *ge;
2431 ge = get_ge_from_cur_tab(ui);
2433 start_job_clicked(w, ge);
2436 static void view_results(GtkWidget *w, gpointer data)
2438 struct gui *ui = (struct gui *) data;
2439 struct gfio_client *gc;
2440 struct gui_entry *ge;
2442 ge = get_ge_from_cur_tab(ui);
2446 if (ge->results_window)
2450 if (gc && gc->nr_results)
2451 gfio_display_end_results(gc);
2454 static void __update_graph_limits(struct gfio_graphs *g)
2456 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2457 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2460 static void update_graph_limits(void)
2462 struct flist_head *entry;
2463 struct gui_entry *ge;
2465 __update_graph_limits(&main_ui.graphs);
2467 flist_for_each(entry, &main_ui.list) {
2468 ge = flist_entry(entry, struct gui_entry, list);
2469 __update_graph_limits(&ge->graphs);
2473 static void preferences(GtkWidget *w, gpointer data)
2475 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2476 GtkWidget *hbox, *spin, *entry, *spin_int;
2479 dialog = gtk_dialog_new_with_buttons("Preferences",
2480 GTK_WINDOW(main_ui.window),
2481 GTK_DIALOG_DESTROY_WITH_PARENT,
2482 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2483 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2486 frame = gtk_frame_new("Graphing");
2487 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2488 vbox = gtk_vbox_new(FALSE, 6);
2489 gtk_container_add(GTK_CONTAINER(frame), vbox);
2491 hbox = gtk_hbox_new(FALSE, 5);
2492 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2493 entry = gtk_label_new("Font face to use for graph labels");
2494 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2496 font = gtk_font_button_new();
2497 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2499 box = gtk_vbox_new(FALSE, 6);
2500 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2502 hbox = gtk_hbox_new(FALSE, 5);
2503 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2504 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2505 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2507 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2509 box = gtk_vbox_new(FALSE, 6);
2510 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2512 hbox = gtk_hbox_new(FALSE, 5);
2513 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2514 entry = gtk_label_new("Client ETA request interval (msec)");
2515 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2517 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2518 frame = gtk_frame_new("Debug logging");
2519 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2520 vbox = gtk_vbox_new(FALSE, 6);
2521 gtk_container_add(GTK_CONTAINER(frame), vbox);
2523 box = gtk_hbox_new(FALSE, 6);
2524 gtk_container_add(GTK_CONTAINER(vbox), box);
2526 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2528 for (i = 0; i < FD_DEBUG_MAX; i++) {
2530 box = gtk_hbox_new(FALSE, 6);
2531 gtk_container_add(GTK_CONTAINER(vbox), box);
2535 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2536 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2537 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2540 gtk_widget_show_all(dialog);
2542 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2543 gtk_widget_destroy(dialog);
2547 for (i = 0; i < FD_DEBUG_MAX; i++) {
2550 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2552 fio_debug |= (1UL << i);
2555 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2556 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2557 update_graph_limits();
2558 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2560 gtk_widget_destroy(dialog);
2563 static void about_dialog(GtkWidget *w, gpointer data)
2565 const char *authors[] = {
2566 "Jens Axboe <axboe@kernel.dk>",
2567 "Stephen Carmeron <stephenmcameron@gmail.com>",
2570 const char *license[] = {
2571 "Fio is free software; you can redistribute it and/or modify "
2572 "it under the terms of the GNU General Public License as published by "
2573 "the Free Software Foundation; either version 2 of the License, or "
2574 "(at your option) any later version.\n",
2575 "Fio is distributed in the hope that it will be useful, "
2576 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2577 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2578 "GNU General Public License for more details.\n",
2579 "You should have received a copy of the GNU General Public License "
2580 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2581 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2583 char *license_trans;
2585 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2586 license[2], "\n", NULL);
2588 gtk_show_about_dialog(NULL,
2589 "program-name", "gfio",
2590 "comments", "Gtk2 UI for fio",
2591 "license", license_trans,
2592 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2594 "version", fio_version_string,
2595 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2596 "logo-icon-name", "fio",
2598 "wrap-license", TRUE,
2601 g_free(license_trans);
2604 static GtkActionEntry menu_items[] = {
2605 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2606 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2607 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2608 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2609 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2610 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2611 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2612 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2613 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2614 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2615 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2616 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
2617 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2618 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2619 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2620 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2621 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2623 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2625 static const gchar *ui_string = " \
2627 <menubar name=\"MainMenu\"> \
2628 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2629 <menuitem name=\"New\" action=\"NewFile\" /> \
2630 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2631 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2632 <separator name=\"Separator1\"/> \
2633 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2634 <separator name=\"Separator2\"/> \
2635 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2636 <separator name=\"Separator3\"/> \
2637 <placeholder name=\"FileRecentFiles\"/> \
2638 <separator name=\"Separator4\"/> \
2639 <menuitem name=\"Quit\" action=\"Quit\" /> \
2641 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2642 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2643 <separator name=\"Separator5\"/> \
2644 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2645 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2646 <separator name=\"Separator6\"/> \
2647 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2649 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2650 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2651 <separator name=\"Separator7\"/> \
2652 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2654 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2655 <menuitem name=\"About\" action=\"About\" /> \
2661 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2664 GtkActionGroup *action_group;
2667 action_group = gtk_action_group_new("Menu");
2668 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2670 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2671 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2673 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2675 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2678 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2679 GtkWidget *vbox, GtkUIManager *ui_manager)
2681 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2684 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2686 struct gui_entry *ge = (struct gui_entry *) data;
2689 index = gtk_combo_box_get_active(box);
2691 multitext_set_entry(&ge->eta.iotype, index);
2692 multitext_set_entry(&ge->eta.bs, index);
2693 multitext_set_entry(&ge->eta.ioengine, index);
2694 multitext_set_entry(&ge->eta.iodepth, index);
2697 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2699 struct gui_entry *ge = (struct gui_entry *) data;
2701 multitext_free(&ge->eta.iotype);
2702 multitext_free(&ge->eta.bs);
2703 multitext_free(&ge->eta.ioengine);
2704 multitext_free(&ge->eta.iodepth);
2707 static GtkWidget *new_client_page(struct gui_entry *ge)
2709 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2710 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2712 main_vbox = gtk_vbox_new(FALSE, 3);
2714 top_align = gtk_alignment_new(0, 0, 1, 0);
2715 top_vbox = gtk_vbox_new(FALSE, 3);
2716 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2717 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2719 probe = gtk_frame_new("Job");
2720 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2721 probe_frame = gtk_vbox_new(FALSE, 3);
2722 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2724 probe_box = gtk_hbox_new(FALSE, 3);
2725 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2726 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2727 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2728 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2729 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2731 probe_box = gtk_hbox_new(FALSE, 3);
2732 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2734 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2735 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2736 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2737 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2738 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
2739 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2740 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2741 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2742 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2744 probe_box = gtk_hbox_new(FALSE, 3);
2745 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2746 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2747 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2748 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2749 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2752 * Only add this if we have a commit rate
2755 probe_box = gtk_hbox_new(FALSE, 3);
2756 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2758 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2759 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2761 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2762 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2766 * Set up a drawing area and IOPS and bandwidth graphs
2768 ge->graphs.drawing_area = gtk_drawing_area_new();
2769 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2770 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2771 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2772 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2773 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2774 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2775 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2776 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2777 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2778 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2779 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2780 ge->graphs.drawing_area);
2781 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2783 setup_graphs(&ge->graphs);
2786 * Set up alignments for widgets at the bottom of ui,
2787 * align bottom left, expand horizontally but not vertically
2789 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2790 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2791 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2792 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2794 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2797 * Set up thread status progress bar
2799 ge->thread_status_pb = gtk_progress_bar_new();
2800 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2801 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2802 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2808 static GtkWidget *new_main_page(struct gui *ui)
2810 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2811 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2813 main_vbox = gtk_vbox_new(FALSE, 3);
2816 * Set up alignments for widgets at the top of ui,
2817 * align top left, expand horizontally but not vertically
2819 top_align = gtk_alignment_new(0, 0, 1, 0);
2820 top_vbox = gtk_vbox_new(FALSE, 0);
2821 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2822 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2824 probe = gtk_frame_new("Run statistics");
2825 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2826 probe_frame = gtk_vbox_new(FALSE, 3);
2827 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2829 probe_box = gtk_hbox_new(FALSE, 3);
2830 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2831 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2832 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2833 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2834 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2835 ui->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 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2845 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2847 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2848 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2852 * Set up a drawing area and IOPS and bandwidth graphs
2854 ui->graphs.drawing_area = gtk_drawing_area_new();
2855 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2856 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2857 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2858 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2859 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2860 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2861 G_CALLBACK(on_config_drawing_area), &ui->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 ui->graphs.drawing_area);
2867 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
2870 setup_graphs(&ui->graphs);
2873 * Set up alignments for widgets at the bottom of ui,
2874 * align bottom left, expand horizontally but not vertically
2876 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2877 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2878 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2879 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2882 * Set up thread status progress bar
2884 ui->thread_status_pb = gtk_progress_bar_new();
2885 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2886 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2887 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2892 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2893 guint page, gpointer data)
2896 struct gui *ui = (struct gui *) data;
2897 struct gui_entry *ge;
2900 set_job_menu_visible(ui, 0);
2901 set_view_results_visible(ui, 0);
2905 set_job_menu_visible(ui, 1);
2906 ge = get_ge_from_page(page, NULL);
2908 update_button_states(ui, ge);
2913 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
2915 time_t time_a = gtk_recent_info_get_visited(a);
2916 time_t time_b = gtk_recent_info_get_visited(b);
2918 return time_b - time_a;
2921 static void add_recent_file_items(struct gui *ui)
2923 const gchar *gfio = g_get_application_name();
2924 GList *items, *item;
2927 if (ui->recent_ui_id) {
2928 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
2929 gtk_ui_manager_ensure_update(ui->uimanager);
2931 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
2933 if (ui->actiongroup) {
2934 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
2935 g_object_unref(ui->actiongroup);
2937 ui->actiongroup = gtk_action_group_new("RecentFileActions");
2939 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
2941 items = gtk_recent_manager_get_items(ui->recentmanager);
2942 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
2944 for (item = items; item && item->data; item = g_list_next(item)) {
2945 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
2950 if (!gtk_recent_info_has_application(info, gfio))
2954 * We only support local files for now
2956 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
2959 action_name = g_strdup_printf("RecentFile%u", i++);
2960 label = gtk_recent_info_get_display_name(info);
2962 action = g_object_new(GTK_TYPE_ACTION,
2963 "name", action_name,
2964 "label", label, NULL);
2966 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
2967 gtk_recent_info_ref(info),
2968 (GDestroyNotify) gtk_recent_info_unref);
2971 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
2973 gtk_action_group_add_action(ui->actiongroup, action);
2974 g_object_unref(action);
2976 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
2977 "/MainMenu/FileMenu/FileRecentFiles",
2979 GTK_UI_MANAGER_MENUITEM, FALSE);
2981 g_free(action_name);
2987 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
2991 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
2992 gint x, gint y, GtkSelectionData *data,
2993 guint info, guint time)
2995 struct gui *ui = &main_ui;
3000 source = gtk_drag_get_source_widget(ctx);
3001 if (source && widget == gtk_widget_get_toplevel(source)) {
3002 gtk_drag_finish(ctx, FALSE, FALSE, time);
3006 uris = gtk_selection_data_get_uris(data);
3008 gtk_drag_finish(ctx, FALSE, FALSE, time);
3014 if (do_file_open_with_tab(ui, uris[i]))
3019 gtk_drag_finish(ctx, TRUE, FALSE, time);
3023 static void init_ui(int *argc, char **argv[], struct gui *ui)
3025 GtkSettings *settings;
3028 /* Magical g*thread incantation, you just need this thread stuff.
3029 * Without it, the update that happens in gfio_update_thread_status
3030 * doesn't really happen in a timely fashion, you need expose events
3032 if (!g_thread_supported())
3033 g_thread_init(NULL);
3036 gtk_init(argc, argv);
3037 settings = gtk_settings_get_default();
3038 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3040 gdk_color_parse("white", &white);
3042 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3043 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3044 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3046 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3047 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3049 ui->vbox = gtk_vbox_new(FALSE, 0);
3050 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3052 ui->uimanager = gtk_ui_manager_new();
3053 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3054 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3056 ui->recentmanager = gtk_recent_manager_get_default();
3057 add_recent_file_items(ui);
3059 ui->notebook = gtk_notebook_new();
3060 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3061 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3062 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3063 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3065 vbox = new_main_page(ui);
3066 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3067 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3068 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3070 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3072 gfio_ui_setup_log(ui);
3074 gtk_widget_show_all(ui->window);
3077 int main(int argc, char *argv[], char *envp[])
3079 if (initialize_fio(envp))
3081 if (fio_init_options())
3084 memset(&main_ui, 0, sizeof(main_ui));
3085 INIT_FLIST_HEAD(&main_ui.list);
3087 init_ui(&argc, &argv, &main_ui);
3089 gdk_threads_enter();
3091 gdk_threads_leave();