2 * gfio - gui front end for fio - the flexible io tester
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 static int gfio_server_running;
36 static const char *gfio_graph_font;
37 static unsigned int gfio_graph_limit = 100;
39 static void view_log(GtkWidget *w, gpointer data);
41 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
43 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
45 static void connect_clicked(GtkWidget *widget, gpointer data);
46 static void start_job_clicked(GtkWidget *widget, gpointer data);
47 static void send_clicked(GtkWidget *widget, gpointer data);
49 static struct button_spec {
50 const char *buttontext;
52 const char *tooltiptext;
53 const int start_insensitive;
54 } buttonspeclist[] = {
55 #define CONNECT_BUTTON 0
57 #define START_JOB_BUTTON 2
58 { "Connect", connect_clicked, "Connect to host", 0 },
59 { "Send", send_clicked, "Send job description to host", 1 },
60 { "Start Job", start_job_clicked,
61 "Start the current job on the server", 1 },
71 struct multitext_widget {
74 unsigned int cur_text;
75 unsigned int max_text;
80 struct multitext_widget iotype;
81 struct multitext_widget ioengine;
82 struct multitext_widget iodepth;
90 GtkWidget *write_iops;
96 #define DRAWING_AREA_XDIM 1000
97 #define DRAWING_AREA_YDIM 400
98 GtkWidget *drawing_area;
99 struct graph *iops_graph;
100 struct graph *bandwidth_graph;
104 * Main window widgets and data
111 GtkWidget *bottomalign;
112 GtkWidget *thread_status_pb;
113 GtkWidget *buttonbox;
114 GtkWidget *scrolled_window;
116 GtkWidget *error_info_bar;
117 GtkWidget *error_label;
118 GtkListStore *log_model;
121 struct gfio_graphs graphs;
122 struct probe_widget probe;
123 struct eta_widget eta;
129 struct flist_head list;
136 struct flist_head list;
142 GtkWidget *bottomalign;
143 GtkWidget *job_notebook;
144 GtkWidget *thread_status_pb;
145 GtkWidget *buttonbox;
146 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
147 GtkWidget *scrolled_window;
149 GtkWidget *error_info_bar;
150 GtkWidget *error_label;
151 GtkWidget *results_notebook;
152 GtkWidget *results_window;
153 GtkListStore *log_model;
156 struct gfio_graphs graphs;
157 struct probe_widget probe;
158 struct eta_widget eta;
159 GtkWidget *page_label;
163 struct gfio_client *client;
169 struct gui_entry *ge;
170 struct fio_client *client;
171 GtkWidget *results_widget;
172 GtkWidget *disk_util_frame;
173 GtkWidget *err_entry;
174 unsigned int job_added;
175 struct thread_options o;
178 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
179 static void gfio_update_thread_status_all(char *status_message, double perc);
180 void report_error(GError *error);
182 static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
184 switch (power_of_ten) {
185 case 9: graph_y_title(g, "Billions of IOs / sec");
187 case 6: graph_y_title(g, "Millions of IOs / sec");
189 case 3: graph_y_title(g, "Thousands of IOs / sec");
192 default: graph_y_title(g, "IOs / sec");
197 static struct graph *setup_iops_graph(void)
201 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
202 graph_title(g, "IOPS");
203 graph_x_title(g, "Time (secs)");
204 graph_y_title(g, "IOs / sec");
205 graph_add_label(g, "Read IOPS");
206 graph_add_label(g, "Write IOPS");
207 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
208 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
209 line_graph_set_data_count_limit(g, gfio_graph_limit);
210 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
211 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
215 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
217 switch (power_of_ten) {
218 case 9: graph_y_title(g, "Petabytes / sec");
220 case 6: graph_y_title(g, "Gigabytes / sec");
222 case 3: graph_y_title(g, "Megabytes / sec");
225 default: graph_y_title(g, "Kilobytes / sec");
230 static struct graph *setup_bandwidth_graph(void)
234 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
235 graph_title(g, "Bandwidth");
236 graph_x_title(g, "Time (secs)");
237 graph_y_title(g, "Kbytes / sec");
238 graph_add_label(g, "Read Bandwidth");
239 graph_add_label(g, "Write Bandwidth");
240 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
241 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
242 line_graph_set_data_count_limit(g, 100);
243 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
244 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
249 static void setup_graphs(struct gfio_graphs *g)
251 g->iops_graph = setup_iops_graph();
252 g->bandwidth_graph = setup_bandwidth_graph();
255 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
257 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
258 mt->text[mt->max_text] = strdup(text);
262 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
264 if (index >= mt->max_text)
266 if (!mt->text || !mt->text[index])
269 mt->cur_text = index;
270 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
273 static void multitext_update_entry(struct multitext_widget *mt,
274 unsigned int index, const char *text)
280 free(mt->text[index]);
282 mt->text[index] = strdup(text);
283 if (mt->cur_text == index)
284 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
287 static void multitext_free(struct multitext_widget *mt)
291 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
293 for (i = 0; i < mt->max_text; i++) {
303 static void clear_ge_ui_info(struct gui_entry *ge)
305 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
306 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
307 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
308 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
310 /* should we empty it... */
311 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
313 multitext_update_entry(&ge->eta.iotype, 0, "");
314 multitext_update_entry(&ge->eta.ioengine, 0, "");
315 multitext_update_entry(&ge->eta.iodepth, 0, "");
316 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
317 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
318 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
319 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
320 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
321 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
324 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
326 GtkWidget *entry, *frame;
328 frame = gtk_frame_new(label);
329 entry = gtk_combo_box_new_text();
330 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
331 gtk_container_add(GTK_CONTAINER(frame), entry);
336 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
338 GtkWidget *entry, *frame;
340 frame = gtk_frame_new(label);
341 entry = gtk_entry_new();
342 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
343 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
344 gtk_container_add(GTK_CONTAINER(frame), entry);
349 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
351 GtkWidget *label_widget;
354 frame = gtk_frame_new(label);
355 label_widget = gtk_label_new(NULL);
356 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
357 gtk_container_add(GTK_CONTAINER(frame), label_widget);
362 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
364 GtkWidget *button, *box;
366 box = gtk_hbox_new(FALSE, 3);
367 gtk_container_add(GTK_CONTAINER(hbox), box);
369 button = gtk_spin_button_new_with_range(min, max, 1.0);
370 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
372 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
373 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
378 static void gfio_set_connected(struct gui_entry *ge, int connected)
381 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
383 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
384 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
387 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
388 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
389 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
390 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
394 static void label_set_int_value(GtkWidget *entry, unsigned int val)
398 sprintf(tmp, "%u", val);
399 gtk_label_set_text(GTK_LABEL(entry), tmp);
402 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
406 sprintf(tmp, "%u", val);
407 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
410 static void show_info_dialog(struct gui *ui, const char *title,
413 GtkWidget *dialog, *content, *label;
415 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
416 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
417 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
419 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
420 label = gtk_label_new(message);
421 gtk_container_add(GTK_CONTAINER(content), label);
422 gtk_widget_show_all(dialog);
423 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
424 gtk_dialog_run(GTK_DIALOG(dialog));
425 gtk_widget_destroy(dialog);
429 #define ALIGN_RIGHT 2
433 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
435 GtkCellRenderer *renderer;
436 GtkTreeViewColumn *col;
437 double xalign = 0.0; /* left as default */
438 PangoAlignment align;
441 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
442 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
444 visible = !(flags & INVISIBLE);
446 renderer = gtk_cell_renderer_text_new();
447 col = gtk_tree_view_column_new();
449 gtk_tree_view_column_set_title(col, title);
450 if (!(flags & UNSORTABLE))
451 gtk_tree_view_column_set_sort_column_id(col, index);
452 gtk_tree_view_column_set_resizable(col, TRUE);
453 gtk_tree_view_column_pack_start(col, renderer, TRUE);
454 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
455 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
457 case PANGO_ALIGN_LEFT:
460 case PANGO_ALIGN_CENTER:
463 case PANGO_ALIGN_RIGHT:
467 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
468 gtk_tree_view_column_set_visible(col, visible);
469 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
473 static void gfio_ui_setup_log(struct gui *ui)
475 GtkTreeSelection *selection;
477 GtkWidget *tree_view;
479 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
481 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
482 gtk_widget_set_can_focus(tree_view, FALSE);
484 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
485 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
486 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
487 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
489 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
490 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
491 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
492 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
494 ui->log_model = model;
495 ui->log_tree = tree_view;
498 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
504 GType types[FIO_IO_U_LIST_MAX_LEN];
505 GtkWidget *tree_view;
506 GtkTreeSelection *selection;
511 for (i = 0; i < len; i++)
512 types[i] = G_TYPE_INT;
514 model = gtk_list_store_newv(len, types);
516 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
517 gtk_widget_set_can_focus(tree_view, FALSE);
519 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
520 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
522 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
523 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
525 for (i = 0; i < len; i++) {
528 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
529 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
532 gtk_list_store_append(model, &iter);
534 for (i = 0; i < len; i++) {
536 ovals[i] = (ovals[i] + 999) / 1000;
537 gtk_list_store_set(model, &iter, i, ovals[i], -1);
543 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
546 unsigned int *io_u_plat = ts->io_u_plat[ddir];
547 unsigned long nr = ts->clat_stat[ddir].samples;
548 fio_fp64_t *plist = ts->percentile_list;
549 unsigned int *ovals, len, minv, maxv, scale_down;
551 GtkWidget *tree_view, *frame, *hbox;
554 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
559 * We default to usecs, but if the value range is such that we
560 * should scale down to msecs, do that.
562 if (minv > 2000 && maxv > 99999) {
570 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
572 sprintf(tmp, "Completion percentiles (%s)", base);
573 frame = gtk_frame_new(tmp);
574 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
576 hbox = gtk_hbox_new(FALSE, 3);
577 gtk_container_add(GTK_CONTAINER(frame), hbox);
579 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
585 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
586 unsigned long max, double mean, double dev)
588 const char *base = "(usec)";
589 GtkWidget *hbox, *label, *frame;
593 if (!usec_to_msec(&min, &max, &mean, &dev))
596 minp = num2str(min, 6, 1, 0);
597 maxp = num2str(max, 6, 1, 0);
599 sprintf(tmp, "%s %s", name, base);
600 frame = gtk_frame_new(tmp);
601 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
603 hbox = gtk_hbox_new(FALSE, 3);
604 gtk_container_add(GTK_CONTAINER(frame), hbox);
606 label = new_info_label_in_frame(hbox, "Minimum");
607 gtk_label_set_text(GTK_LABEL(label), minp);
608 label = new_info_label_in_frame(hbox, "Maximum");
609 gtk_label_set_text(GTK_LABEL(label), maxp);
610 label = new_info_label_in_frame(hbox, "Average");
611 sprintf(tmp, "%5.02f", mean);
612 gtk_label_set_text(GTK_LABEL(label), tmp);
613 label = new_info_label_in_frame(hbox, "Standard deviation");
614 sprintf(tmp, "%5.02f", dev);
615 gtk_label_set_text(GTK_LABEL(label), tmp);
626 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
627 struct thread_stat *ts, int ddir)
629 const char *ddir_label[2] = { "Read", "Write" };
630 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
631 unsigned long min[3], max[3], runt;
632 unsigned long long bw, iops;
633 unsigned int flags = 0;
634 double mean[3], dev[3];
635 char *io_p, *bw_p, *iops_p;
638 if (!ts->runtime[ddir])
641 i2p = is_power_of_2(rs->kb_base);
642 runt = ts->runtime[ddir];
644 bw = (1000 * ts->io_bytes[ddir]) / runt;
645 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
646 bw_p = num2str(bw, 6, 1, i2p);
648 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
649 iops_p = num2str(iops, 6, 1, 0);
651 box = gtk_hbox_new(FALSE, 3);
652 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
654 frame = gtk_frame_new(ddir_label[ddir]);
655 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
657 main_vbox = gtk_vbox_new(FALSE, 3);
658 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
660 box = gtk_hbox_new(FALSE, 3);
661 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
663 label = new_info_label_in_frame(box, "IO");
664 gtk_label_set_text(GTK_LABEL(label), io_p);
665 label = new_info_label_in_frame(box, "Bandwidth");
666 gtk_label_set_text(GTK_LABEL(label), bw_p);
667 label = new_info_label_in_frame(box, "IOPS");
668 gtk_label_set_text(GTK_LABEL(label), iops_p);
669 label = new_info_label_in_frame(box, "Runtime (msec)");
670 label_set_int_value(label, ts->runtime[ddir]);
672 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
673 double p_of_agg = 100.0;
674 const char *bw_str = "KB";
678 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
679 if (p_of_agg > 100.0)
683 if (mean[0] > 999999.9) {
691 sprintf(tmp, "Bandwidth (%s)", bw_str);
692 frame = gtk_frame_new(tmp);
693 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
695 box = gtk_hbox_new(FALSE, 3);
696 gtk_container_add(GTK_CONTAINER(frame), box);
698 label = new_info_label_in_frame(box, "Minimum");
699 label_set_int_value(label, min[0]);
700 label = new_info_label_in_frame(box, "Maximum");
701 label_set_int_value(label, max[0]);
702 label = new_info_label_in_frame(box, "Percentage of jobs");
703 sprintf(tmp, "%3.2f%%", p_of_agg);
704 gtk_label_set_text(GTK_LABEL(label), tmp);
705 label = new_info_label_in_frame(box, "Average");
706 sprintf(tmp, "%5.02f", mean[0]);
707 gtk_label_set_text(GTK_LABEL(label), tmp);
708 label = new_info_label_in_frame(box, "Standard deviation");
709 sprintf(tmp, "%5.02f", dev[0]);
710 gtk_label_set_text(GTK_LABEL(label), tmp);
713 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
715 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
717 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
721 frame = gtk_frame_new("Latency");
722 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
724 vbox = gtk_vbox_new(FALSE, 3);
725 gtk_container_add(GTK_CONTAINER(frame), vbox);
727 if (flags & GFIO_SLAT)
728 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
729 if (flags & GFIO_CLAT)
730 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
731 if (flags & GFIO_LAT)
732 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
735 if (ts->clat_percentiles)
736 gfio_show_clat_percentiles(main_vbox, ts, ddir);
744 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
747 GtkWidget *tree_view;
748 GtkTreeSelection *selection;
755 * Check if all are empty, in which case don't bother
757 for (i = 0, skipped = 0; i < num; i++)
764 types = malloc(num * sizeof(GType));
766 for (i = 0; i < num; i++)
767 types[i] = G_TYPE_STRING;
769 model = gtk_list_store_newv(num, types);
773 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
774 gtk_widget_set_can_focus(tree_view, FALSE);
776 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
777 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
779 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
780 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
782 for (i = 0; i < num; i++)
783 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
785 gtk_list_store_append(model, &iter);
787 for (i = 0; i < num; i++) {
791 sprintf(fbuf, "0.00");
793 sprintf(fbuf, "%3.2f%%", lat[i]);
795 gtk_list_store_set(model, &iter, i, fbuf, -1);
801 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
803 GtkWidget *box, *frame, *tree_view;
804 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
805 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
806 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
807 "250", "500", "750", "1000", };
808 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
809 "250", "500", "750", "1000", "2000",
812 stat_calc_lat_u(ts, io_u_lat_u);
813 stat_calc_lat_m(ts, io_u_lat_m);
815 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
817 frame = gtk_frame_new("Latency buckets (usec)");
818 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
820 box = gtk_hbox_new(FALSE, 3);
821 gtk_container_add(GTK_CONTAINER(frame), box);
822 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
825 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
827 frame = gtk_frame_new("Latency buckets (msec)");
828 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
830 box = gtk_hbox_new(FALSE, 3);
831 gtk_container_add(GTK_CONTAINER(frame), box);
832 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
836 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
838 GtkWidget *box, *frame, *entry;
839 double usr_cpu, sys_cpu;
840 unsigned long runtime;
843 runtime = ts->total_run_time;
845 double runt = (double) runtime;
847 usr_cpu = (double) ts->usr_time * 100 / runt;
848 sys_cpu = (double) ts->sys_time * 100 / runt;
854 frame = gtk_frame_new("OS resources");
855 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
857 box = gtk_hbox_new(FALSE, 3);
858 gtk_container_add(GTK_CONTAINER(frame), box);
860 entry = new_info_entry_in_frame(box, "User CPU");
861 sprintf(tmp, "%3.2f%%", usr_cpu);
862 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
863 entry = new_info_entry_in_frame(box, "System CPU");
864 sprintf(tmp, "%3.2f%%", sys_cpu);
865 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
866 entry = new_info_entry_in_frame(box, "Context switches");
867 entry_set_int_value(entry, ts->ctx);
868 entry = new_info_entry_in_frame(box, "Major faults");
869 entry_set_int_value(entry, ts->majf);
870 entry = new_info_entry_in_frame(box, "Minor faults");
871 entry_set_int_value(entry, ts->minf);
873 static void gfio_add_sc_depths_tree(GtkListStore *model,
874 struct thread_stat *ts, unsigned int len,
877 double io_u_dist[FIO_IO_U_MAP_NR];
879 /* Bits 0, and 3-8 */
880 const int add_mask = 0x1f9;
884 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
886 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
888 gtk_list_store_append(model, &iter);
890 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
892 for (i = 1, j = 0; i < len; i++) {
895 if (!(add_mask & (1UL << (i - 1))))
896 sprintf(fbuf, "0.0%%");
898 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
902 gtk_list_store_set(model, &iter, i, fbuf, -1);
907 static void gfio_add_total_depths_tree(GtkListStore *model,
908 struct thread_stat *ts, unsigned int len)
910 double io_u_dist[FIO_IO_U_MAP_NR];
912 /* Bits 1-6, and 8 */
913 const int add_mask = 0x17e;
916 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
918 gtk_list_store_append(model, &iter);
920 gtk_list_store_set(model, &iter, 0, "Total", -1);
922 for (i = 1, j = 0; i < len; i++) {
925 if (!(add_mask & (1UL << (i - 1))))
926 sprintf(fbuf, "0.0%%");
928 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
932 gtk_list_store_set(model, &iter, i, fbuf, -1);
937 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
939 GtkWidget *frame, *box, *tree_view;
940 GtkTreeSelection *selection;
942 GType types[FIO_IO_U_MAP_NR + 1];
945 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
947 frame = gtk_frame_new("IO depths");
948 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
950 box = gtk_hbox_new(FALSE, 3);
951 gtk_container_add(GTK_CONTAINER(frame), box);
953 for (i = 0; i < NR_LABELS; i++)
954 types[i] = G_TYPE_STRING;
956 model = gtk_list_store_newv(NR_LABELS, types);
958 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
959 gtk_widget_set_can_focus(tree_view, FALSE);
961 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
962 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
964 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
965 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
967 for (i = 0; i < NR_LABELS; i++)
968 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
970 gfio_add_total_depths_tree(model, ts, NR_LABELS);
971 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
972 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
974 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
977 static gboolean results_window_delete(GtkWidget *w, gpointer data)
979 struct gui_entry *ge = (struct gui_entry *) data;
981 gtk_widget_destroy(w);
982 ge->results_window = NULL;
983 ge->results_notebook = NULL;
987 static GtkWidget *get_results_window(struct gui_entry *ge)
989 GtkWidget *win, *notebook;
991 if (ge->results_window)
992 return ge->results_notebook;
994 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
995 gtk_window_set_title(GTK_WINDOW(win), "Results");
996 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
997 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
998 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1000 notebook = gtk_notebook_new();
1001 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1002 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1003 gtk_container_add(GTK_CONTAINER(win), notebook);
1005 ge->results_window = win;
1006 ge->results_notebook = notebook;
1007 return ge->results_notebook;
1010 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1011 struct group_run_stats *rs)
1013 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
1014 struct gfio_client *gc = client->client_data;
1016 gdk_threads_enter();
1018 res_win = get_results_window(gc->ge);
1020 scroll = gtk_scrolled_window_new(NULL, NULL);
1021 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1022 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1024 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);
1031 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1033 gc->results_widget = vbox;
1035 entry = new_info_entry_in_frame(box, "Name");
1036 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1037 if (strlen(ts->description)) {
1038 entry = new_info_entry_in_frame(box, "Description");
1039 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1041 entry = new_info_entry_in_frame(box, "Group ID");
1042 entry_set_int_value(entry, ts->groupid);
1043 entry = new_info_entry_in_frame(box, "Jobs");
1044 entry_set_int_value(entry, ts->members);
1045 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1046 entry_set_int_value(entry, ts->error);
1047 entry = new_info_entry_in_frame(box, "PID");
1048 entry_set_int_value(entry, ts->pid);
1050 if (ts->io_bytes[DDIR_READ])
1051 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1052 if (ts->io_bytes[DDIR_WRITE])
1053 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1055 gfio_show_latency_buckets(vbox, ts);
1056 gfio_show_cpu_usage(vbox, ts);
1057 gfio_show_io_depths(vbox, ts);
1059 gtk_widget_show_all(gc->ge->results_window);
1060 gdk_threads_leave();
1063 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1065 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1066 struct gui *ui = &main_ui;
1070 char tmp[64], timebuf[80];
1073 tm = localtime(&sec);
1074 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1075 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1077 gdk_threads_enter();
1079 gtk_list_store_append(ui->log_model, &iter);
1080 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1081 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1082 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1083 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1085 if (p->level == FIO_LOG_ERR)
1086 view_log(NULL, (gpointer) ui);
1088 gdk_threads_leave();
1091 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1093 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1094 struct gfio_client *gc = client->client_data;
1095 GtkWidget *box, *frame, *entry, *vbox;
1099 gdk_threads_enter();
1101 if (!gc->results_widget)
1104 if (!gc->disk_util_frame) {
1105 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1106 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1109 vbox = gtk_vbox_new(FALSE, 3);
1110 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1112 frame = gtk_frame_new((char *) p->dus.name);
1113 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1115 box = gtk_vbox_new(FALSE, 3);
1116 gtk_container_add(GTK_CONTAINER(frame), box);
1118 frame = gtk_frame_new("Read");
1119 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1120 vbox = gtk_hbox_new(TRUE, 3);
1121 gtk_container_add(GTK_CONTAINER(frame), vbox);
1122 entry = new_info_entry_in_frame(vbox, "IOs");
1123 entry_set_int_value(entry, p->dus.ios[0]);
1124 entry = new_info_entry_in_frame(vbox, "Merges");
1125 entry_set_int_value(entry, p->dus.merges[0]);
1126 entry = new_info_entry_in_frame(vbox, "Sectors");
1127 entry_set_int_value(entry, p->dus.sectors[0]);
1128 entry = new_info_entry_in_frame(vbox, "Ticks");
1129 entry_set_int_value(entry, p->dus.ticks[0]);
1131 frame = gtk_frame_new("Write");
1132 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1133 vbox = gtk_hbox_new(TRUE, 3);
1134 gtk_container_add(GTK_CONTAINER(frame), vbox);
1135 entry = new_info_entry_in_frame(vbox, "IOs");
1136 entry_set_int_value(entry, p->dus.ios[1]);
1137 entry = new_info_entry_in_frame(vbox, "Merges");
1138 entry_set_int_value(entry, p->dus.merges[1]);
1139 entry = new_info_entry_in_frame(vbox, "Sectors");
1140 entry_set_int_value(entry, p->dus.sectors[1]);
1141 entry = new_info_entry_in_frame(vbox, "Ticks");
1142 entry_set_int_value(entry, p->dus.ticks[1]);
1144 frame = gtk_frame_new("Shared");
1145 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1146 vbox = gtk_hbox_new(TRUE, 3);
1147 gtk_container_add(GTK_CONTAINER(frame), vbox);
1148 entry = new_info_entry_in_frame(vbox, "IO ticks");
1149 entry_set_int_value(entry, p->dus.io_ticks);
1150 entry = new_info_entry_in_frame(vbox, "Time in queue");
1151 entry_set_int_value(entry, p->dus.time_in_queue);
1155 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1159 sprintf(tmp, "%3.2f%%", util);
1160 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1161 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1163 gtk_widget_show_all(gc->results_widget);
1165 gdk_threads_leave();
1168 extern int sum_stat_clients;
1169 extern struct thread_stat client_ts;
1170 extern struct group_run_stats client_gs;
1172 static int sum_stat_nr;
1174 static void gfio_thread_status_op(struct fio_client *client,
1175 struct fio_net_cmd *cmd)
1177 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1179 gfio_display_ts(client, &p->ts, &p->rs);
1181 if (sum_stat_clients == 1)
1184 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1185 sum_group_stats(&client_gs, &p->rs);
1187 client_ts.members++;
1188 client_ts.groupid = p->ts.groupid;
1190 if (++sum_stat_nr == sum_stat_clients) {
1191 strcpy(client_ts.name, "All clients");
1192 gfio_display_ts(client, &client_ts, &client_gs);
1196 static void gfio_group_stats_op(struct fio_client *client,
1197 struct fio_net_cmd *cmd)
1199 /* We're ignoring group stats for now */
1202 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1205 struct gfio_graphs *g = data;
1207 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1208 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1209 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1210 graph_set_position(g->bandwidth_graph, 0, 0);
1214 static void draw_graph(struct graph *g, cairo_t *cr)
1216 line_graph_draw(g, cr);
1220 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1222 struct gfio_graphs *g = p;
1225 cr = gdk_cairo_create(w->window);
1226 cairo_set_source_rgb(cr, 0, 0, 0);
1227 draw_graph(g->iops_graph, cr);
1228 draw_graph(g->bandwidth_graph, cr);
1235 * Client specific ETA
1237 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1239 struct gfio_client *gc = client->client_data;
1240 struct gui_entry *ge = gc->ge;
1241 static int eta_good;
1248 gdk_threads_enter();
1253 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1254 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1255 eta_to_str(eta_str, je->eta_sec);
1258 sprintf(tmp, "%u", je->nr_running);
1259 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1260 sprintf(tmp, "%u", je->files_open);
1261 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1264 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1265 if (je->m_rate || je->t_rate) {
1268 mr = num2str(je->m_rate, 4, 0, i2p);
1269 tr = num2str(je->t_rate, 4, 0, i2p);
1270 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1271 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1274 } else if (je->m_iops || je->t_iops)
1275 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1277 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1278 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1279 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1280 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1283 if (je->eta_sec != INT_MAX && je->nr_running) {
1287 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1288 strcpy(output, "-.-% done");
1292 sprintf(output, "%3.1f%% done", perc);
1295 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1296 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1298 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1299 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1301 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1302 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1303 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1304 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1306 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1307 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1308 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1309 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1318 char *dst = output + strlen(output);
1320 sprintf(dst, " - %s", eta_str);
1323 gfio_update_thread_status(ge, output, perc);
1324 gdk_threads_leave();
1328 * Update ETA in main window for all clients
1330 static void gfio_update_all_eta(struct jobs_eta *je)
1332 struct gui *ui = &main_ui;
1333 static int eta_good;
1339 gdk_threads_enter();
1344 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1345 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1346 eta_to_str(eta_str, je->eta_sec);
1350 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1351 if (je->m_rate || je->t_rate) {
1354 mr = num2str(je->m_rate, 4, 0, i2p);
1355 tr = num2str(je->t_rate, 4, 0, i2p);
1356 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1357 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1360 } else if (je->m_iops || je->t_iops)
1361 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1363 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1364 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1365 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1366 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1369 entry_set_int_value(ui->eta.jobs, je->nr_running);
1371 if (je->eta_sec != INT_MAX && je->nr_running) {
1375 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1376 strcpy(output, "-.-% done");
1380 sprintf(output, "%3.1f%% done", perc);
1383 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1384 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1386 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1387 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1389 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1390 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1391 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1392 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1394 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1395 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1396 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1397 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1406 char *dst = output + strlen(output);
1408 sprintf(dst, " - %s", eta_str);
1411 gfio_update_thread_status_all(output, perc);
1412 gdk_threads_leave();
1415 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1417 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1418 struct gfio_client *gc = client->client_data;
1419 struct gui_entry *ge = gc->ge;
1420 const char *os, *arch;
1423 os = fio_get_os_string(probe->os);
1427 arch = fio_get_arch_string(probe->arch);
1432 client->name = strdup((char *) probe->hostname);
1434 gdk_threads_enter();
1436 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1437 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1438 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1439 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1440 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1442 gfio_set_connected(ge, 1);
1444 gdk_threads_leave();
1447 static void gfio_update_thread_status(struct gui_entry *ge,
1448 char *status_message, double perc)
1450 static char message[100];
1451 const char *m = message;
1453 strncpy(message, status_message, sizeof(message) - 1);
1454 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1455 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1456 gtk_widget_queue_draw(main_ui.window);
1459 static void gfio_update_thread_status_all(char *status_message, double perc)
1461 struct gui *ui = &main_ui;
1462 static char message[100];
1463 const char *m = message;
1465 strncpy(message, status_message, sizeof(message) - 1);
1466 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1467 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1468 gtk_widget_queue_draw(ui->window);
1471 static void gfio_quit_op(struct fio_client *client)
1473 struct gfio_client *gc = client->client_data;
1475 gdk_threads_enter();
1476 gfio_set_connected(gc->ge, 0);
1477 gdk_threads_leave();
1480 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1482 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1483 struct gfio_client *gc = client->client_data;
1484 struct thread_options *o = &gc->o;
1485 struct gui_entry *ge = gc->ge;
1488 convert_thread_options_to_cpu(o, &p->top);
1490 gdk_threads_enter();
1492 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1494 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1495 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1497 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1498 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1500 sprintf(tmp, "%u", o->iodepth);
1501 multitext_add_entry(&ge->eta.iodepth, tmp);
1503 multitext_set_entry(&ge->eta.iotype, 0);
1504 multitext_set_entry(&ge->eta.ioengine, 0);
1505 multitext_set_entry(&ge->eta.iodepth, 0);
1509 gdk_threads_leave();
1512 static void gfio_client_timed_out(struct fio_client *client)
1514 struct gfio_client *gc = client->client_data;
1517 gdk_threads_enter();
1519 gfio_set_connected(gc->ge, 0);
1520 clear_ge_ui_info(gc->ge);
1522 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1523 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1525 gdk_threads_leave();
1528 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1530 struct gfio_client *gc = client->client_data;
1532 gdk_threads_enter();
1534 gfio_set_connected(gc->ge, 0);
1537 entry_set_int_value(gc->err_entry, client->error);
1539 gdk_threads_leave();
1542 struct client_ops gfio_client_ops = {
1543 .text_op = gfio_text_op,
1544 .disk_util = gfio_disk_util_op,
1545 .thread_status = gfio_thread_status_op,
1546 .group_stats = gfio_group_stats_op,
1547 .jobs_eta = gfio_update_client_eta,
1548 .eta = gfio_update_all_eta,
1549 .probe = gfio_probe_op,
1550 .quit = gfio_quit_op,
1551 .add_job = gfio_add_job_op,
1552 .timed_out = gfio_client_timed_out,
1553 .stop = gfio_client_stop,
1554 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1555 .stay_connected = 1,
1558 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1559 __attribute__((unused)) gpointer data)
1564 static void *job_thread(void *arg)
1566 struct gui *ui = arg;
1568 ui->handler_running = 1;
1569 fio_handle_clients(&gfio_client_ops);
1570 ui->handler_running = 0;
1574 static int send_job_files(struct gui_entry *ge)
1576 struct gfio_client *gc = ge->client;
1579 for (i = 0; i < ge->nr_job_files; i++) {
1580 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1584 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1585 report_error(error);
1586 g_error_free(error);
1591 free(ge->job_files[i]);
1592 ge->job_files[i] = NULL;
1594 while (i < ge->nr_job_files) {
1595 free(ge->job_files[i]);
1596 ge->job_files[i] = NULL;
1603 static void *server_thread(void *arg)
1606 gfio_server_running = 1;
1607 fio_start_server(NULL);
1608 gfio_server_running = 0;
1612 static void gfio_start_server(void)
1614 struct gui *ui = &main_ui;
1616 if (!gfio_server_running) {
1617 gfio_server_running = 1;
1618 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1619 pthread_detach(ui->server_t);
1623 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1626 struct gui_entry *ge = data;
1627 struct gfio_client *gc = ge->client;
1629 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1630 fio_start_client(gc->client);
1633 static void file_open(GtkWidget *w, gpointer data);
1635 static void connect_clicked(GtkWidget *widget, gpointer data)
1637 struct gui_entry *ge = data;
1638 struct gfio_client *gc = ge->client;
1640 if (!ge->connected) {
1643 if (!ge->nr_job_files)
1644 file_open(widget, data);
1645 if (!ge->nr_job_files)
1648 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1649 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1650 ret = fio_client_connect(gc->client);
1652 if (!ge->ui->handler_running)
1653 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1654 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1655 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1659 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1660 report_error(error);
1661 g_error_free(error);
1664 fio_client_terminate(gc->client);
1665 gfio_set_connected(ge, 0);
1666 clear_ge_ui_info(ge);
1670 static void send_clicked(GtkWidget *widget, gpointer data)
1672 struct gui_entry *ge = data;
1674 if (send_job_files(ge)) {
1677 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);
1678 report_error(error);
1679 g_error_free(error);
1681 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1684 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1685 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1688 static GtkWidget *add_button(GtkWidget *buttonbox,
1689 struct button_spec *buttonspec, gpointer data)
1691 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1693 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1694 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1695 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1696 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1701 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1706 for (i = 0; i < nbuttons; i++)
1707 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1710 static void on_info_bar_response(GtkWidget *widget, gint response,
1713 struct gui *ui = &main_ui;
1715 if (response == GTK_RESPONSE_OK) {
1716 gtk_widget_destroy(widget);
1717 ui->error_info_bar = NULL;
1721 void report_error(GError *error)
1723 struct gui *ui = &main_ui;
1725 if (ui->error_info_bar == NULL) {
1726 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1729 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1730 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1733 ui->error_label = gtk_label_new(error->message);
1734 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1735 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1737 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1738 gtk_widget_show_all(ui->vbox);
1741 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1742 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1746 struct connection_widgets
1753 static void hostname_cb(GtkEntry *entry, gpointer data)
1755 struct connection_widgets *cw = data;
1756 int uses_net = 0, is_localhost = 0;
1761 * Check whether to display the 'auto start backend' box
1762 * or not. Show it if we are a localhost and using network,
1763 * or using a socket.
1765 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1766 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1771 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1772 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1773 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1774 !strcmp(text, "ip6-loopback"))
1778 if (!uses_net || is_localhost) {
1779 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1780 gtk_widget_set_sensitive(cw->button, 1);
1782 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1783 gtk_widget_set_sensitive(cw->button, 0);
1787 static int get_connection_details(char **host, int *port, int *type,
1790 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1791 struct connection_widgets cw;
1794 dialog = gtk_dialog_new_with_buttons("Connection details",
1795 GTK_WINDOW(main_ui.window),
1796 GTK_DIALOG_DESTROY_WITH_PARENT,
1797 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1798 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1800 frame = gtk_frame_new("Hostname / socket name");
1801 /* gtk_dialog_get_content_area() is 2.14 and newer */
1802 vbox = GTK_DIALOG(dialog)->vbox;
1803 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1805 box = gtk_vbox_new(FALSE, 6);
1806 gtk_container_add(GTK_CONTAINER(frame), box);
1808 hbox = gtk_hbox_new(TRUE, 10);
1809 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1810 cw.hentry = gtk_entry_new();
1811 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1812 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1814 frame = gtk_frame_new("Port");
1815 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1816 box = gtk_vbox_new(FALSE, 10);
1817 gtk_container_add(GTK_CONTAINER(frame), box);
1819 hbox = gtk_hbox_new(TRUE, 4);
1820 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1821 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1823 frame = gtk_frame_new("Type");
1824 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1825 box = gtk_vbox_new(FALSE, 10);
1826 gtk_container_add(GTK_CONTAINER(frame), box);
1828 hbox = gtk_hbox_new(TRUE, 4);
1829 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1831 cw.combo = gtk_combo_box_new_text();
1832 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1833 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1834 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1835 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1837 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1839 frame = gtk_frame_new("Options");
1840 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1841 box = gtk_vbox_new(FALSE, 10);
1842 gtk_container_add(GTK_CONTAINER(frame), box);
1844 hbox = gtk_hbox_new(TRUE, 4);
1845 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1847 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1848 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1849 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.");
1850 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1853 * Connect edit signal, so we can show/not-show the auto start button
1855 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1856 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1858 gtk_widget_show_all(dialog);
1860 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1861 gtk_widget_destroy(dialog);
1865 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1866 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1868 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1869 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1870 *type = Fio_client_ipv4;
1871 else if (!strncmp(typeentry, "IPv6", 4))
1872 *type = Fio_client_ipv6;
1874 *type = Fio_client_socket;
1877 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1879 gtk_widget_destroy(dialog);
1883 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1885 struct gfio_client *gc;
1887 gc = malloc(sizeof(*gc));
1888 memset(gc, 0, sizeof(*gc));
1890 gc->client = fio_get_client(client);
1894 client->client_data = gc;
1897 static GtkWidget *new_client_page(struct gui_entry *ge);
1899 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1901 struct gui_entry *ge;
1903 ge = malloc(sizeof(*ge));
1904 memset(ge, 0, sizeof(*ge));
1905 INIT_FLIST_HEAD(&ge->list);
1906 flist_add_tail(&ge->list, &ui->list);
1912 * FIXME: need more handling here
1914 static void ge_destroy(GtkWidget *w, gpointer data)
1916 struct gui_entry *ge = data;
1917 struct gfio_client *gc = ge->client;
1919 if (gc && gc->client) {
1921 fio_client_terminate(gc->client);
1923 fio_put_client(gc->client);
1926 flist_del(&ge->list);
1930 static struct gui_entry *get_new_ge_with_tab(const char *name)
1932 struct gui_entry *ge;
1934 ge = alloc_new_gui_entry(&main_ui);
1936 ge->vbox = new_client_page(ge);
1937 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1939 ge->page_label = gtk_label_new(name);
1940 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1942 gtk_widget_show_all(main_ui.window);
1946 static void file_new(GtkWidget *w, gpointer data)
1948 struct gui *ui = (struct gui *) data;
1949 struct gui_entry *ge;
1951 ge = get_new_ge_with_tab("Untitled");
1952 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1956 * Return the 'ge' corresponding to the tab. If the active tab is the
1957 * main tab, open a new tab.
1959 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1961 struct flist_head *entry;
1962 struct gui_entry *ge;
1965 return get_new_ge_with_tab("Untitled");
1967 flist_for_each(entry, &main_ui.list) {
1968 ge = flist_entry(entry, struct gui_entry, list);
1969 if (ge->page_num == cur_page)
1976 static void file_close(GtkWidget *w, gpointer data)
1978 struct gui *ui = (struct gui *) data;
1982 * Can't close the main tab
1984 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1986 struct gui_entry *ge = get_ge_from_page(cur_page);
1988 gtk_widget_destroy(ge->vbox);
1992 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
1995 static void file_open(GtkWidget *w, gpointer data)
1997 struct gui *ui = data;
1999 GSList *filenames, *fn_glist;
2000 GtkFileFilter *filter;
2002 int port, type, server_start;
2003 struct gui_entry *ge;
2007 * Creates new tab if current tab is the main window, or the
2008 * current tab already has a client.
2010 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2011 ge = get_ge_from_page(cur_page);
2013 ge = get_new_ge_with_tab("Untitled");
2015 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2017 dialog = gtk_file_chooser_dialog_new("Open File",
2018 GTK_WINDOW(ui->window),
2019 GTK_FILE_CHOOSER_ACTION_OPEN,
2020 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2021 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2023 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2025 filter = gtk_file_filter_new();
2026 gtk_file_filter_add_pattern(filter, "*.fio");
2027 gtk_file_filter_add_pattern(filter, "*.job");
2028 gtk_file_filter_add_pattern(filter, "*.ini");
2029 gtk_file_filter_add_mime_type(filter, "text/fio");
2030 gtk_file_filter_set_name(filter, "Fio job file");
2031 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2033 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2034 gtk_widget_destroy(dialog);
2038 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2040 gtk_widget_destroy(dialog);
2042 if (get_connection_details(&host, &port, &type, &server_start))
2045 filenames = fn_glist;
2046 while (filenames != NULL) {
2047 struct fio_client *client;
2049 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2050 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2053 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2057 error = g_error_new(g_quark_from_string("fio"), 1,
2058 "Failed to add client %s", host);
2059 report_error(error);
2060 g_error_free(error);
2062 gfio_client_added(ge, client);
2064 g_free(filenames->data);
2065 filenames = g_slist_next(filenames);
2070 gfio_start_server();
2072 g_slist_free(fn_glist);
2075 static void file_save(GtkWidget *w, gpointer data)
2077 struct gui *ui = data;
2080 dialog = gtk_file_chooser_dialog_new("Save File",
2081 GTK_WINDOW(ui->window),
2082 GTK_FILE_CHOOSER_ACTION_SAVE,
2083 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2084 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2087 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2088 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2090 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2093 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2094 // save_job_file(filename);
2097 gtk_widget_destroy(dialog);
2100 static void view_log_destroy(GtkWidget *w, gpointer data)
2102 struct gui *ui = (struct gui *) data;
2104 gtk_widget_ref(ui->log_tree);
2105 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2106 gtk_widget_destroy(w);
2107 ui->log_view = NULL;
2110 static void view_log(GtkWidget *w, gpointer data)
2112 GtkWidget *win, *scroll, *vbox, *box;
2113 struct gui *ui = (struct gui *) data;
2118 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2119 gtk_window_set_title(GTK_WINDOW(win), "Log");
2120 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2122 scroll = gtk_scrolled_window_new(NULL, NULL);
2124 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2126 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2128 box = gtk_hbox_new(TRUE, 0);
2129 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2130 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2131 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2133 vbox = gtk_vbox_new(TRUE, 5);
2134 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2136 gtk_container_add(GTK_CONTAINER(win), vbox);
2137 gtk_widget_show_all(win);
2140 static void edit_options(GtkWidget *w, gpointer data)
2144 static void __update_graph_limits(struct gfio_graphs *g)
2146 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2147 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2150 static void update_graph_limits(void)
2152 struct flist_head *entry;
2153 struct gui_entry *ge;
2155 __update_graph_limits(&main_ui.graphs);
2157 flist_for_each(entry, &main_ui.list) {
2158 ge = flist_entry(entry, struct gui_entry, list);
2159 __update_graph_limits(&ge->graphs);
2163 static void preferences(GtkWidget *w, gpointer data)
2165 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2166 GtkWidget *hbox, *spin, *entry, *spin_int;
2169 dialog = gtk_dialog_new_with_buttons("Preferences",
2170 GTK_WINDOW(main_ui.window),
2171 GTK_DIALOG_DESTROY_WITH_PARENT,
2172 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2173 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2176 frame = gtk_frame_new("Graphing");
2177 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2178 vbox = gtk_vbox_new(FALSE, 6);
2179 gtk_container_add(GTK_CONTAINER(frame), vbox);
2181 hbox = gtk_hbox_new(FALSE, 5);
2182 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2183 entry = gtk_label_new("Font face to use for graph labels");
2184 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2186 font = gtk_font_button_new();
2187 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2189 box = gtk_vbox_new(FALSE, 6);
2190 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2192 hbox = gtk_hbox_new(FALSE, 5);
2193 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2194 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2195 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2197 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2199 box = gtk_vbox_new(FALSE, 6);
2200 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2202 hbox = gtk_hbox_new(FALSE, 5);
2203 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2204 entry = gtk_label_new("Client ETA request interval (msec)");
2205 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2207 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2208 frame = gtk_frame_new("Debug logging");
2209 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2210 vbox = gtk_vbox_new(FALSE, 6);
2211 gtk_container_add(GTK_CONTAINER(frame), vbox);
2213 box = gtk_hbox_new(FALSE, 6);
2214 gtk_container_add(GTK_CONTAINER(vbox), box);
2216 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2218 for (i = 0; i < FD_DEBUG_MAX; i++) {
2220 box = gtk_hbox_new(FALSE, 6);
2221 gtk_container_add(GTK_CONTAINER(vbox), box);
2225 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2226 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2227 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2230 gtk_widget_show_all(dialog);
2232 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2233 gtk_widget_destroy(dialog);
2237 for (i = 0; i < FD_DEBUG_MAX; i++) {
2240 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2242 fio_debug |= (1UL << i);
2245 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2246 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2247 update_graph_limits();
2248 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2250 gtk_widget_destroy(dialog);
2253 static void about_dialog(GtkWidget *w, gpointer data)
2255 const char *authors[] = {
2256 "Jens Axboe <axboe@kernel.dk>",
2257 "Stephen Carmeron <stephenmcameron@gmail.com>",
2260 const char *license[] = {
2261 "Fio is free software; you can redistribute it and/or modify "
2262 "it under the terms of the GNU General Public License as published by "
2263 "the Free Software Foundation; either version 2 of the License, or "
2264 "(at your option) any later version.\n",
2265 "Fio is distributed in the hope that it will be useful, "
2266 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2267 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2268 "GNU General Public License for more details.\n",
2269 "You should have received a copy of the GNU General Public License "
2270 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2271 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2273 char *license_trans;
2275 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2276 license[2], "\n", NULL);
2278 gtk_show_about_dialog(NULL,
2279 "program-name", "gfio",
2280 "comments", "Gtk2 UI for fio",
2281 "license", license_trans,
2282 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2284 "version", fio_version_string,
2285 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2286 "logo-icon-name", "fio",
2288 "wrap-license", TRUE,
2291 g_free(license_trans);
2294 static GtkActionEntry menu_items[] = {
2295 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2296 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2297 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2298 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2299 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2300 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2301 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2302 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2303 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2304 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2305 { "EditOptions", NULL, "Edit Options", "<Control>E", NULL, G_CALLBACK(edit_options) },
2306 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2307 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2309 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2311 static const gchar *ui_string = " \
2313 <menubar name=\"MainMenu\"> \
2314 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2315 <menuitem name=\"New\" action=\"NewFile\" /> \
2316 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2317 <separator name=\"Separator1\"/> \
2318 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2319 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2320 <separator name=\"Separator2\"/> \
2321 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2322 <separator name=\"Separator3\"/> \
2323 <menuitem name=\"Quit\" action=\"Quit\" /> \
2325 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2326 <menuitem name=\"Edit Options\" action=\"EditOptions\" /> \
2328 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2329 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2331 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2332 <menuitem name=\"About\" action=\"About\" /> \
2338 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2341 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2344 action_group = gtk_action_group_new("Menu");
2345 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2347 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2348 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2350 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2351 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2354 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2355 GtkWidget *vbox, GtkUIManager *ui_manager)
2357 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2360 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2362 struct gui_entry *ge = (struct gui_entry *) data;
2365 index = gtk_combo_box_get_active(box);
2367 multitext_set_entry(&ge->eta.iotype, index);
2368 multitext_set_entry(&ge->eta.ioengine, index);
2369 multitext_set_entry(&ge->eta.iodepth, index);
2372 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2374 struct gui_entry *ge = (struct gui_entry *) data;
2376 multitext_free(&ge->eta.iotype);
2377 multitext_free(&ge->eta.ioengine);
2378 multitext_free(&ge->eta.iodepth);
2381 static GtkWidget *new_client_page(struct gui_entry *ge)
2383 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2386 main_vbox = gtk_vbox_new(FALSE, 3);
2388 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2389 ge->topvbox = gtk_vbox_new(FALSE, 3);
2390 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2391 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2393 probe = gtk_frame_new("Job");
2394 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2395 probe_frame = gtk_vbox_new(FALSE, 3);
2396 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2398 probe_box = gtk_hbox_new(FALSE, 3);
2399 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2400 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2401 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2402 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2403 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2405 probe_box = gtk_hbox_new(FALSE, 3);
2406 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2408 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2409 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2410 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2411 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2412 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2413 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2414 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2415 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2417 probe_box = gtk_hbox_new(FALSE, 3);
2418 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2419 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2420 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2421 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2422 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2425 * Only add this if we have a commit rate
2428 probe_box = gtk_hbox_new(FALSE, 3);
2429 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2431 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2432 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2434 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2435 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2439 * Set up a drawing area and IOPS and bandwidth graphs
2441 gdk_color_parse("white", &white);
2442 ge->graphs.drawing_area = gtk_drawing_area_new();
2443 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2444 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2445 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2446 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2447 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2448 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2449 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2450 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2451 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2452 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2453 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2454 ge->graphs.drawing_area);
2455 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2458 setup_graphs(&ge->graphs);
2461 * Set up alignments for widgets at the bottom of ui,
2462 * align bottom left, expand horizontally but not vertically
2464 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2465 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2466 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2467 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2470 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2473 * Set up thread status progress bar
2475 ge->thread_status_pb = gtk_progress_bar_new();
2476 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2477 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2478 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2484 static GtkWidget *new_main_page(struct gui *ui)
2486 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2489 main_vbox = gtk_vbox_new(FALSE, 3);
2492 * Set up alignments for widgets at the top of ui,
2493 * align top left, expand horizontally but not vertically
2495 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2496 ui->topvbox = gtk_vbox_new(FALSE, 0);
2497 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2498 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2500 probe = gtk_frame_new("Run statistics");
2501 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2502 probe_frame = gtk_vbox_new(FALSE, 3);
2503 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2505 probe_box = gtk_hbox_new(FALSE, 3);
2506 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2507 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2508 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2509 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2510 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2511 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2514 * Only add this if we have a commit rate
2517 probe_box = gtk_hbox_new(FALSE, 3);
2518 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2520 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2521 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2523 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2524 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2528 * Set up a drawing area and IOPS and bandwidth graphs
2530 gdk_color_parse("white", &white);
2531 ui->graphs.drawing_area = gtk_drawing_area_new();
2532 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2533 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2534 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2535 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2536 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2537 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2538 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2539 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2540 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2541 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2542 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2543 ui->graphs.drawing_area);
2544 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2547 setup_graphs(&ui->graphs);
2550 * Set up alignments for widgets at the bottom of ui,
2551 * align bottom left, expand horizontally but not vertically
2553 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2554 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2555 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2556 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2560 * Set up thread status progress bar
2562 ui->thread_status_pb = gtk_progress_bar_new();
2563 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2564 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2565 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2570 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2571 guint page, gpointer data)
2577 static void init_ui(int *argc, char **argv[], struct gui *ui)
2579 GtkSettings *settings;
2580 GtkUIManager *uimanager;
2581 GtkWidget *menu, *vbox;
2583 /* Magical g*thread incantation, you just need this thread stuff.
2584 * Without it, the update that happens in gfio_update_thread_status
2585 * doesn't really happen in a timely fashion, you need expose events
2587 if (!g_thread_supported())
2588 g_thread_init(NULL);
2591 gtk_init(argc, argv);
2592 settings = gtk_settings_get_default();
2593 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2596 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2597 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2598 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2600 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2601 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2603 ui->vbox = gtk_vbox_new(FALSE, 0);
2604 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2606 uimanager = gtk_ui_manager_new();
2607 menu = get_menubar_menu(ui->window, uimanager, ui);
2608 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2610 ui->notebook = gtk_notebook_new();
2611 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2612 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2613 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2614 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2616 vbox = new_main_page(ui);
2618 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2620 gfio_ui_setup_log(ui);
2622 gtk_widget_show_all(ui->window);
2625 int main(int argc, char *argv[], char *envp[])
2627 if (initialize_fio(envp))
2629 if (fio_init_options())
2632 memset(&main_ui, 0, sizeof(main_ui));
2633 INIT_FLIST_HEAD(&main_ui.list);
2635 init_ui(&argc, &argv, &main_ui);
2637 gdk_threads_enter();
2639 gdk_threads_leave();