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[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)
277 free(mt->text[index]);
279 mt->text[index] = strdup(text);
280 if (mt->cur_text == index)
281 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
284 static void multitext_free(struct multitext_widget *mt)
288 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
290 for (i = 0; i < mt->max_text; i++) {
300 static void clear_ge_ui_info(struct gui_entry *ge)
302 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
303 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
304 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
305 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
307 /* should we empty it... */
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
310 multitext_update_entry(&ge->eta.iotype, 0, "");
311 multitext_update_entry(&ge->eta.ioengine, 0, "");
312 multitext_update_entry(&ge->eta.iodepth, 0, "");
313 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
314 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
315 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
316 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
317 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
318 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
321 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
323 GtkWidget *entry, *frame;
325 frame = gtk_frame_new(label);
326 entry = gtk_combo_box_new_text();
327 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
328 gtk_container_add(GTK_CONTAINER(frame), entry);
333 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
335 GtkWidget *entry, *frame;
337 frame = gtk_frame_new(label);
338 entry = gtk_entry_new();
339 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
340 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
341 gtk_container_add(GTK_CONTAINER(frame), entry);
346 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
348 GtkWidget *label_widget;
351 frame = gtk_frame_new(label);
352 label_widget = gtk_label_new(NULL);
353 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
354 gtk_container_add(GTK_CONTAINER(frame), label_widget);
359 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
361 GtkWidget *button, *box;
363 box = gtk_hbox_new(FALSE, 3);
364 gtk_container_add(GTK_CONTAINER(hbox), box);
366 button = gtk_spin_button_new_with_range(min, max, 1.0);
367 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
369 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
370 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
375 static void gfio_set_connected(struct gui_entry *ge, int connected)
378 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
380 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
381 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
384 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
385 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
386 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
387 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
391 static void label_set_int_value(GtkWidget *entry, unsigned int val)
395 sprintf(tmp, "%u", val);
396 gtk_label_set_text(GTK_LABEL(entry), tmp);
399 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
403 sprintf(tmp, "%u", val);
404 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
408 #define ALIGN_RIGHT 2
412 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
414 GtkCellRenderer *renderer;
415 GtkTreeViewColumn *col;
416 double xalign = 0.0; /* left as default */
417 PangoAlignment align;
420 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
421 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
423 visible = !(flags & INVISIBLE);
425 renderer = gtk_cell_renderer_text_new();
426 col = gtk_tree_view_column_new();
428 gtk_tree_view_column_set_title(col, title);
429 if (!(flags & UNSORTABLE))
430 gtk_tree_view_column_set_sort_column_id(col, index);
431 gtk_tree_view_column_set_resizable(col, TRUE);
432 gtk_tree_view_column_pack_start(col, renderer, TRUE);
433 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
434 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
436 case PANGO_ALIGN_LEFT:
439 case PANGO_ALIGN_CENTER:
442 case PANGO_ALIGN_RIGHT:
446 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
447 gtk_tree_view_column_set_visible(col, visible);
448 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
452 static void gfio_ui_setup_log(struct gui *ui)
454 GtkTreeSelection *selection;
456 GtkWidget *tree_view;
458 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
460 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
461 gtk_widget_set_can_focus(tree_view, FALSE);
463 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
464 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
465 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
466 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
468 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
469 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
470 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
471 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
473 ui->log_model = model;
474 ui->log_tree = tree_view;
477 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
483 GType types[FIO_IO_U_LIST_MAX_LEN];
484 GtkWidget *tree_view;
485 GtkTreeSelection *selection;
490 for (i = 0; i < len; i++)
491 types[i] = G_TYPE_INT;
493 model = gtk_list_store_newv(len, types);
495 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
496 gtk_widget_set_can_focus(tree_view, FALSE);
498 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
499 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
501 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
502 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
504 for (i = 0; i < len; i++) {
507 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
508 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
511 gtk_list_store_append(model, &iter);
513 for (i = 0; i < len; i++) {
515 ovals[i] = (ovals[i] + 999) / 1000;
516 gtk_list_store_set(model, &iter, i, ovals[i], -1);
522 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
525 unsigned int *io_u_plat = ts->io_u_plat[ddir];
526 unsigned long nr = ts->clat_stat[ddir].samples;
527 fio_fp64_t *plist = ts->percentile_list;
528 unsigned int *ovals, len, minv, maxv, scale_down;
530 GtkWidget *tree_view, *frame, *hbox;
533 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
538 * We default to usecs, but if the value range is such that we
539 * should scale down to msecs, do that.
541 if (minv > 2000 && maxv > 99999) {
549 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
551 sprintf(tmp, "Completion percentiles (%s)", base);
552 frame = gtk_frame_new(tmp);
553 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
555 hbox = gtk_hbox_new(FALSE, 3);
556 gtk_container_add(GTK_CONTAINER(frame), hbox);
558 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
564 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
565 unsigned long max, double mean, double dev)
567 const char *base = "(usec)";
568 GtkWidget *hbox, *label, *frame;
572 if (!usec_to_msec(&min, &max, &mean, &dev))
575 minp = num2str(min, 6, 1, 0);
576 maxp = num2str(max, 6, 1, 0);
578 sprintf(tmp, "%s %s", name, base);
579 frame = gtk_frame_new(tmp);
580 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
582 hbox = gtk_hbox_new(FALSE, 3);
583 gtk_container_add(GTK_CONTAINER(frame), hbox);
585 label = new_info_label_in_frame(hbox, "Minimum");
586 gtk_label_set_text(GTK_LABEL(label), minp);
587 label = new_info_label_in_frame(hbox, "Maximum");
588 gtk_label_set_text(GTK_LABEL(label), maxp);
589 label = new_info_label_in_frame(hbox, "Average");
590 sprintf(tmp, "%5.02f", mean);
591 gtk_label_set_text(GTK_LABEL(label), tmp);
592 label = new_info_label_in_frame(hbox, "Standard deviation");
593 sprintf(tmp, "%5.02f", dev);
594 gtk_label_set_text(GTK_LABEL(label), tmp);
605 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
606 struct thread_stat *ts, int ddir)
608 const char *ddir_label[2] = { "Read", "Write" };
609 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
610 unsigned long min[3], max[3], runt;
611 unsigned long long bw, iops;
612 unsigned int flags = 0;
613 double mean[3], dev[3];
614 char *io_p, *bw_p, *iops_p;
617 if (!ts->runtime[ddir])
620 i2p = is_power_of_2(rs->kb_base);
621 runt = ts->runtime[ddir];
623 bw = (1000 * ts->io_bytes[ddir]) / runt;
624 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
625 bw_p = num2str(bw, 6, 1, i2p);
627 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
628 iops_p = num2str(iops, 6, 1, 0);
630 box = gtk_hbox_new(FALSE, 3);
631 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
633 frame = gtk_frame_new(ddir_label[ddir]);
634 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
636 main_vbox = gtk_vbox_new(FALSE, 3);
637 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
639 box = gtk_hbox_new(FALSE, 3);
640 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
642 label = new_info_label_in_frame(box, "IO");
643 gtk_label_set_text(GTK_LABEL(label), io_p);
644 label = new_info_label_in_frame(box, "Bandwidth");
645 gtk_label_set_text(GTK_LABEL(label), bw_p);
646 label = new_info_label_in_frame(box, "IOPS");
647 gtk_label_set_text(GTK_LABEL(label), iops_p);
648 label = new_info_label_in_frame(box, "Runtime (msec)");
649 label_set_int_value(label, ts->runtime[ddir]);
651 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
652 double p_of_agg = 100.0;
653 const char *bw_str = "KB";
657 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
658 if (p_of_agg > 100.0)
662 if (mean[0] > 999999.9) {
670 sprintf(tmp, "Bandwidth (%s)", bw_str);
671 frame = gtk_frame_new(tmp);
672 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
674 box = gtk_hbox_new(FALSE, 3);
675 gtk_container_add(GTK_CONTAINER(frame), box);
677 label = new_info_label_in_frame(box, "Minimum");
678 label_set_int_value(label, min[0]);
679 label = new_info_label_in_frame(box, "Maximum");
680 label_set_int_value(label, max[0]);
681 label = new_info_label_in_frame(box, "Percentage of jobs");
682 sprintf(tmp, "%3.2f%%", p_of_agg);
683 gtk_label_set_text(GTK_LABEL(label), tmp);
684 label = new_info_label_in_frame(box, "Average");
685 sprintf(tmp, "%5.02f", mean[0]);
686 gtk_label_set_text(GTK_LABEL(label), tmp);
687 label = new_info_label_in_frame(box, "Standard deviation");
688 sprintf(tmp, "%5.02f", dev[0]);
689 gtk_label_set_text(GTK_LABEL(label), tmp);
692 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
694 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
696 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
700 frame = gtk_frame_new("Latency");
701 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
703 vbox = gtk_vbox_new(FALSE, 3);
704 gtk_container_add(GTK_CONTAINER(frame), vbox);
706 if (flags & GFIO_SLAT)
707 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
708 if (flags & GFIO_CLAT)
709 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
710 if (flags & GFIO_LAT)
711 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
714 if (ts->clat_percentiles)
715 gfio_show_clat_percentiles(main_vbox, ts, ddir);
723 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
726 GtkWidget *tree_view;
727 GtkTreeSelection *selection;
734 * Check if all are empty, in which case don't bother
736 for (i = 0, skipped = 0; i < num; i++)
743 types = malloc(num * sizeof(GType));
745 for (i = 0; i < num; i++)
746 types[i] = G_TYPE_STRING;
748 model = gtk_list_store_newv(num, types);
752 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
753 gtk_widget_set_can_focus(tree_view, FALSE);
755 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
756 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
758 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
759 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
761 for (i = 0; i < num; i++)
762 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
764 gtk_list_store_append(model, &iter);
766 for (i = 0; i < num; i++) {
770 sprintf(fbuf, "0.00");
772 sprintf(fbuf, "%3.2f%%", lat[i]);
774 gtk_list_store_set(model, &iter, i, fbuf, -1);
780 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
782 GtkWidget *box, *frame, *tree_view;
783 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
784 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
785 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
786 "250", "500", "750", "1000", };
787 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
788 "250", "500", "750", "1000", "2000",
791 stat_calc_lat_u(ts, io_u_lat_u);
792 stat_calc_lat_m(ts, io_u_lat_m);
794 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
796 frame = gtk_frame_new("Latency buckets (usec)");
797 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
799 box = gtk_hbox_new(FALSE, 3);
800 gtk_container_add(GTK_CONTAINER(frame), box);
801 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
804 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
806 frame = gtk_frame_new("Latency buckets (msec)");
807 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
809 box = gtk_hbox_new(FALSE, 3);
810 gtk_container_add(GTK_CONTAINER(frame), box);
811 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
815 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
817 GtkWidget *box, *frame, *entry;
818 double usr_cpu, sys_cpu;
819 unsigned long runtime;
822 runtime = ts->total_run_time;
824 double runt = (double) runtime;
826 usr_cpu = (double) ts->usr_time * 100 / runt;
827 sys_cpu = (double) ts->sys_time * 100 / runt;
833 frame = gtk_frame_new("OS resources");
834 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
836 box = gtk_hbox_new(FALSE, 3);
837 gtk_container_add(GTK_CONTAINER(frame), box);
839 entry = new_info_entry_in_frame(box, "User CPU");
840 sprintf(tmp, "%3.2f%%", usr_cpu);
841 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
842 entry = new_info_entry_in_frame(box, "System CPU");
843 sprintf(tmp, "%3.2f%%", sys_cpu);
844 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
845 entry = new_info_entry_in_frame(box, "Context switches");
846 entry_set_int_value(entry, ts->ctx);
847 entry = new_info_entry_in_frame(box, "Major faults");
848 entry_set_int_value(entry, ts->majf);
849 entry = new_info_entry_in_frame(box, "Minor faults");
850 entry_set_int_value(entry, ts->minf);
852 static void gfio_add_sc_depths_tree(GtkListStore *model,
853 struct thread_stat *ts, unsigned int len,
856 double io_u_dist[FIO_IO_U_MAP_NR];
858 /* Bits 0, and 3-8 */
859 const int add_mask = 0x1f9;
863 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
865 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
867 gtk_list_store_append(model, &iter);
869 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
871 for (i = 1, j = 0; i < len; i++) {
874 if (!(add_mask & (1UL << (i - 1))))
875 sprintf(fbuf, "0.0%%");
877 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
881 gtk_list_store_set(model, &iter, i, fbuf, -1);
886 static void gfio_add_total_depths_tree(GtkListStore *model,
887 struct thread_stat *ts, unsigned int len)
889 double io_u_dist[FIO_IO_U_MAP_NR];
891 /* Bits 1-6, and 8 */
892 const int add_mask = 0x17e;
895 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
897 gtk_list_store_append(model, &iter);
899 gtk_list_store_set(model, &iter, 0, "Total", -1);
901 for (i = 1, j = 0; i < len; i++) {
904 if (!(add_mask & (1UL << (i - 1))))
905 sprintf(fbuf, "0.0%%");
907 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
911 gtk_list_store_set(model, &iter, i, fbuf, -1);
916 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
918 GtkWidget *frame, *box, *tree_view;
919 GtkTreeSelection *selection;
921 GType types[FIO_IO_U_MAP_NR + 1];
924 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
926 frame = gtk_frame_new("IO depths");
927 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
929 box = gtk_hbox_new(FALSE, 3);
930 gtk_container_add(GTK_CONTAINER(frame), box);
932 for (i = 0; i < NR_LABELS; i++)
933 types[i] = G_TYPE_STRING;
935 model = gtk_list_store_newv(NR_LABELS, types);
937 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
938 gtk_widget_set_can_focus(tree_view, FALSE);
940 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
941 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
943 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
944 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
946 for (i = 0; i < NR_LABELS; i++)
947 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
949 gfio_add_total_depths_tree(model, ts, NR_LABELS);
950 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
951 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
953 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
956 static gboolean results_window_delete(GtkWidget *w, gpointer data)
958 struct gui_entry *ge = (struct gui_entry *) data;
960 gtk_widget_destroy(w);
961 ge->results_window = NULL;
962 ge->results_notebook = NULL;
966 static GtkWidget *get_results_window(struct gui_entry *ge)
968 GtkWidget *win, *notebook;
970 if (ge->results_window)
971 return ge->results_notebook;
973 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
974 gtk_window_set_title(GTK_WINDOW(win), "Results");
975 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
976 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
977 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
979 notebook = gtk_notebook_new();
980 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
981 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
982 gtk_container_add(GTK_CONTAINER(win), notebook);
984 ge->results_window = win;
985 ge->results_notebook = notebook;
986 return ge->results_notebook;
989 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
990 struct group_run_stats *rs)
992 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
993 struct gfio_client *gc = client->client_data;
997 res_win = get_results_window(gc->ge);
999 scroll = gtk_scrolled_window_new(NULL, NULL);
1000 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1001 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1003 vbox = gtk_vbox_new(FALSE, 3);
1005 box = gtk_hbox_new(FALSE, 0);
1006 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1008 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1010 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1012 gc->results_widget = vbox;
1014 entry = new_info_entry_in_frame(box, "Name");
1015 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1016 if (strlen(ts->description)) {
1017 entry = new_info_entry_in_frame(box, "Description");
1018 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1020 entry = new_info_entry_in_frame(box, "Group ID");
1021 entry_set_int_value(entry, ts->groupid);
1022 entry = new_info_entry_in_frame(box, "Jobs");
1023 entry_set_int_value(entry, ts->members);
1024 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1025 entry_set_int_value(entry, ts->error);
1026 entry = new_info_entry_in_frame(box, "PID");
1027 entry_set_int_value(entry, ts->pid);
1029 if (ts->io_bytes[DDIR_READ])
1030 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1031 if (ts->io_bytes[DDIR_WRITE])
1032 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1034 gfio_show_latency_buckets(vbox, ts);
1035 gfio_show_cpu_usage(vbox, ts);
1036 gfio_show_io_depths(vbox, ts);
1038 gtk_widget_show_all(gc->ge->results_window);
1039 gdk_threads_leave();
1042 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1044 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1045 struct gui *ui = &main_ui;
1049 char tmp[64], timebuf[80];
1052 tm = localtime(&sec);
1053 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1054 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1056 gdk_threads_enter();
1058 gtk_list_store_append(ui->log_model, &iter);
1059 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1060 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1061 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1062 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1064 if (p->level == FIO_LOG_ERR)
1065 view_log(NULL, (gpointer) ui);
1067 gdk_threads_leave();
1070 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1072 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1073 struct gfio_client *gc = client->client_data;
1074 GtkWidget *box, *frame, *entry, *vbox;
1078 gdk_threads_enter();
1080 if (!gc->results_widget)
1083 if (!gc->disk_util_frame) {
1084 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1085 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1088 vbox = gtk_vbox_new(FALSE, 3);
1089 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1091 frame = gtk_frame_new((char *) p->dus.name);
1092 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1094 box = gtk_vbox_new(FALSE, 3);
1095 gtk_container_add(GTK_CONTAINER(frame), box);
1097 frame = gtk_frame_new("Read");
1098 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1099 vbox = gtk_hbox_new(TRUE, 3);
1100 gtk_container_add(GTK_CONTAINER(frame), vbox);
1101 entry = new_info_entry_in_frame(vbox, "IOs");
1102 entry_set_int_value(entry, p->dus.ios[0]);
1103 entry = new_info_entry_in_frame(vbox, "Merges");
1104 entry_set_int_value(entry, p->dus.merges[0]);
1105 entry = new_info_entry_in_frame(vbox, "Sectors");
1106 entry_set_int_value(entry, p->dus.sectors[0]);
1107 entry = new_info_entry_in_frame(vbox, "Ticks");
1108 entry_set_int_value(entry, p->dus.ticks[0]);
1110 frame = gtk_frame_new("Write");
1111 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1112 vbox = gtk_hbox_new(TRUE, 3);
1113 gtk_container_add(GTK_CONTAINER(frame), vbox);
1114 entry = new_info_entry_in_frame(vbox, "IOs");
1115 entry_set_int_value(entry, p->dus.ios[1]);
1116 entry = new_info_entry_in_frame(vbox, "Merges");
1117 entry_set_int_value(entry, p->dus.merges[1]);
1118 entry = new_info_entry_in_frame(vbox, "Sectors");
1119 entry_set_int_value(entry, p->dus.sectors[1]);
1120 entry = new_info_entry_in_frame(vbox, "Ticks");
1121 entry_set_int_value(entry, p->dus.ticks[1]);
1123 frame = gtk_frame_new("Shared");
1124 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1125 vbox = gtk_hbox_new(TRUE, 3);
1126 gtk_container_add(GTK_CONTAINER(frame), vbox);
1127 entry = new_info_entry_in_frame(vbox, "IO ticks");
1128 entry_set_int_value(entry, p->dus.io_ticks);
1129 entry = new_info_entry_in_frame(vbox, "Time in queue");
1130 entry_set_int_value(entry, p->dus.time_in_queue);
1134 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1138 sprintf(tmp, "%3.2f%%", util);
1139 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1140 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1142 gtk_widget_show_all(gc->results_widget);
1144 gdk_threads_leave();
1147 extern int sum_stat_clients;
1148 extern struct thread_stat client_ts;
1149 extern struct group_run_stats client_gs;
1151 static int sum_stat_nr;
1153 static void gfio_thread_status_op(struct fio_client *client,
1154 struct fio_net_cmd *cmd)
1156 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1158 gfio_display_ts(client, &p->ts, &p->rs);
1160 if (sum_stat_clients == 1)
1163 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1164 sum_group_stats(&client_gs, &p->rs);
1166 client_ts.members++;
1167 client_ts.groupid = p->ts.groupid;
1169 if (++sum_stat_nr == sum_stat_clients) {
1170 strcpy(client_ts.name, "All clients");
1171 gfio_display_ts(client, &client_ts, &client_gs);
1175 static void gfio_group_stats_op(struct fio_client *client,
1176 struct fio_net_cmd *cmd)
1178 /* We're ignoring group stats for now */
1181 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1184 struct gfio_graphs *g = data;
1186 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1187 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1188 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1189 graph_set_position(g->bandwidth_graph, 0, 0);
1193 static void draw_graph(struct graph *g, cairo_t *cr)
1195 line_graph_draw(g, cr);
1199 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1201 struct gfio_graphs *g = p;
1204 cr = gdk_cairo_create(w->window);
1205 cairo_set_source_rgb(cr, 0, 0, 0);
1206 draw_graph(g->iops_graph, cr);
1207 draw_graph(g->bandwidth_graph, cr);
1214 * Client specific ETA
1216 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1218 struct gfio_client *gc = client->client_data;
1219 struct gui_entry *ge = gc->ge;
1220 static int eta_good;
1227 gdk_threads_enter();
1232 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1233 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1234 eta_to_str(eta_str, je->eta_sec);
1237 sprintf(tmp, "%u", je->nr_running);
1238 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1239 sprintf(tmp, "%u", je->files_open);
1240 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1243 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1244 if (je->m_rate || je->t_rate) {
1247 mr = num2str(je->m_rate, 4, 0, i2p);
1248 tr = num2str(je->t_rate, 4, 0, i2p);
1249 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1250 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1253 } else if (je->m_iops || je->t_iops)
1254 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1256 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1257 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1258 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1259 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1262 if (je->eta_sec != INT_MAX && je->nr_running) {
1266 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1267 strcpy(output, "-.-% done");
1271 sprintf(output, "%3.1f%% done", perc);
1274 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1275 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1277 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1278 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1280 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1281 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1282 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1283 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1285 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1286 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1287 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1288 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1297 char *dst = output + strlen(output);
1299 sprintf(dst, " - %s", eta_str);
1302 gfio_update_thread_status(ge, output, perc);
1303 gdk_threads_leave();
1307 * Update ETA in main window for all clients
1309 static void gfio_update_all_eta(struct jobs_eta *je)
1311 struct gui *ui = &main_ui;
1312 static int eta_good;
1318 gdk_threads_enter();
1323 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1324 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1325 eta_to_str(eta_str, je->eta_sec);
1329 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1330 if (je->m_rate || je->t_rate) {
1333 mr = num2str(je->m_rate, 4, 0, i2p);
1334 tr = num2str(je->t_rate, 4, 0, i2p);
1335 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1336 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1339 } else if (je->m_iops || je->t_iops)
1340 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1342 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1343 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1344 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1345 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1348 entry_set_int_value(ui->eta.jobs, je->nr_running);
1350 if (je->eta_sec != INT_MAX && je->nr_running) {
1354 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1355 strcpy(output, "-.-% done");
1359 sprintf(output, "%3.1f%% done", perc);
1362 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1363 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1365 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1366 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1368 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1369 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1370 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1371 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1373 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1374 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1375 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1376 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1385 char *dst = output + strlen(output);
1387 sprintf(dst, " - %s", eta_str);
1390 gfio_update_thread_status_all(output, perc);
1391 gdk_threads_leave();
1394 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1396 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1397 struct gfio_client *gc = client->client_data;
1398 struct gui_entry *ge = gc->ge;
1399 const char *os, *arch;
1402 os = fio_get_os_string(probe->os);
1406 arch = fio_get_arch_string(probe->arch);
1411 client->name = strdup((char *) probe->hostname);
1413 gdk_threads_enter();
1415 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1416 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1417 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1418 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1419 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1421 gfio_set_connected(ge, 1);
1423 gdk_threads_leave();
1426 static void gfio_update_thread_status(struct gui_entry *ge,
1427 char *status_message, double perc)
1429 static char message[100];
1430 const char *m = message;
1432 strncpy(message, status_message, sizeof(message) - 1);
1433 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1434 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1435 gtk_widget_queue_draw(main_ui.window);
1438 static void gfio_update_thread_status_all(char *status_message, double perc)
1440 struct gui *ui = &main_ui;
1441 static char message[100];
1442 const char *m = message;
1444 strncpy(message, status_message, sizeof(message) - 1);
1445 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1446 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1447 gtk_widget_queue_draw(ui->window);
1450 static void gfio_quit_op(struct fio_client *client)
1452 struct gfio_client *gc = client->client_data;
1454 gdk_threads_enter();
1455 gfio_set_connected(gc->ge, 0);
1456 gdk_threads_leave();
1459 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1461 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1462 struct gfio_client *gc = client->client_data;
1463 struct thread_options *o = &gc->o;
1464 struct gui_entry *ge = gc->ge;
1467 convert_thread_options_to_cpu(o, &p->top);
1469 gdk_threads_enter();
1471 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1473 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1474 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1476 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1477 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1479 sprintf(tmp, "%u", o->iodepth);
1480 multitext_add_entry(&ge->eta.iodepth, tmp);
1482 multitext_set_entry(&ge->eta.iotype, 0);
1483 multitext_set_entry(&ge->eta.ioengine, 0);
1484 multitext_set_entry(&ge->eta.iodepth, 0);
1488 gdk_threads_leave();
1491 static void gfio_client_timed_out(struct fio_client *client)
1493 struct gfio_client *gc = client->client_data;
1494 GtkWidget *dialog, *label, *content;
1497 gdk_threads_enter();
1499 gfio_set_connected(gc->ge, 0);
1500 clear_ge_ui_info(gc->ge);
1502 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1504 dialog = gtk_dialog_new_with_buttons("Timed out!",
1505 GTK_WINDOW(main_ui.window),
1506 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1507 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1509 /* gtk_dialog_get_content_area() is 2.14 and newer */
1510 content = GTK_DIALOG(dialog)->vbox;
1512 label = gtk_label_new((const gchar *) buf);
1513 gtk_container_add(GTK_CONTAINER(content), label);
1514 gtk_widget_show_all(dialog);
1515 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1517 gtk_dialog_run(GTK_DIALOG(dialog));
1518 gtk_widget_destroy(dialog);
1520 gdk_threads_leave();
1523 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1525 struct gfio_client *gc = client->client_data;
1527 gdk_threads_enter();
1529 gfio_set_connected(gc->ge, 0);
1532 entry_set_int_value(gc->err_entry, client->error);
1534 gdk_threads_leave();
1537 struct client_ops gfio_client_ops = {
1538 .text_op = gfio_text_op,
1539 .disk_util = gfio_disk_util_op,
1540 .thread_status = gfio_thread_status_op,
1541 .group_stats = gfio_group_stats_op,
1542 .jobs_eta = gfio_update_client_eta,
1543 .eta = gfio_update_all_eta,
1544 .probe = gfio_probe_op,
1545 .quit = gfio_quit_op,
1546 .add_job = gfio_add_job_op,
1547 .timed_out = gfio_client_timed_out,
1548 .stop = gfio_client_stop,
1549 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1550 .stay_connected = 1,
1553 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1554 __attribute__((unused)) gpointer data)
1559 static void *job_thread(void *arg)
1561 struct gui *ui = arg;
1563 ui->handler_running = 1;
1564 fio_handle_clients(&gfio_client_ops);
1565 ui->handler_running = 0;
1569 static int send_job_files(struct gui_entry *ge)
1571 struct gfio_client *gc = ge->client;
1574 for (i = 0; i < ge->nr_job_files; i++) {
1575 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1579 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1580 report_error(error);
1581 g_error_free(error);
1586 free(ge->job_files[i]);
1587 ge->job_files[i] = NULL;
1589 while (i < ge->nr_job_files) {
1590 free(ge->job_files[i]);
1591 ge->job_files[i] = NULL;
1598 static void *server_thread(void *arg)
1601 gfio_server_running = 1;
1602 fio_start_server(NULL);
1603 gfio_server_running = 0;
1607 static void gfio_start_server(void)
1609 struct gui *ui = &main_ui;
1611 if (!gfio_server_running) {
1612 gfio_server_running = 1;
1613 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1614 pthread_detach(ui->server_t);
1618 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1621 struct gui_entry *ge = data;
1622 struct gfio_client *gc = ge->client;
1624 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1625 fio_start_client(gc->client);
1628 static void file_open(GtkWidget *w, gpointer data);
1630 static void connect_clicked(GtkWidget *widget, gpointer data)
1632 struct gui_entry *ge = data;
1633 struct gfio_client *gc = ge->client;
1635 if (!ge->connected) {
1638 if (!ge->nr_job_files)
1639 file_open(widget, data);
1640 if (!ge->nr_job_files)
1643 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1644 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1645 ret = fio_client_connect(gc->client);
1647 if (!ge->ui->handler_running)
1648 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1649 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1650 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1654 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1655 report_error(error);
1656 g_error_free(error);
1659 fio_client_terminate(gc->client);
1660 gfio_set_connected(ge, 0);
1661 clear_ge_ui_info(ge);
1665 static void send_clicked(GtkWidget *widget, gpointer data)
1667 struct gui_entry *ge = data;
1669 if (send_job_files(ge)) {
1672 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);
1673 report_error(error);
1674 g_error_free(error);
1676 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1679 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1680 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1683 static GtkWidget *add_button(GtkWidget *buttonbox,
1684 struct button_spec *buttonspec, gpointer data)
1686 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1688 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1689 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1690 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1691 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1696 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1701 for (i = 0; i < nbuttons; i++)
1702 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1705 static void on_info_bar_response(GtkWidget *widget, gint response,
1708 struct gui *ui = &main_ui;
1710 if (response == GTK_RESPONSE_OK) {
1711 gtk_widget_destroy(widget);
1712 ui->error_info_bar = NULL;
1716 void report_error(GError *error)
1718 struct gui *ui = &main_ui;
1720 if (ui->error_info_bar == NULL) {
1721 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1724 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1725 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1728 ui->error_label = gtk_label_new(error->message);
1729 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1730 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1732 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1733 gtk_widget_show_all(ui->vbox);
1736 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1737 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1741 struct connection_widgets
1748 static void hostname_cb(GtkEntry *entry, gpointer data)
1750 struct connection_widgets *cw = data;
1751 int uses_net = 0, is_localhost = 0;
1756 * Check whether to display the 'auto start backend' box
1757 * or not. Show it if we are a localhost and using network,
1758 * or using a socket.
1760 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1761 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1766 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1767 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1768 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1769 !strcmp(text, "ip6-loopback"))
1773 if (!uses_net || is_localhost) {
1774 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1775 gtk_widget_set_sensitive(cw->button, 1);
1777 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1778 gtk_widget_set_sensitive(cw->button, 0);
1782 static int get_connection_details(char **host, int *port, int *type,
1785 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1786 struct connection_widgets cw;
1789 dialog = gtk_dialog_new_with_buttons("Connection details",
1790 GTK_WINDOW(main_ui.window),
1791 GTK_DIALOG_DESTROY_WITH_PARENT,
1792 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1793 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1795 frame = gtk_frame_new("Hostname / socket name");
1796 /* gtk_dialog_get_content_area() is 2.14 and newer */
1797 vbox = GTK_DIALOG(dialog)->vbox;
1798 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1800 box = gtk_vbox_new(FALSE, 6);
1801 gtk_container_add(GTK_CONTAINER(frame), box);
1803 hbox = gtk_hbox_new(TRUE, 10);
1804 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1805 cw.hentry = gtk_entry_new();
1806 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1807 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1809 frame = gtk_frame_new("Port");
1810 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1811 box = gtk_vbox_new(FALSE, 10);
1812 gtk_container_add(GTK_CONTAINER(frame), box);
1814 hbox = gtk_hbox_new(TRUE, 4);
1815 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1816 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1818 frame = gtk_frame_new("Type");
1819 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1820 box = gtk_vbox_new(FALSE, 10);
1821 gtk_container_add(GTK_CONTAINER(frame), box);
1823 hbox = gtk_hbox_new(TRUE, 4);
1824 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1826 cw.combo = gtk_combo_box_new_text();
1827 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1828 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1829 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1830 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1832 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1834 frame = gtk_frame_new("Options");
1835 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1836 box = gtk_vbox_new(FALSE, 10);
1837 gtk_container_add(GTK_CONTAINER(frame), box);
1839 hbox = gtk_hbox_new(TRUE, 4);
1840 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1842 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1843 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1844 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.");
1845 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1848 * Connect edit signal, so we can show/not-show the auto start button
1850 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1851 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1853 gtk_widget_show_all(dialog);
1855 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1856 gtk_widget_destroy(dialog);
1860 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1861 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1863 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1864 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1865 *type = Fio_client_ipv4;
1866 else if (!strncmp(typeentry, "IPv6", 4))
1867 *type = Fio_client_ipv6;
1869 *type = Fio_client_socket;
1872 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1874 gtk_widget_destroy(dialog);
1878 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1880 struct gfio_client *gc;
1882 gc = malloc(sizeof(*gc));
1883 memset(gc, 0, sizeof(*gc));
1885 gc->client = fio_get_client(client);
1889 client->client_data = gc;
1892 static GtkWidget *new_client_page(struct gui_entry *ge);
1894 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1896 struct gui_entry *ge;
1898 ge = malloc(sizeof(*ge));
1899 memset(ge, 0, sizeof(*ge));
1900 INIT_FLIST_HEAD(&ge->list);
1901 flist_add_tail(&ge->list, &ui->list);
1907 * FIXME: need more handling here
1909 static void ge_destroy(GtkWidget *w, gpointer data)
1911 struct gui_entry *ge = data;
1912 struct gfio_client *gc = ge->client;
1915 fio_put_client(gc->client);
1917 flist_del(&ge->list);
1921 static struct gui_entry *get_new_ge_with_tab(const char *name)
1923 struct gui_entry *ge;
1925 ge = alloc_new_gui_entry(&main_ui);
1927 ge->vbox = new_client_page(ge);
1928 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1930 ge->page_label = gtk_label_new(name);
1931 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1933 gtk_widget_show_all(main_ui.window);
1937 static void file_new(GtkWidget *w, gpointer data)
1939 get_new_ge_with_tab("Untitled");
1943 * Return the 'ge' corresponding to the tab. If the active tab is the
1944 * main tab, open a new tab.
1946 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1948 struct flist_head *entry;
1949 struct gui_entry *ge;
1952 return get_new_ge_with_tab("Untitled");
1954 flist_for_each(entry, &main_ui.list) {
1955 ge = flist_entry(entry, struct gui_entry, list);
1956 if (ge->page_num == cur_page)
1963 static void file_open(GtkWidget *w, gpointer data)
1965 struct gui *ui = data;
1967 GSList *filenames, *fn_glist;
1968 GtkFileFilter *filter;
1970 int port, type, server_start;
1971 struct gui_entry *ge;
1975 * Creates new tab if current tab is the main window, or the
1976 * current tab already has a client.
1978 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1979 ge = get_ge_from_page(cur_page);
1981 ge = get_new_ge_with_tab("Untitled");
1983 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1985 dialog = gtk_file_chooser_dialog_new("Open File",
1986 GTK_WINDOW(ui->window),
1987 GTK_FILE_CHOOSER_ACTION_OPEN,
1988 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1989 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1991 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1993 filter = gtk_file_filter_new();
1994 gtk_file_filter_add_pattern(filter, "*.fio");
1995 gtk_file_filter_add_pattern(filter, "*.job");
1996 gtk_file_filter_add_pattern(filter, "*.ini");
1997 gtk_file_filter_add_mime_type(filter, "text/fio");
1998 gtk_file_filter_set_name(filter, "Fio job file");
1999 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2001 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2002 gtk_widget_destroy(dialog);
2006 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2008 gtk_widget_destroy(dialog);
2010 if (get_connection_details(&host, &port, &type, &server_start))
2013 filenames = fn_glist;
2014 while (filenames != NULL) {
2015 struct fio_client *client;
2017 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2018 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2021 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2025 error = g_error_new(g_quark_from_string("fio"), 1,
2026 "Failed to add client %s", host);
2027 report_error(error);
2028 g_error_free(error);
2030 gfio_client_added(ge, client);
2032 g_free(filenames->data);
2033 filenames = g_slist_next(filenames);
2038 gfio_start_server();
2040 g_slist_free(fn_glist);
2043 static void file_save(GtkWidget *w, gpointer data)
2045 struct gui *ui = data;
2048 dialog = gtk_file_chooser_dialog_new("Save File",
2049 GTK_WINDOW(ui->window),
2050 GTK_FILE_CHOOSER_ACTION_SAVE,
2051 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2052 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2055 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2056 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2058 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2061 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2062 // save_job_file(filename);
2065 gtk_widget_destroy(dialog);
2068 static void view_log_destroy(GtkWidget *w, gpointer data)
2070 struct gui *ui = (struct gui *) data;
2072 gtk_widget_ref(ui->log_tree);
2073 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2074 gtk_widget_destroy(w);
2075 ui->log_view = NULL;
2078 static void view_log(GtkWidget *w, gpointer data)
2080 GtkWidget *win, *scroll, *vbox, *box;
2081 struct gui *ui = (struct gui *) data;
2086 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2087 gtk_window_set_title(GTK_WINDOW(win), "Log");
2088 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2090 scroll = gtk_scrolled_window_new(NULL, NULL);
2092 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2094 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2096 box = gtk_hbox_new(TRUE, 0);
2097 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2098 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2099 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2101 vbox = gtk_vbox_new(TRUE, 5);
2102 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2104 gtk_container_add(GTK_CONTAINER(win), vbox);
2105 gtk_widget_show_all(win);
2108 static void __update_graph_limits(struct gfio_graphs *g)
2110 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2111 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2114 static void update_graph_limits(void)
2116 struct flist_head *entry;
2117 struct gui_entry *ge;
2119 __update_graph_limits(&main_ui.graphs);
2121 flist_for_each(entry, &main_ui.list) {
2122 ge = flist_entry(entry, struct gui_entry, list);
2123 __update_graph_limits(&ge->graphs);
2127 static void preferences(GtkWidget *w, gpointer data)
2129 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2130 GtkWidget *hbox, *spin, *entry, *spin_int;
2133 dialog = gtk_dialog_new_with_buttons("Preferences",
2134 GTK_WINDOW(main_ui.window),
2135 GTK_DIALOG_DESTROY_WITH_PARENT,
2136 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2137 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2140 frame = gtk_frame_new("Graphing");
2141 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2142 vbox = gtk_vbox_new(FALSE, 6);
2143 gtk_container_add(GTK_CONTAINER(frame), vbox);
2145 hbox = gtk_hbox_new(FALSE, 5);
2146 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2147 entry = gtk_label_new("Font face to use for graph labels");
2148 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2150 font = gtk_font_button_new();
2151 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2153 box = gtk_vbox_new(FALSE, 6);
2154 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2156 hbox = gtk_hbox_new(FALSE, 5);
2157 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2158 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2159 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2161 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2163 box = gtk_vbox_new(FALSE, 6);
2164 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2166 hbox = gtk_hbox_new(FALSE, 5);
2167 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2168 entry = gtk_label_new("Client ETA request interval (msec)");
2169 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2171 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2172 frame = gtk_frame_new("Debug logging");
2173 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2174 vbox = gtk_vbox_new(FALSE, 6);
2175 gtk_container_add(GTK_CONTAINER(frame), vbox);
2177 box = gtk_hbox_new(FALSE, 6);
2178 gtk_container_add(GTK_CONTAINER(vbox), box);
2180 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2182 for (i = 0; i < FD_DEBUG_MAX; i++) {
2184 box = gtk_hbox_new(FALSE, 6);
2185 gtk_container_add(GTK_CONTAINER(vbox), box);
2189 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2190 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2191 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2194 gtk_widget_show_all(dialog);
2196 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2197 gtk_widget_destroy(dialog);
2201 for (i = 0; i < FD_DEBUG_MAX; i++) {
2204 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2206 fio_debug |= (1UL << i);
2209 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2210 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2211 update_graph_limits();
2212 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2214 gtk_widget_destroy(dialog);
2217 static void about_dialog(GtkWidget *w, gpointer data)
2219 const char *authors[] = {
2220 "Jens Axboe <axboe@kernel.dk>",
2221 "Stephen Carmeron <stephenmcameron@gmail.com>",
2224 const char *license[] = {
2225 "Fio is free software; you can redistribute it and/or modify "
2226 "it under the terms of the GNU General Public License as published by "
2227 "the Free Software Foundation; either version 2 of the License, or "
2228 "(at your option) any later version.\n",
2229 "Fio is distributed in the hope that it will be useful, "
2230 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2231 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2232 "GNU General Public License for more details.\n",
2233 "You should have received a copy of the GNU General Public License "
2234 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2235 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2237 char *license_trans;
2239 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2240 license[2], "\n", NULL);
2242 gtk_show_about_dialog(NULL,
2243 "program-name", "gfio",
2244 "comments", "Gtk2 UI for fio",
2245 "license", license_trans,
2246 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2248 "version", fio_version_string,
2249 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2250 "logo-icon-name", "fio",
2252 "wrap-license", TRUE,
2255 g_free(license_trans);
2258 static GtkActionEntry menu_items[] = {
2259 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2260 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2261 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2262 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2263 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2264 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2265 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2266 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2267 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2268 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2270 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2272 static const gchar *ui_string = " \
2274 <menubar name=\"MainMenu\"> \
2275 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2276 <menuitem name=\"New\" action=\"NewFile\" /> \
2277 <separator name=\"Separator1\"/> \
2278 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2279 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2280 <separator name=\"Separator2\"/> \
2281 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2282 <separator name=\"Separator3\"/> \
2283 <menuitem name=\"Quit\" action=\"Quit\" /> \
2285 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2286 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2288 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2289 <menuitem name=\"About\" action=\"About\" /> \
2295 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2298 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2301 action_group = gtk_action_group_new("Menu");
2302 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2304 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2305 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2307 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2308 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2311 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2312 GtkWidget *vbox, GtkUIManager *ui_manager)
2314 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2317 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2319 struct gui_entry *ge = (struct gui_entry *) data;
2322 index = gtk_combo_box_get_active(box);
2324 multitext_set_entry(&ge->eta.iotype, index);
2325 multitext_set_entry(&ge->eta.ioengine, index);
2326 multitext_set_entry(&ge->eta.iodepth, index);
2329 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2331 struct gui_entry *ge = (struct gui_entry *) data;
2333 multitext_free(&ge->eta.iotype);
2334 multitext_free(&ge->eta.ioengine);
2335 multitext_free(&ge->eta.iodepth);
2338 static GtkWidget *new_client_page(struct gui_entry *ge)
2340 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2343 main_vbox = gtk_vbox_new(FALSE, 3);
2345 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2346 ge->topvbox = gtk_vbox_new(FALSE, 3);
2347 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2348 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2350 probe = gtk_frame_new("Job");
2351 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2352 probe_frame = gtk_vbox_new(FALSE, 3);
2353 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2355 probe_box = gtk_hbox_new(FALSE, 3);
2356 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2357 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2358 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2359 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2360 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2362 probe_box = gtk_hbox_new(FALSE, 3);
2363 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2365 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2366 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2367 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2368 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2369 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2370 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2371 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2372 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2374 probe_box = gtk_hbox_new(FALSE, 3);
2375 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2376 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2377 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2378 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2379 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2382 * Only add this if we have a commit rate
2385 probe_box = gtk_hbox_new(FALSE, 3);
2386 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2388 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2389 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2391 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2392 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2396 * Set up a drawing area and IOPS and bandwidth graphs
2398 gdk_color_parse("white", &white);
2399 ge->graphs.drawing_area = gtk_drawing_area_new();
2400 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2401 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2402 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2403 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2404 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2405 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2406 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2407 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2408 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2409 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2410 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2411 ge->graphs.drawing_area);
2412 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2415 setup_graphs(&ge->graphs);
2418 * Set up alignments for widgets at the bottom of ui,
2419 * align bottom left, expand horizontally but not vertically
2421 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2422 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2423 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2424 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2427 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2430 * Set up thread status progress bar
2432 ge->thread_status_pb = gtk_progress_bar_new();
2433 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2434 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2435 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2441 static GtkWidget *new_main_page(struct gui *ui)
2443 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2446 main_vbox = gtk_vbox_new(FALSE, 3);
2449 * Set up alignments for widgets at the top of ui,
2450 * align top left, expand horizontally but not vertically
2452 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2453 ui->topvbox = gtk_vbox_new(FALSE, 0);
2454 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2455 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2457 probe = gtk_frame_new("Run statistics");
2458 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2459 probe_frame = gtk_vbox_new(FALSE, 3);
2460 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2462 probe_box = gtk_hbox_new(FALSE, 3);
2463 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2464 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2465 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2466 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2467 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2468 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2471 * Only add this if we have a commit rate
2474 probe_box = gtk_hbox_new(FALSE, 3);
2475 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2477 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2478 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2480 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2481 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2485 * Set up a drawing area and IOPS and bandwidth graphs
2487 gdk_color_parse("white", &white);
2488 ui->graphs.drawing_area = gtk_drawing_area_new();
2489 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2490 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2491 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2492 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2493 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2494 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2495 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2496 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2497 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2498 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2499 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2500 ui->graphs.drawing_area);
2501 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2504 setup_graphs(&ui->graphs);
2507 * Set up alignments for widgets at the bottom of ui,
2508 * align bottom left, expand horizontally but not vertically
2510 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2511 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2512 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2513 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2517 * Set up thread status progress bar
2519 ui->thread_status_pb = gtk_progress_bar_new();
2520 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2521 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2522 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2527 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2528 guint page, gpointer data)
2534 static void init_ui(int *argc, char **argv[], struct gui *ui)
2536 GtkSettings *settings;
2537 GtkUIManager *uimanager;
2538 GtkWidget *menu, *vbox;
2540 /* Magical g*thread incantation, you just need this thread stuff.
2541 * Without it, the update that happens in gfio_update_thread_status
2542 * doesn't really happen in a timely fashion, you need expose events
2544 if (!g_thread_supported())
2545 g_thread_init(NULL);
2548 gtk_init(argc, argv);
2549 settings = gtk_settings_get_default();
2550 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2553 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2554 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2555 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2557 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2558 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2560 ui->vbox = gtk_vbox_new(FALSE, 0);
2561 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2563 uimanager = gtk_ui_manager_new();
2564 menu = get_menubar_menu(ui->window, uimanager, ui);
2565 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2567 ui->notebook = gtk_notebook_new();
2568 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2569 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2570 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2571 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2573 vbox = new_main_page(ui);
2575 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2577 gfio_ui_setup_log(ui);
2579 gtk_widget_show_all(ui->window);
2582 int main(int argc, char *argv[], char *envp[])
2584 if (initialize_fio(envp))
2586 if (fio_init_options())
2589 memset(&main_ui, 0, sizeof(main_ui));
2590 INIT_FLIST_HEAD(&main_ui.list);
2592 init_ui(&argc, &argv, &main_ui);
2594 gdk_threads_enter();
2596 gdk_threads_leave();