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
107 GtkUIManager *uimanager;
113 GtkWidget *bottomalign;
114 GtkWidget *thread_status_pb;
115 GtkWidget *buttonbox;
116 GtkWidget *scrolled_window;
118 GtkWidget *error_info_bar;
119 GtkWidget *error_label;
120 GtkListStore *log_model;
123 struct gfio_graphs graphs;
124 struct probe_widget probe;
125 struct eta_widget eta;
131 struct flist_head list;
138 struct flist_head list;
144 GtkWidget *bottomalign;
145 GtkWidget *job_notebook;
146 GtkWidget *thread_status_pb;
147 GtkWidget *buttonbox;
148 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
149 GtkWidget *scrolled_window;
151 GtkWidget *error_info_bar;
152 GtkWidget *error_label;
153 GtkWidget *results_notebook;
154 GtkWidget *results_window;
155 GtkListStore *log_model;
158 struct gfio_graphs graphs;
159 struct probe_widget probe;
160 struct eta_widget eta;
161 GtkWidget *page_label;
165 struct gfio_client *client;
171 struct gui_entry *ge;
172 struct fio_client *client;
173 GtkWidget *results_widget;
174 GtkWidget *disk_util_frame;
175 GtkWidget *err_entry;
176 unsigned int job_added;
177 struct thread_options o;
180 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
181 static void gfio_update_thread_status_all(char *status_message, double perc);
182 void report_error(GError *error);
184 static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
186 switch (power_of_ten) {
187 case 9: graph_y_title(g, "Billions of IOs / sec");
189 case 6: graph_y_title(g, "Millions of IOs / sec");
191 case 3: graph_y_title(g, "Thousands of IOs / sec");
194 default: graph_y_title(g, "IOs / sec");
199 static struct graph *setup_iops_graph(void)
203 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
204 graph_title(g, "IOPS");
205 graph_x_title(g, "Time (secs)");
206 graph_y_title(g, "IOs / sec");
207 graph_add_label(g, "Read IOPS");
208 graph_add_label(g, "Write IOPS");
209 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
210 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
211 line_graph_set_data_count_limit(g, gfio_graph_limit);
212 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
213 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
217 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
219 switch (power_of_ten) {
220 case 9: graph_y_title(g, "Petabytes / sec");
222 case 6: graph_y_title(g, "Gigabytes / sec");
224 case 3: graph_y_title(g, "Megabytes / sec");
227 default: graph_y_title(g, "Kilobytes / sec");
232 static struct graph *setup_bandwidth_graph(void)
236 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
237 graph_title(g, "Bandwidth");
238 graph_x_title(g, "Time (secs)");
239 graph_y_title(g, "Kbytes / sec");
240 graph_add_label(g, "Read Bandwidth");
241 graph_add_label(g, "Write Bandwidth");
242 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
243 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
244 line_graph_set_data_count_limit(g, 100);
245 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
246 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
251 static void setup_graphs(struct gfio_graphs *g)
253 g->iops_graph = setup_iops_graph();
254 g->bandwidth_graph = setup_bandwidth_graph();
257 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
259 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
260 mt->text[mt->max_text] = strdup(text);
264 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
266 if (index >= mt->max_text)
268 if (!mt->text || !mt->text[index])
271 mt->cur_text = index;
272 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
275 static void multitext_update_entry(struct multitext_widget *mt,
276 unsigned int index, const char *text)
282 free(mt->text[index]);
284 mt->text[index] = strdup(text);
285 if (mt->cur_text == index)
286 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
289 static void multitext_free(struct multitext_widget *mt)
293 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
295 for (i = 0; i < mt->max_text; i++) {
305 static void clear_ge_ui_info(struct gui_entry *ge)
307 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
308 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
309 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
310 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
312 /* should we empty it... */
313 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
315 multitext_update_entry(&ge->eta.iotype, 0, "");
316 multitext_update_entry(&ge->eta.ioengine, 0, "");
317 multitext_update_entry(&ge->eta.iodepth, 0, "");
318 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
319 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
320 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
321 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
322 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
323 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
326 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
328 GtkWidget *entry, *frame;
330 frame = gtk_frame_new(label);
331 entry = gtk_combo_box_new_text();
332 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
333 gtk_container_add(GTK_CONTAINER(frame), entry);
338 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
340 GtkWidget *entry, *frame;
342 frame = gtk_frame_new(label);
343 entry = gtk_entry_new();
344 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
345 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
346 gtk_container_add(GTK_CONTAINER(frame), entry);
351 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
353 GtkWidget *label_widget;
356 frame = gtk_frame_new(label);
357 label_widget = gtk_label_new(NULL);
358 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
359 gtk_container_add(GTK_CONTAINER(frame), label_widget);
364 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
366 GtkWidget *button, *box;
368 box = gtk_hbox_new(FALSE, 3);
369 gtk_container_add(GTK_CONTAINER(hbox), box);
371 button = gtk_spin_button_new_with_range(min, max, 1.0);
372 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
374 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
375 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
380 static void gfio_set_connected(struct gui_entry *ge, int connected)
383 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
385 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
386 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
389 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
390 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
391 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
392 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
396 static void label_set_int_value(GtkWidget *entry, unsigned int val)
400 sprintf(tmp, "%u", val);
401 gtk_label_set_text(GTK_LABEL(entry), tmp);
404 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
408 sprintf(tmp, "%u", val);
409 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
412 static void show_info_dialog(struct gui *ui, const char *title,
415 GtkWidget *dialog, *content, *label;
417 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
418 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
419 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
421 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
422 label = gtk_label_new(message);
423 gtk_container_add(GTK_CONTAINER(content), label);
424 gtk_widget_show_all(dialog);
425 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
426 gtk_dialog_run(GTK_DIALOG(dialog));
427 gtk_widget_destroy(dialog);
431 #define ALIGN_RIGHT 2
435 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
437 GtkCellRenderer *renderer;
438 GtkTreeViewColumn *col;
439 double xalign = 0.0; /* left as default */
440 PangoAlignment align;
443 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
444 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
446 visible = !(flags & INVISIBLE);
448 renderer = gtk_cell_renderer_text_new();
449 col = gtk_tree_view_column_new();
451 gtk_tree_view_column_set_title(col, title);
452 if (!(flags & UNSORTABLE))
453 gtk_tree_view_column_set_sort_column_id(col, index);
454 gtk_tree_view_column_set_resizable(col, TRUE);
455 gtk_tree_view_column_pack_start(col, renderer, TRUE);
456 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
457 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
459 case PANGO_ALIGN_LEFT:
462 case PANGO_ALIGN_CENTER:
465 case PANGO_ALIGN_RIGHT:
469 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
470 gtk_tree_view_column_set_visible(col, visible);
471 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
475 static void gfio_ui_setup_log(struct gui *ui)
477 GtkTreeSelection *selection;
479 GtkWidget *tree_view;
481 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
483 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
484 gtk_widget_set_can_focus(tree_view, FALSE);
486 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
487 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
488 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
489 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
491 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
492 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
493 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
494 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
496 ui->log_model = model;
497 ui->log_tree = tree_view;
500 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
506 GType types[FIO_IO_U_LIST_MAX_LEN];
507 GtkWidget *tree_view;
508 GtkTreeSelection *selection;
513 for (i = 0; i < len; i++)
514 types[i] = G_TYPE_INT;
516 model = gtk_list_store_newv(len, types);
518 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
519 gtk_widget_set_can_focus(tree_view, FALSE);
521 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
522 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
524 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
525 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
527 for (i = 0; i < len; i++) {
530 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
531 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
534 gtk_list_store_append(model, &iter);
536 for (i = 0; i < len; i++) {
538 ovals[i] = (ovals[i] + 999) / 1000;
539 gtk_list_store_set(model, &iter, i, ovals[i], -1);
545 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
548 unsigned int *io_u_plat = ts->io_u_plat[ddir];
549 unsigned long nr = ts->clat_stat[ddir].samples;
550 fio_fp64_t *plist = ts->percentile_list;
551 unsigned int *ovals, len, minv, maxv, scale_down;
553 GtkWidget *tree_view, *frame, *hbox;
556 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
561 * We default to usecs, but if the value range is such that we
562 * should scale down to msecs, do that.
564 if (minv > 2000 && maxv > 99999) {
572 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
574 sprintf(tmp, "Completion percentiles (%s)", base);
575 frame = gtk_frame_new(tmp);
576 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
578 hbox = gtk_hbox_new(FALSE, 3);
579 gtk_container_add(GTK_CONTAINER(frame), hbox);
581 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
587 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
588 unsigned long max, double mean, double dev)
590 const char *base = "(usec)";
591 GtkWidget *hbox, *label, *frame;
595 if (!usec_to_msec(&min, &max, &mean, &dev))
598 minp = num2str(min, 6, 1, 0);
599 maxp = num2str(max, 6, 1, 0);
601 sprintf(tmp, "%s %s", name, base);
602 frame = gtk_frame_new(tmp);
603 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
605 hbox = gtk_hbox_new(FALSE, 3);
606 gtk_container_add(GTK_CONTAINER(frame), hbox);
608 label = new_info_label_in_frame(hbox, "Minimum");
609 gtk_label_set_text(GTK_LABEL(label), minp);
610 label = new_info_label_in_frame(hbox, "Maximum");
611 gtk_label_set_text(GTK_LABEL(label), maxp);
612 label = new_info_label_in_frame(hbox, "Average");
613 sprintf(tmp, "%5.02f", mean);
614 gtk_label_set_text(GTK_LABEL(label), tmp);
615 label = new_info_label_in_frame(hbox, "Standard deviation");
616 sprintf(tmp, "%5.02f", dev);
617 gtk_label_set_text(GTK_LABEL(label), tmp);
628 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
629 struct thread_stat *ts, int ddir)
631 const char *ddir_label[2] = { "Read", "Write" };
632 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
633 unsigned long min[3], max[3], runt;
634 unsigned long long bw, iops;
635 unsigned int flags = 0;
636 double mean[3], dev[3];
637 char *io_p, *bw_p, *iops_p;
640 if (!ts->runtime[ddir])
643 i2p = is_power_of_2(rs->kb_base);
644 runt = ts->runtime[ddir];
646 bw = (1000 * ts->io_bytes[ddir]) / runt;
647 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
648 bw_p = num2str(bw, 6, 1, i2p);
650 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
651 iops_p = num2str(iops, 6, 1, 0);
653 box = gtk_hbox_new(FALSE, 3);
654 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
656 frame = gtk_frame_new(ddir_label[ddir]);
657 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
659 main_vbox = gtk_vbox_new(FALSE, 3);
660 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
662 box = gtk_hbox_new(FALSE, 3);
663 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
665 label = new_info_label_in_frame(box, "IO");
666 gtk_label_set_text(GTK_LABEL(label), io_p);
667 label = new_info_label_in_frame(box, "Bandwidth");
668 gtk_label_set_text(GTK_LABEL(label), bw_p);
669 label = new_info_label_in_frame(box, "IOPS");
670 gtk_label_set_text(GTK_LABEL(label), iops_p);
671 label = new_info_label_in_frame(box, "Runtime (msec)");
672 label_set_int_value(label, ts->runtime[ddir]);
674 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
675 double p_of_agg = 100.0;
676 const char *bw_str = "KB";
680 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
681 if (p_of_agg > 100.0)
685 if (mean[0] > 999999.9) {
693 sprintf(tmp, "Bandwidth (%s)", bw_str);
694 frame = gtk_frame_new(tmp);
695 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
697 box = gtk_hbox_new(FALSE, 3);
698 gtk_container_add(GTK_CONTAINER(frame), box);
700 label = new_info_label_in_frame(box, "Minimum");
701 label_set_int_value(label, min[0]);
702 label = new_info_label_in_frame(box, "Maximum");
703 label_set_int_value(label, max[0]);
704 label = new_info_label_in_frame(box, "Percentage of jobs");
705 sprintf(tmp, "%3.2f%%", p_of_agg);
706 gtk_label_set_text(GTK_LABEL(label), tmp);
707 label = new_info_label_in_frame(box, "Average");
708 sprintf(tmp, "%5.02f", mean[0]);
709 gtk_label_set_text(GTK_LABEL(label), tmp);
710 label = new_info_label_in_frame(box, "Standard deviation");
711 sprintf(tmp, "%5.02f", dev[0]);
712 gtk_label_set_text(GTK_LABEL(label), tmp);
715 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
717 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
719 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
723 frame = gtk_frame_new("Latency");
724 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
726 vbox = gtk_vbox_new(FALSE, 3);
727 gtk_container_add(GTK_CONTAINER(frame), vbox);
729 if (flags & GFIO_SLAT)
730 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
731 if (flags & GFIO_CLAT)
732 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
733 if (flags & GFIO_LAT)
734 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
737 if (ts->clat_percentiles)
738 gfio_show_clat_percentiles(main_vbox, ts, ddir);
746 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
749 GtkWidget *tree_view;
750 GtkTreeSelection *selection;
757 * Check if all are empty, in which case don't bother
759 for (i = 0, skipped = 0; i < num; i++)
766 types = malloc(num * sizeof(GType));
768 for (i = 0; i < num; i++)
769 types[i] = G_TYPE_STRING;
771 model = gtk_list_store_newv(num, types);
775 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
776 gtk_widget_set_can_focus(tree_view, FALSE);
778 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
779 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
781 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
782 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
784 for (i = 0; i < num; i++)
785 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
787 gtk_list_store_append(model, &iter);
789 for (i = 0; i < num; i++) {
793 sprintf(fbuf, "0.00");
795 sprintf(fbuf, "%3.2f%%", lat[i]);
797 gtk_list_store_set(model, &iter, i, fbuf, -1);
803 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
805 GtkWidget *box, *frame, *tree_view;
806 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
807 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
808 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
809 "250", "500", "750", "1000", };
810 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
811 "250", "500", "750", "1000", "2000",
814 stat_calc_lat_u(ts, io_u_lat_u);
815 stat_calc_lat_m(ts, io_u_lat_m);
817 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
819 frame = gtk_frame_new("Latency buckets (usec)");
820 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
822 box = gtk_hbox_new(FALSE, 3);
823 gtk_container_add(GTK_CONTAINER(frame), box);
824 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
827 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
829 frame = gtk_frame_new("Latency buckets (msec)");
830 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
832 box = gtk_hbox_new(FALSE, 3);
833 gtk_container_add(GTK_CONTAINER(frame), box);
834 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
838 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
840 GtkWidget *box, *frame, *entry;
841 double usr_cpu, sys_cpu;
842 unsigned long runtime;
845 runtime = ts->total_run_time;
847 double runt = (double) runtime;
849 usr_cpu = (double) ts->usr_time * 100 / runt;
850 sys_cpu = (double) ts->sys_time * 100 / runt;
856 frame = gtk_frame_new("OS resources");
857 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
859 box = gtk_hbox_new(FALSE, 3);
860 gtk_container_add(GTK_CONTAINER(frame), box);
862 entry = new_info_entry_in_frame(box, "User CPU");
863 sprintf(tmp, "%3.2f%%", usr_cpu);
864 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
865 entry = new_info_entry_in_frame(box, "System CPU");
866 sprintf(tmp, "%3.2f%%", sys_cpu);
867 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
868 entry = new_info_entry_in_frame(box, "Context switches");
869 entry_set_int_value(entry, ts->ctx);
870 entry = new_info_entry_in_frame(box, "Major faults");
871 entry_set_int_value(entry, ts->majf);
872 entry = new_info_entry_in_frame(box, "Minor faults");
873 entry_set_int_value(entry, ts->minf);
875 static void gfio_add_sc_depths_tree(GtkListStore *model,
876 struct thread_stat *ts, unsigned int len,
879 double io_u_dist[FIO_IO_U_MAP_NR];
881 /* Bits 0, and 3-8 */
882 const int add_mask = 0x1f9;
886 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
888 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
890 gtk_list_store_append(model, &iter);
892 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
894 for (i = 1, j = 0; i < len; i++) {
897 if (!(add_mask & (1UL << (i - 1))))
898 sprintf(fbuf, "0.0%%");
900 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
904 gtk_list_store_set(model, &iter, i, fbuf, -1);
909 static void gfio_add_total_depths_tree(GtkListStore *model,
910 struct thread_stat *ts, unsigned int len)
912 double io_u_dist[FIO_IO_U_MAP_NR];
914 /* Bits 1-6, and 8 */
915 const int add_mask = 0x17e;
918 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
920 gtk_list_store_append(model, &iter);
922 gtk_list_store_set(model, &iter, 0, "Total", -1);
924 for (i = 1, j = 0; i < len; i++) {
927 if (!(add_mask & (1UL << (i - 1))))
928 sprintf(fbuf, "0.0%%");
930 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
934 gtk_list_store_set(model, &iter, i, fbuf, -1);
939 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
941 GtkWidget *frame, *box, *tree_view;
942 GtkTreeSelection *selection;
944 GType types[FIO_IO_U_MAP_NR + 1];
947 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
949 frame = gtk_frame_new("IO depths");
950 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
952 box = gtk_hbox_new(FALSE, 3);
953 gtk_container_add(GTK_CONTAINER(frame), box);
955 for (i = 0; i < NR_LABELS; i++)
956 types[i] = G_TYPE_STRING;
958 model = gtk_list_store_newv(NR_LABELS, types);
960 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
961 gtk_widget_set_can_focus(tree_view, FALSE);
963 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
964 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
966 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
967 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
969 for (i = 0; i < NR_LABELS; i++)
970 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
972 gfio_add_total_depths_tree(model, ts, NR_LABELS);
973 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
974 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
976 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
979 static gboolean results_window_delete(GtkWidget *w, gpointer data)
981 struct gui_entry *ge = (struct gui_entry *) data;
983 gtk_widget_destroy(w);
984 ge->results_window = NULL;
985 ge->results_notebook = NULL;
989 static GtkWidget *get_results_window(struct gui_entry *ge)
991 GtkWidget *win, *notebook;
993 if (ge->results_window)
994 return ge->results_notebook;
996 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
997 gtk_window_set_title(GTK_WINDOW(win), "Results");
998 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
999 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1000 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1002 notebook = gtk_notebook_new();
1003 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1004 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1005 gtk_container_add(GTK_CONTAINER(win), notebook);
1007 ge->results_window = win;
1008 ge->results_notebook = notebook;
1009 return ge->results_notebook;
1012 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1013 struct group_run_stats *rs)
1015 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
1016 struct gfio_client *gc = client->client_data;
1018 gdk_threads_enter();
1020 res_win = get_results_window(gc->ge);
1022 scroll = gtk_scrolled_window_new(NULL, NULL);
1023 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1024 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1026 vbox = gtk_vbox_new(FALSE, 3);
1028 box = gtk_hbox_new(FALSE, 0);
1029 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1031 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1033 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1035 gc->results_widget = vbox;
1037 entry = new_info_entry_in_frame(box, "Name");
1038 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1039 if (strlen(ts->description)) {
1040 entry = new_info_entry_in_frame(box, "Description");
1041 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1043 entry = new_info_entry_in_frame(box, "Group ID");
1044 entry_set_int_value(entry, ts->groupid);
1045 entry = new_info_entry_in_frame(box, "Jobs");
1046 entry_set_int_value(entry, ts->members);
1047 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1048 entry_set_int_value(entry, ts->error);
1049 entry = new_info_entry_in_frame(box, "PID");
1050 entry_set_int_value(entry, ts->pid);
1052 if (ts->io_bytes[DDIR_READ])
1053 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1054 if (ts->io_bytes[DDIR_WRITE])
1055 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1057 gfio_show_latency_buckets(vbox, ts);
1058 gfio_show_cpu_usage(vbox, ts);
1059 gfio_show_io_depths(vbox, ts);
1061 gtk_widget_show_all(gc->ge->results_window);
1062 gdk_threads_leave();
1065 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1067 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1068 struct gui *ui = &main_ui;
1072 char tmp[64], timebuf[80];
1075 tm = localtime(&sec);
1076 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1077 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1079 gdk_threads_enter();
1081 gtk_list_store_append(ui->log_model, &iter);
1082 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1083 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1084 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1085 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1087 if (p->level == FIO_LOG_ERR)
1088 view_log(NULL, (gpointer) ui);
1090 gdk_threads_leave();
1093 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1095 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1096 struct gfio_client *gc = client->client_data;
1097 GtkWidget *box, *frame, *entry, *vbox;
1101 gdk_threads_enter();
1103 if (!gc->results_widget)
1106 if (!gc->disk_util_frame) {
1107 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1108 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1111 vbox = gtk_vbox_new(FALSE, 3);
1112 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1114 frame = gtk_frame_new((char *) p->dus.name);
1115 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1117 box = gtk_vbox_new(FALSE, 3);
1118 gtk_container_add(GTK_CONTAINER(frame), box);
1120 frame = gtk_frame_new("Read");
1121 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1122 vbox = gtk_hbox_new(TRUE, 3);
1123 gtk_container_add(GTK_CONTAINER(frame), vbox);
1124 entry = new_info_entry_in_frame(vbox, "IOs");
1125 entry_set_int_value(entry, p->dus.ios[0]);
1126 entry = new_info_entry_in_frame(vbox, "Merges");
1127 entry_set_int_value(entry, p->dus.merges[0]);
1128 entry = new_info_entry_in_frame(vbox, "Sectors");
1129 entry_set_int_value(entry, p->dus.sectors[0]);
1130 entry = new_info_entry_in_frame(vbox, "Ticks");
1131 entry_set_int_value(entry, p->dus.ticks[0]);
1133 frame = gtk_frame_new("Write");
1134 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1135 vbox = gtk_hbox_new(TRUE, 3);
1136 gtk_container_add(GTK_CONTAINER(frame), vbox);
1137 entry = new_info_entry_in_frame(vbox, "IOs");
1138 entry_set_int_value(entry, p->dus.ios[1]);
1139 entry = new_info_entry_in_frame(vbox, "Merges");
1140 entry_set_int_value(entry, p->dus.merges[1]);
1141 entry = new_info_entry_in_frame(vbox, "Sectors");
1142 entry_set_int_value(entry, p->dus.sectors[1]);
1143 entry = new_info_entry_in_frame(vbox, "Ticks");
1144 entry_set_int_value(entry, p->dus.ticks[1]);
1146 frame = gtk_frame_new("Shared");
1147 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1148 vbox = gtk_hbox_new(TRUE, 3);
1149 gtk_container_add(GTK_CONTAINER(frame), vbox);
1150 entry = new_info_entry_in_frame(vbox, "IO ticks");
1151 entry_set_int_value(entry, p->dus.io_ticks);
1152 entry = new_info_entry_in_frame(vbox, "Time in queue");
1153 entry_set_int_value(entry, p->dus.time_in_queue);
1157 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1161 sprintf(tmp, "%3.2f%%", util);
1162 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1163 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1165 gtk_widget_show_all(gc->results_widget);
1167 gdk_threads_leave();
1170 extern int sum_stat_clients;
1171 extern struct thread_stat client_ts;
1172 extern struct group_run_stats client_gs;
1174 static int sum_stat_nr;
1176 static void gfio_thread_status_op(struct fio_client *client,
1177 struct fio_net_cmd *cmd)
1179 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1181 gfio_display_ts(client, &p->ts, &p->rs);
1183 if (sum_stat_clients == 1)
1186 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1187 sum_group_stats(&client_gs, &p->rs);
1189 client_ts.members++;
1190 client_ts.groupid = p->ts.groupid;
1192 if (++sum_stat_nr == sum_stat_clients) {
1193 strcpy(client_ts.name, "All clients");
1194 gfio_display_ts(client, &client_ts, &client_gs);
1198 static void gfio_group_stats_op(struct fio_client *client,
1199 struct fio_net_cmd *cmd)
1201 /* We're ignoring group stats for now */
1204 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1207 struct gfio_graphs *g = data;
1209 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1210 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1211 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1212 graph_set_position(g->bandwidth_graph, 0, 0);
1216 static void draw_graph(struct graph *g, cairo_t *cr)
1218 line_graph_draw(g, cr);
1222 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1224 struct gfio_graphs *g = p;
1227 cr = gdk_cairo_create(w->window);
1228 cairo_set_source_rgb(cr, 0, 0, 0);
1229 draw_graph(g->iops_graph, cr);
1230 draw_graph(g->bandwidth_graph, cr);
1237 * Client specific ETA
1239 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1241 struct gfio_client *gc = client->client_data;
1242 struct gui_entry *ge = gc->ge;
1243 static int eta_good;
1250 gdk_threads_enter();
1255 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1256 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1257 eta_to_str(eta_str, je->eta_sec);
1260 sprintf(tmp, "%u", je->nr_running);
1261 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1262 sprintf(tmp, "%u", je->files_open);
1263 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1266 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1267 if (je->m_rate || je->t_rate) {
1270 mr = num2str(je->m_rate, 4, 0, i2p);
1271 tr = num2str(je->t_rate, 4, 0, i2p);
1272 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1273 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1276 } else if (je->m_iops || je->t_iops)
1277 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1279 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1280 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1281 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1282 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1285 if (je->eta_sec != INT_MAX && je->nr_running) {
1289 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1290 strcpy(output, "-.-% done");
1294 sprintf(output, "%3.1f%% done", perc);
1297 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1298 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1300 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1301 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1303 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1304 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1305 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1306 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1308 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1309 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1310 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1311 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1320 char *dst = output + strlen(output);
1322 sprintf(dst, " - %s", eta_str);
1325 gfio_update_thread_status(ge, output, perc);
1326 gdk_threads_leave();
1330 * Update ETA in main window for all clients
1332 static void gfio_update_all_eta(struct jobs_eta *je)
1334 struct gui *ui = &main_ui;
1335 static int eta_good;
1341 gdk_threads_enter();
1346 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1347 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1348 eta_to_str(eta_str, je->eta_sec);
1352 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1353 if (je->m_rate || je->t_rate) {
1356 mr = num2str(je->m_rate, 4, 0, i2p);
1357 tr = num2str(je->t_rate, 4, 0, i2p);
1358 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1359 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1362 } else if (je->m_iops || je->t_iops)
1363 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1365 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1366 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1367 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1368 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1371 entry_set_int_value(ui->eta.jobs, je->nr_running);
1373 if (je->eta_sec != INT_MAX && je->nr_running) {
1377 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1378 strcpy(output, "-.-% done");
1382 sprintf(output, "%3.1f%% done", perc);
1385 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1386 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1388 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1389 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1391 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1392 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1393 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1394 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1396 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1397 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1398 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1399 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1408 char *dst = output + strlen(output);
1410 sprintf(dst, " - %s", eta_str);
1413 gfio_update_thread_status_all(output, perc);
1414 gdk_threads_leave();
1417 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1419 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1420 struct gfio_client *gc = client->client_data;
1421 struct gui_entry *ge = gc->ge;
1422 const char *os, *arch;
1425 os = fio_get_os_string(probe->os);
1429 arch = fio_get_arch_string(probe->arch);
1434 client->name = strdup((char *) probe->hostname);
1436 gdk_threads_enter();
1438 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1439 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1440 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1441 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1442 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1444 gfio_set_connected(ge, 1);
1446 gdk_threads_leave();
1449 static void gfio_update_thread_status(struct gui_entry *ge,
1450 char *status_message, double perc)
1452 static char message[100];
1453 const char *m = message;
1455 strncpy(message, status_message, sizeof(message) - 1);
1456 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1457 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1458 gtk_widget_queue_draw(main_ui.window);
1461 static void gfio_update_thread_status_all(char *status_message, double perc)
1463 struct gui *ui = &main_ui;
1464 static char message[100];
1465 const char *m = message;
1467 strncpy(message, status_message, sizeof(message) - 1);
1468 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1469 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1470 gtk_widget_queue_draw(ui->window);
1473 static void gfio_quit_op(struct fio_client *client)
1475 struct gfio_client *gc = client->client_data;
1477 gdk_threads_enter();
1478 gfio_set_connected(gc->ge, 0);
1479 gdk_threads_leave();
1482 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1484 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1485 struct gfio_client *gc = client->client_data;
1486 struct thread_options *o = &gc->o;
1487 struct gui_entry *ge = gc->ge;
1490 convert_thread_options_to_cpu(o, &p->top);
1492 gdk_threads_enter();
1494 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1496 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1497 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1499 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1500 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1502 sprintf(tmp, "%u", o->iodepth);
1503 multitext_add_entry(&ge->eta.iodepth, tmp);
1505 multitext_set_entry(&ge->eta.iotype, 0);
1506 multitext_set_entry(&ge->eta.ioengine, 0);
1507 multitext_set_entry(&ge->eta.iodepth, 0);
1511 gdk_threads_leave();
1514 static void gfio_client_timed_out(struct fio_client *client)
1516 struct gfio_client *gc = client->client_data;
1519 gdk_threads_enter();
1521 gfio_set_connected(gc->ge, 0);
1522 clear_ge_ui_info(gc->ge);
1524 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1525 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1527 gdk_threads_leave();
1530 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1532 struct gfio_client *gc = client->client_data;
1534 gdk_threads_enter();
1536 gfio_set_connected(gc->ge, 0);
1539 entry_set_int_value(gc->err_entry, client->error);
1541 gdk_threads_leave();
1544 struct client_ops gfio_client_ops = {
1545 .text_op = gfio_text_op,
1546 .disk_util = gfio_disk_util_op,
1547 .thread_status = gfio_thread_status_op,
1548 .group_stats = gfio_group_stats_op,
1549 .jobs_eta = gfio_update_client_eta,
1550 .eta = gfio_update_all_eta,
1551 .probe = gfio_probe_op,
1552 .quit = gfio_quit_op,
1553 .add_job = gfio_add_job_op,
1554 .timed_out = gfio_client_timed_out,
1555 .stop = gfio_client_stop,
1556 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1557 .stay_connected = 1,
1560 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1561 __attribute__((unused)) gpointer data)
1566 static void *job_thread(void *arg)
1568 struct gui *ui = arg;
1570 ui->handler_running = 1;
1571 fio_handle_clients(&gfio_client_ops);
1572 ui->handler_running = 0;
1576 static int send_job_files(struct gui_entry *ge)
1578 struct gfio_client *gc = ge->client;
1581 for (i = 0; i < ge->nr_job_files; i++) {
1582 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1586 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1587 report_error(error);
1588 g_error_free(error);
1593 free(ge->job_files[i]);
1594 ge->job_files[i] = NULL;
1596 while (i < ge->nr_job_files) {
1597 free(ge->job_files[i]);
1598 ge->job_files[i] = NULL;
1605 static void *server_thread(void *arg)
1608 gfio_server_running = 1;
1609 fio_start_server(NULL);
1610 gfio_server_running = 0;
1614 static void gfio_start_server(void)
1616 struct gui *ui = &main_ui;
1618 if (!gfio_server_running) {
1619 gfio_server_running = 1;
1620 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1621 pthread_detach(ui->server_t);
1625 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1628 struct gui_entry *ge = data;
1629 struct gfio_client *gc = ge->client;
1631 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1632 fio_start_client(gc->client);
1635 static void file_open(GtkWidget *w, gpointer data);
1637 static void connect_clicked(GtkWidget *widget, gpointer data)
1639 struct gui_entry *ge = data;
1640 struct gfio_client *gc = ge->client;
1642 if (!ge->connected) {
1645 if (!ge->nr_job_files)
1646 file_open(widget, data);
1647 if (!ge->nr_job_files)
1650 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1651 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1652 ret = fio_client_connect(gc->client);
1654 if (!ge->ui->handler_running)
1655 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1656 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1657 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1661 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1662 report_error(error);
1663 g_error_free(error);
1666 fio_client_terminate(gc->client);
1667 gfio_set_connected(ge, 0);
1668 clear_ge_ui_info(ge);
1672 static void send_clicked(GtkWidget *widget, gpointer data)
1674 struct gui_entry *ge = data;
1676 if (send_job_files(ge)) {
1679 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);
1680 report_error(error);
1681 g_error_free(error);
1683 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1686 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1687 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1690 static GtkWidget *add_button(GtkWidget *buttonbox,
1691 struct button_spec *buttonspec, gpointer data)
1693 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1695 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1696 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1697 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1698 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1703 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1708 for (i = 0; i < nbuttons; i++)
1709 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1712 static void on_info_bar_response(GtkWidget *widget, gint response,
1715 struct gui *ui = &main_ui;
1717 if (response == GTK_RESPONSE_OK) {
1718 gtk_widget_destroy(widget);
1719 ui->error_info_bar = NULL;
1723 void report_error(GError *error)
1725 struct gui *ui = &main_ui;
1727 if (ui->error_info_bar == NULL) {
1728 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1731 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1732 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1735 ui->error_label = gtk_label_new(error->message);
1736 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1737 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1739 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1740 gtk_widget_show_all(ui->vbox);
1743 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1744 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1748 struct connection_widgets
1755 static void hostname_cb(GtkEntry *entry, gpointer data)
1757 struct connection_widgets *cw = data;
1758 int uses_net = 0, is_localhost = 0;
1763 * Check whether to display the 'auto start backend' box
1764 * or not. Show it if we are a localhost and using network,
1765 * or using a socket.
1767 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1768 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1773 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1774 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1775 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1776 !strcmp(text, "ip6-loopback"))
1780 if (!uses_net || is_localhost) {
1781 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1782 gtk_widget_set_sensitive(cw->button, 1);
1784 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1785 gtk_widget_set_sensitive(cw->button, 0);
1789 static int get_connection_details(char **host, int *port, int *type,
1792 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1793 struct connection_widgets cw;
1796 dialog = gtk_dialog_new_with_buttons("Connection details",
1797 GTK_WINDOW(main_ui.window),
1798 GTK_DIALOG_DESTROY_WITH_PARENT,
1799 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1800 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1802 frame = gtk_frame_new("Hostname / socket name");
1803 /* gtk_dialog_get_content_area() is 2.14 and newer */
1804 vbox = GTK_DIALOG(dialog)->vbox;
1805 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1807 box = gtk_vbox_new(FALSE, 6);
1808 gtk_container_add(GTK_CONTAINER(frame), box);
1810 hbox = gtk_hbox_new(TRUE, 10);
1811 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1812 cw.hentry = gtk_entry_new();
1813 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1814 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1816 frame = gtk_frame_new("Port");
1817 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1818 box = gtk_vbox_new(FALSE, 10);
1819 gtk_container_add(GTK_CONTAINER(frame), box);
1821 hbox = gtk_hbox_new(TRUE, 4);
1822 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1823 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1825 frame = gtk_frame_new("Type");
1826 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1827 box = gtk_vbox_new(FALSE, 10);
1828 gtk_container_add(GTK_CONTAINER(frame), box);
1830 hbox = gtk_hbox_new(TRUE, 4);
1831 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1833 cw.combo = gtk_combo_box_new_text();
1834 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1835 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1836 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1837 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1839 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1841 frame = gtk_frame_new("Options");
1842 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1843 box = gtk_vbox_new(FALSE, 10);
1844 gtk_container_add(GTK_CONTAINER(frame), box);
1846 hbox = gtk_hbox_new(TRUE, 4);
1847 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1849 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1850 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1851 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.");
1852 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1855 * Connect edit signal, so we can show/not-show the auto start button
1857 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1858 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1860 gtk_widget_show_all(dialog);
1862 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1863 gtk_widget_destroy(dialog);
1867 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1868 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1870 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1871 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1872 *type = Fio_client_ipv4;
1873 else if (!strncmp(typeentry, "IPv6", 4))
1874 *type = Fio_client_ipv6;
1876 *type = Fio_client_socket;
1879 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1881 gtk_widget_destroy(dialog);
1885 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1887 struct gfio_client *gc;
1889 gc = malloc(sizeof(*gc));
1890 memset(gc, 0, sizeof(*gc));
1892 gc->client = fio_get_client(client);
1896 client->client_data = gc;
1899 static GtkWidget *new_client_page(struct gui_entry *ge);
1901 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1903 struct gui_entry *ge;
1905 ge = malloc(sizeof(*ge));
1906 memset(ge, 0, sizeof(*ge));
1907 INIT_FLIST_HEAD(&ge->list);
1908 flist_add_tail(&ge->list, &ui->list);
1914 * FIXME: need more handling here
1916 static void ge_destroy(GtkWidget *w, gpointer data)
1918 struct gui_entry *ge = data;
1919 struct gfio_client *gc = ge->client;
1921 if (gc && gc->client) {
1923 fio_client_terminate(gc->client);
1925 fio_put_client(gc->client);
1928 flist_del(&ge->list);
1932 static struct gui_entry *get_new_ge_with_tab(const char *name)
1934 struct gui_entry *ge;
1936 ge = alloc_new_gui_entry(&main_ui);
1938 ge->vbox = new_client_page(ge);
1939 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1941 ge->page_label = gtk_label_new(name);
1942 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1944 gtk_widget_show_all(main_ui.window);
1948 static void file_new(GtkWidget *w, gpointer data)
1950 struct gui *ui = (struct gui *) data;
1951 struct gui_entry *ge;
1953 ge = get_new_ge_with_tab("Untitled");
1954 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1958 * Return the 'ge' corresponding to the tab. If the active tab is the
1959 * main tab, open a new tab.
1961 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1963 struct flist_head *entry;
1964 struct gui_entry *ge;
1967 return get_new_ge_with_tab("Untitled");
1969 flist_for_each(entry, &main_ui.list) {
1970 ge = flist_entry(entry, struct gui_entry, list);
1971 if (ge->page_num == cur_page)
1978 static void file_close(GtkWidget *w, gpointer data)
1980 struct gui *ui = (struct gui *) data;
1984 * Can't close the main tab
1986 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1988 struct gui_entry *ge = get_ge_from_page(cur_page);
1990 gtk_widget_destroy(ge->vbox);
1994 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
1997 static void file_open(GtkWidget *w, gpointer data)
1999 struct gui *ui = data;
2001 GSList *filenames, *fn_glist;
2002 GtkFileFilter *filter;
2004 int port, type, server_start;
2005 struct gui_entry *ge;
2009 * Creates new tab if current tab is the main window, or the
2010 * current tab already has a client.
2012 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2013 ge = get_ge_from_page(cur_page);
2015 ge = get_new_ge_with_tab("Untitled");
2017 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2019 dialog = gtk_file_chooser_dialog_new("Open File",
2020 GTK_WINDOW(ui->window),
2021 GTK_FILE_CHOOSER_ACTION_OPEN,
2022 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2023 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2025 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2027 filter = gtk_file_filter_new();
2028 gtk_file_filter_add_pattern(filter, "*.fio");
2029 gtk_file_filter_add_pattern(filter, "*.job");
2030 gtk_file_filter_add_pattern(filter, "*.ini");
2031 gtk_file_filter_add_mime_type(filter, "text/fio");
2032 gtk_file_filter_set_name(filter, "Fio job file");
2033 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2035 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2036 gtk_widget_destroy(dialog);
2040 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2042 gtk_widget_destroy(dialog);
2044 if (get_connection_details(&host, &port, &type, &server_start))
2047 filenames = fn_glist;
2048 while (filenames != NULL) {
2049 struct fio_client *client;
2051 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2052 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2055 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2059 error = g_error_new(g_quark_from_string("fio"), 1,
2060 "Failed to add client %s", host);
2061 report_error(error);
2062 g_error_free(error);
2064 gfio_client_added(ge, client);
2066 g_free(filenames->data);
2067 filenames = g_slist_next(filenames);
2072 gfio_start_server();
2074 g_slist_free(fn_glist);
2077 static void file_save(GtkWidget *w, gpointer data)
2079 struct gui *ui = data;
2082 dialog = gtk_file_chooser_dialog_new("Save File",
2083 GTK_WINDOW(ui->window),
2084 GTK_FILE_CHOOSER_ACTION_SAVE,
2085 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2086 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2089 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2090 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2092 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2095 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2096 // save_job_file(filename);
2099 gtk_widget_destroy(dialog);
2102 static void view_log_destroy(GtkWidget *w, gpointer data)
2104 struct gui *ui = (struct gui *) data;
2106 gtk_widget_ref(ui->log_tree);
2107 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2108 gtk_widget_destroy(w);
2109 ui->log_view = NULL;
2112 static void view_log(GtkWidget *w, gpointer data)
2114 GtkWidget *win, *scroll, *vbox, *box;
2115 struct gui *ui = (struct gui *) data;
2120 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2121 gtk_window_set_title(GTK_WINDOW(win), "Log");
2122 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2124 scroll = gtk_scrolled_window_new(NULL, NULL);
2126 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2128 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2130 box = gtk_hbox_new(TRUE, 0);
2131 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2132 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2133 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2135 vbox = gtk_vbox_new(TRUE, 5);
2136 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2138 gtk_container_add(GTK_CONTAINER(win), vbox);
2139 gtk_widget_show_all(win);
2142 static void edit_options(GtkWidget *w, gpointer data)
2146 static void __update_graph_limits(struct gfio_graphs *g)
2148 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2149 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2152 static void update_graph_limits(void)
2154 struct flist_head *entry;
2155 struct gui_entry *ge;
2157 __update_graph_limits(&main_ui.graphs);
2159 flist_for_each(entry, &main_ui.list) {
2160 ge = flist_entry(entry, struct gui_entry, list);
2161 __update_graph_limits(&ge->graphs);
2165 static void preferences(GtkWidget *w, gpointer data)
2167 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2168 GtkWidget *hbox, *spin, *entry, *spin_int;
2171 dialog = gtk_dialog_new_with_buttons("Preferences",
2172 GTK_WINDOW(main_ui.window),
2173 GTK_DIALOG_DESTROY_WITH_PARENT,
2174 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2175 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2178 frame = gtk_frame_new("Graphing");
2179 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2180 vbox = gtk_vbox_new(FALSE, 6);
2181 gtk_container_add(GTK_CONTAINER(frame), vbox);
2183 hbox = gtk_hbox_new(FALSE, 5);
2184 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2185 entry = gtk_label_new("Font face to use for graph labels");
2186 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2188 font = gtk_font_button_new();
2189 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2191 box = gtk_vbox_new(FALSE, 6);
2192 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2194 hbox = gtk_hbox_new(FALSE, 5);
2195 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2196 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2197 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2199 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2201 box = gtk_vbox_new(FALSE, 6);
2202 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2204 hbox = gtk_hbox_new(FALSE, 5);
2205 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2206 entry = gtk_label_new("Client ETA request interval (msec)");
2207 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2209 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2210 frame = gtk_frame_new("Debug logging");
2211 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2212 vbox = gtk_vbox_new(FALSE, 6);
2213 gtk_container_add(GTK_CONTAINER(frame), vbox);
2215 box = gtk_hbox_new(FALSE, 6);
2216 gtk_container_add(GTK_CONTAINER(vbox), box);
2218 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2220 for (i = 0; i < FD_DEBUG_MAX; i++) {
2222 box = gtk_hbox_new(FALSE, 6);
2223 gtk_container_add(GTK_CONTAINER(vbox), box);
2227 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2228 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2229 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2232 gtk_widget_show_all(dialog);
2234 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2235 gtk_widget_destroy(dialog);
2239 for (i = 0; i < FD_DEBUG_MAX; i++) {
2242 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2244 fio_debug |= (1UL << i);
2247 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2248 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2249 update_graph_limits();
2250 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2252 gtk_widget_destroy(dialog);
2255 static void about_dialog(GtkWidget *w, gpointer data)
2257 const char *authors[] = {
2258 "Jens Axboe <axboe@kernel.dk>",
2259 "Stephen Carmeron <stephenmcameron@gmail.com>",
2262 const char *license[] = {
2263 "Fio is free software; you can redistribute it and/or modify "
2264 "it under the terms of the GNU General Public License as published by "
2265 "the Free Software Foundation; either version 2 of the License, or "
2266 "(at your option) any later version.\n",
2267 "Fio is distributed in the hope that it will be useful, "
2268 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2269 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2270 "GNU General Public License for more details.\n",
2271 "You should have received a copy of the GNU General Public License "
2272 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2273 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2275 char *license_trans;
2277 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2278 license[2], "\n", NULL);
2280 gtk_show_about_dialog(NULL,
2281 "program-name", "gfio",
2282 "comments", "Gtk2 UI for fio",
2283 "license", license_trans,
2284 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2286 "version", fio_version_string,
2287 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2288 "logo-icon-name", "fio",
2290 "wrap-license", TRUE,
2293 g_free(license_trans);
2296 static GtkActionEntry menu_items[] = {
2297 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2298 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2299 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2300 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2301 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2302 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2303 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2304 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2305 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2306 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2307 { "EditOptions", NULL, "Edit Options", "<Control>E", NULL, G_CALLBACK(edit_options) },
2308 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2309 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2311 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2313 static const gchar *ui_string = " \
2315 <menubar name=\"MainMenu\"> \
2316 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2317 <menuitem name=\"New\" action=\"NewFile\" /> \
2318 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2319 <separator name=\"Separator1\"/> \
2320 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2321 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2322 <separator name=\"Separator2\"/> \
2323 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2324 <separator name=\"Separator3\"/> \
2325 <menuitem name=\"Quit\" action=\"Quit\" /> \
2327 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2328 <menuitem name=\"Edit Options\" action=\"EditOptions\" /> \
2330 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2331 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2333 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2334 <menuitem name=\"About\" action=\"About\" /> \
2340 static void set_job_menu_visible(struct gui *ui, int visible)
2344 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2345 gtk_widget_set_sensitive(job, visible);
2348 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2351 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2354 action_group = gtk_action_group_new("Menu");
2355 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2357 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2358 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2360 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2362 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2365 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2366 GtkWidget *vbox, GtkUIManager *ui_manager)
2368 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2371 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2373 struct gui_entry *ge = (struct gui_entry *) data;
2376 index = gtk_combo_box_get_active(box);
2378 multitext_set_entry(&ge->eta.iotype, index);
2379 multitext_set_entry(&ge->eta.ioengine, index);
2380 multitext_set_entry(&ge->eta.iodepth, index);
2383 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2385 struct gui_entry *ge = (struct gui_entry *) data;
2387 multitext_free(&ge->eta.iotype);
2388 multitext_free(&ge->eta.ioengine);
2389 multitext_free(&ge->eta.iodepth);
2392 static GtkWidget *new_client_page(struct gui_entry *ge)
2394 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2397 main_vbox = gtk_vbox_new(FALSE, 3);
2399 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2400 ge->topvbox = gtk_vbox_new(FALSE, 3);
2401 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2402 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2404 probe = gtk_frame_new("Job");
2405 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2406 probe_frame = gtk_vbox_new(FALSE, 3);
2407 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2409 probe_box = gtk_hbox_new(FALSE, 3);
2410 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2411 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2412 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2413 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2414 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2416 probe_box = gtk_hbox_new(FALSE, 3);
2417 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2419 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2420 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2421 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2422 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2423 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2424 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2425 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2426 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2428 probe_box = gtk_hbox_new(FALSE, 3);
2429 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2430 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2431 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2432 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2433 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2436 * Only add this if we have a commit rate
2439 probe_box = gtk_hbox_new(FALSE, 3);
2440 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2442 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2443 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2445 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2446 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2450 * Set up a drawing area and IOPS and bandwidth graphs
2452 gdk_color_parse("white", &white);
2453 ge->graphs.drawing_area = gtk_drawing_area_new();
2454 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2455 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2456 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2457 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2458 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2459 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2460 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2461 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2462 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2463 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2464 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2465 ge->graphs.drawing_area);
2466 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2469 setup_graphs(&ge->graphs);
2472 * Set up alignments for widgets at the bottom of ui,
2473 * align bottom left, expand horizontally but not vertically
2475 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2476 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2477 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2478 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2481 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2484 * Set up thread status progress bar
2486 ge->thread_status_pb = gtk_progress_bar_new();
2487 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2488 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2489 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2495 static GtkWidget *new_main_page(struct gui *ui)
2497 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2500 main_vbox = gtk_vbox_new(FALSE, 3);
2503 * Set up alignments for widgets at the top of ui,
2504 * align top left, expand horizontally but not vertically
2506 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2507 ui->topvbox = gtk_vbox_new(FALSE, 0);
2508 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2509 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2511 probe = gtk_frame_new("Run statistics");
2512 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2513 probe_frame = gtk_vbox_new(FALSE, 3);
2514 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2516 probe_box = gtk_hbox_new(FALSE, 3);
2517 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2518 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2519 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2520 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2521 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2522 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2525 * Only add this if we have a commit rate
2528 probe_box = gtk_hbox_new(FALSE, 3);
2529 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2531 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2532 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2534 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2535 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2539 * Set up a drawing area and IOPS and bandwidth graphs
2541 gdk_color_parse("white", &white);
2542 ui->graphs.drawing_area = gtk_drawing_area_new();
2543 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2544 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2545 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2546 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2547 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2548 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2549 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2550 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2551 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2552 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2553 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2554 ui->graphs.drawing_area);
2555 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2558 setup_graphs(&ui->graphs);
2561 * Set up alignments for widgets at the bottom of ui,
2562 * align bottom left, expand horizontally but not vertically
2564 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2565 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2566 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2567 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2571 * Set up thread status progress bar
2573 ui->thread_status_pb = gtk_progress_bar_new();
2574 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2575 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2576 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2581 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2582 guint page, gpointer data)
2585 struct gui *ui = (struct gui *) data;
2587 set_job_menu_visible(ui, page);
2591 static void init_ui(int *argc, char **argv[], struct gui *ui)
2593 GtkSettings *settings;
2596 /* Magical g*thread incantation, you just need this thread stuff.
2597 * Without it, the update that happens in gfio_update_thread_status
2598 * doesn't really happen in a timely fashion, you need expose events
2600 if (!g_thread_supported())
2601 g_thread_init(NULL);
2604 gtk_init(argc, argv);
2605 settings = gtk_settings_get_default();
2606 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2609 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2610 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2611 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2613 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2614 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2616 ui->vbox = gtk_vbox_new(FALSE, 0);
2617 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2619 ui->uimanager = gtk_ui_manager_new();
2620 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
2621 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2623 ui->notebook = gtk_notebook_new();
2624 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2625 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2626 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2627 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2629 vbox = new_main_page(ui);
2631 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2633 gfio_ui_setup_log(ui);
2635 gtk_widget_show_all(ui->window);
2638 int main(int argc, char *argv[], char *envp[])
2640 if (initialize_fio(envp))
2642 if (fio_init_options())
2645 memset(&main_ui, 0, sizeof(main_ui));
2646 INIT_FLIST_HEAD(&main_ui.list);
2648 init_ui(&argc, &argv, &main_ui);
2650 gdk_threads_enter();
2652 gdk_threads_leave();