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 },
83 GtkWidget *write_iops;
89 #define DRAWING_AREA_XDIM 1000
90 #define DRAWING_AREA_YDIM 400
91 GtkWidget *drawing_area;
92 int drawing_area_xdim;
93 int drawing_area_ydim;
95 struct graph *iops_graph;
96 struct graph *bandwidth_graph;
100 * Main window widgets and data
107 GtkWidget *bottomalign;
108 GtkWidget *thread_status_pb;
109 GtkWidget *buttonbox;
110 GtkWidget *scrolled_window;
112 GtkWidget *error_info_bar;
113 GtkWidget *error_label;
114 GtkListStore *log_model;
117 struct gfio_graphs graphs;
118 struct probe_widget probe;
119 struct eta_widget eta;
125 struct flist_head list;
132 struct flist_head list;
138 GtkWidget *bottomalign;
139 GtkWidget *thread_status_pb;
140 GtkWidget *buttonbox;
141 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
142 GtkWidget *scrolled_window;
144 GtkWidget *error_info_bar;
145 GtkWidget *error_label;
146 GtkWidget *results_notebook;
147 GtkWidget *results_window;
148 GtkListStore *log_model;
151 struct gfio_graphs graphs;
152 struct probe_widget probe;
153 struct eta_widget eta;
154 GtkWidget *page_label;
158 struct gfio_client *client;
164 struct gui_entry *ge;
165 struct fio_client *client;
166 GtkWidget *results_widget;
167 GtkWidget *disk_util_frame;
168 GtkWidget *err_entry;
169 unsigned int job_added;
170 struct thread_options o;
173 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
174 static void gfio_update_thread_status_all(char *status_message, double perc);
175 void report_error(GError *error);
177 static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
179 switch (power_of_ten) {
180 case 9: graph_y_title(g, "Billions of IOs / sec");
182 case 6: graph_y_title(g, "Millions of IOs / sec");
184 case 3: graph_y_title(g, "Thousands of IOs / sec");
187 default: graph_y_title(g, "IOs / sec");
192 static struct graph *setup_iops_graph(void)
196 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
197 graph_title(g, "IOPS");
198 graph_x_title(g, "Time (secs)");
199 graph_y_title(g, "IOs / sec");
200 graph_add_label(g, "Read IOPS");
201 graph_add_label(g, "Write IOPS");
202 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
203 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
204 line_graph_set_data_count_limit(g, gfio_graph_limit);
205 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
209 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
211 switch (power_of_ten) {
212 case 9: graph_y_title(g, "Petabytes / sec");
214 case 6: graph_y_title(g, "Gigabytes / sec");
216 case 3: graph_y_title(g, "Megabytes / sec");
219 default: graph_y_title(g, "Kilobytes / sec");
224 static struct graph *setup_bandwidth_graph(void)
228 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
229 graph_title(g, "Bandwidth");
230 graph_x_title(g, "Time (secs)");
231 graph_y_title(g, "Kbytes / sec");
232 graph_add_label(g, "Read Bandwidth");
233 graph_add_label(g, "Write Bandwidth");
234 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
235 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
236 line_graph_set_data_count_limit(g, 100);
237 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
241 static void setup_graphs(struct gfio_graphs *g)
243 g->iops_graph = setup_iops_graph();
244 g->bandwidth_graph = setup_bandwidth_graph();
247 static void clear_ge_ui_info(struct gui_entry *ge)
249 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
250 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
251 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
252 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
254 /* should we empty it... */
255 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
257 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
258 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
259 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
260 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
261 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
262 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
263 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
264 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
265 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
268 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
270 GtkWidget *entry, *frame;
272 frame = gtk_frame_new(label);
273 entry = gtk_combo_box_new_text();
274 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
275 gtk_container_add(GTK_CONTAINER(frame), entry);
280 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
282 GtkWidget *entry, *frame;
284 frame = gtk_frame_new(label);
285 entry = gtk_entry_new();
286 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
287 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
288 gtk_container_add(GTK_CONTAINER(frame), entry);
293 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
295 GtkWidget *label_widget;
298 frame = gtk_frame_new(label);
299 label_widget = gtk_label_new(NULL);
300 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
301 gtk_container_add(GTK_CONTAINER(frame), label_widget);
306 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
308 GtkWidget *button, *box;
310 box = gtk_hbox_new(FALSE, 3);
311 gtk_container_add(GTK_CONTAINER(hbox), box);
313 button = gtk_spin_button_new_with_range(min, max, 1.0);
314 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
316 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
317 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
322 static void gfio_set_connected(struct gui_entry *ge, int connected)
325 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
327 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
328 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
331 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
332 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
333 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
334 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
338 static void label_set_int_value(GtkWidget *entry, unsigned int val)
342 sprintf(tmp, "%u", val);
343 gtk_label_set_text(GTK_LABEL(entry), tmp);
346 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
350 sprintf(tmp, "%u", val);
351 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
355 #define ALIGN_RIGHT 2
359 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
361 GtkCellRenderer *renderer;
362 GtkTreeViewColumn *col;
363 double xalign = 0.0; /* left as default */
364 PangoAlignment align;
367 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
368 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
370 visible = !(flags & INVISIBLE);
372 renderer = gtk_cell_renderer_text_new();
373 col = gtk_tree_view_column_new();
375 gtk_tree_view_column_set_title(col, title);
376 if (!(flags & UNSORTABLE))
377 gtk_tree_view_column_set_sort_column_id(col, index);
378 gtk_tree_view_column_set_resizable(col, TRUE);
379 gtk_tree_view_column_pack_start(col, renderer, TRUE);
380 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
381 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
383 case PANGO_ALIGN_LEFT:
386 case PANGO_ALIGN_CENTER:
389 case PANGO_ALIGN_RIGHT:
393 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
394 gtk_tree_view_column_set_visible(col, visible);
395 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
399 static void gfio_ui_setup_log(struct gui *ui)
401 GtkTreeSelection *selection;
403 GtkWidget *tree_view;
405 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
407 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
408 gtk_widget_set_can_focus(tree_view, FALSE);
410 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
411 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
412 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
413 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
415 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
416 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
417 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
418 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
420 ui->log_model = model;
421 ui->log_tree = tree_view;
424 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
430 GType types[FIO_IO_U_LIST_MAX_LEN];
431 GtkWidget *tree_view;
432 GtkTreeSelection *selection;
437 for (i = 0; i < len; i++)
438 types[i] = G_TYPE_INT;
440 model = gtk_list_store_newv(len, types);
442 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
443 gtk_widget_set_can_focus(tree_view, FALSE);
445 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
446 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
448 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
449 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
451 for (i = 0; i < len; i++) {
454 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
455 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
458 gtk_list_store_append(model, &iter);
460 for (i = 0; i < len; i++) {
462 ovals[i] = (ovals[i] + 999) / 1000;
463 gtk_list_store_set(model, &iter, i, ovals[i], -1);
469 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
472 unsigned int *io_u_plat = ts->io_u_plat[ddir];
473 unsigned long nr = ts->clat_stat[ddir].samples;
474 fio_fp64_t *plist = ts->percentile_list;
475 unsigned int *ovals, len, minv, maxv, scale_down;
477 GtkWidget *tree_view, *frame, *hbox;
480 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
485 * We default to usecs, but if the value range is such that we
486 * should scale down to msecs, do that.
488 if (minv > 2000 && maxv > 99999) {
496 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
498 sprintf(tmp, "Completion percentiles (%s)", base);
499 frame = gtk_frame_new(tmp);
500 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
502 hbox = gtk_hbox_new(FALSE, 3);
503 gtk_container_add(GTK_CONTAINER(frame), hbox);
505 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
511 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
512 unsigned long max, double mean, double dev)
514 const char *base = "(usec)";
515 GtkWidget *hbox, *label, *frame;
519 if (!usec_to_msec(&min, &max, &mean, &dev))
522 minp = num2str(min, 6, 1, 0);
523 maxp = num2str(max, 6, 1, 0);
525 sprintf(tmp, "%s %s", name, base);
526 frame = gtk_frame_new(tmp);
527 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
529 hbox = gtk_hbox_new(FALSE, 3);
530 gtk_container_add(GTK_CONTAINER(frame), hbox);
532 label = new_info_label_in_frame(hbox, "Minimum");
533 gtk_label_set_text(GTK_LABEL(label), minp);
534 label = new_info_label_in_frame(hbox, "Maximum");
535 gtk_label_set_text(GTK_LABEL(label), maxp);
536 label = new_info_label_in_frame(hbox, "Average");
537 sprintf(tmp, "%5.02f", mean);
538 gtk_label_set_text(GTK_LABEL(label), tmp);
539 label = new_info_label_in_frame(hbox, "Standard deviation");
540 sprintf(tmp, "%5.02f", dev);
541 gtk_label_set_text(GTK_LABEL(label), tmp);
552 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
553 struct thread_stat *ts, int ddir)
555 const char *ddir_label[2] = { "Read", "Write" };
556 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
557 unsigned long min[3], max[3], runt;
558 unsigned long long bw, iops;
559 unsigned int flags = 0;
560 double mean[3], dev[3];
561 char *io_p, *bw_p, *iops_p;
564 if (!ts->runtime[ddir])
567 i2p = is_power_of_2(rs->kb_base);
568 runt = ts->runtime[ddir];
570 bw = (1000 * ts->io_bytes[ddir]) / runt;
571 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
572 bw_p = num2str(bw, 6, 1, i2p);
574 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
575 iops_p = num2str(iops, 6, 1, 0);
577 box = gtk_hbox_new(FALSE, 3);
578 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
580 frame = gtk_frame_new(ddir_label[ddir]);
581 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
583 main_vbox = gtk_vbox_new(FALSE, 3);
584 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
586 box = gtk_hbox_new(FALSE, 3);
587 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
589 label = new_info_label_in_frame(box, "IO");
590 gtk_label_set_text(GTK_LABEL(label), io_p);
591 label = new_info_label_in_frame(box, "Bandwidth");
592 gtk_label_set_text(GTK_LABEL(label), bw_p);
593 label = new_info_label_in_frame(box, "IOPS");
594 gtk_label_set_text(GTK_LABEL(label), iops_p);
595 label = new_info_label_in_frame(box, "Runtime (msec)");
596 label_set_int_value(label, ts->runtime[ddir]);
598 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
599 double p_of_agg = 100.0;
600 const char *bw_str = "KB";
604 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
605 if (p_of_agg > 100.0)
609 if (mean[0] > 999999.9) {
617 sprintf(tmp, "Bandwidth (%s)", bw_str);
618 frame = gtk_frame_new(tmp);
619 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
621 box = gtk_hbox_new(FALSE, 3);
622 gtk_container_add(GTK_CONTAINER(frame), box);
624 label = new_info_label_in_frame(box, "Minimum");
625 label_set_int_value(label, min[0]);
626 label = new_info_label_in_frame(box, "Maximum");
627 label_set_int_value(label, max[0]);
628 label = new_info_label_in_frame(box, "Percentage of jobs");
629 sprintf(tmp, "%3.2f%%", p_of_agg);
630 gtk_label_set_text(GTK_LABEL(label), tmp);
631 label = new_info_label_in_frame(box, "Average");
632 sprintf(tmp, "%5.02f", mean[0]);
633 gtk_label_set_text(GTK_LABEL(label), tmp);
634 label = new_info_label_in_frame(box, "Standard deviation");
635 sprintf(tmp, "%5.02f", dev[0]);
636 gtk_label_set_text(GTK_LABEL(label), tmp);
639 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
641 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
643 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
647 frame = gtk_frame_new("Latency");
648 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
650 vbox = gtk_vbox_new(FALSE, 3);
651 gtk_container_add(GTK_CONTAINER(frame), vbox);
653 if (flags & GFIO_SLAT)
654 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
655 if (flags & GFIO_CLAT)
656 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
657 if (flags & GFIO_LAT)
658 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
661 if (ts->clat_percentiles)
662 gfio_show_clat_percentiles(main_vbox, ts, ddir);
670 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
673 GtkWidget *tree_view;
674 GtkTreeSelection *selection;
681 * Check if all are empty, in which case don't bother
683 for (i = 0, skipped = 0; i < num; i++)
690 types = malloc(num * sizeof(GType));
692 for (i = 0; i < num; i++)
693 types[i] = G_TYPE_STRING;
695 model = gtk_list_store_newv(num, types);
699 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
700 gtk_widget_set_can_focus(tree_view, FALSE);
702 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
703 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
705 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
706 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
708 for (i = 0; i < num; i++)
709 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
711 gtk_list_store_append(model, &iter);
713 for (i = 0; i < num; i++) {
717 sprintf(fbuf, "0.00");
719 sprintf(fbuf, "%3.2f%%", lat[i]);
721 gtk_list_store_set(model, &iter, i, fbuf, -1);
727 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
729 GtkWidget *box, *frame, *tree_view;
730 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
731 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
732 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
733 "250", "500", "750", "1000", };
734 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
735 "250", "500", "750", "1000", "2000",
738 stat_calc_lat_u(ts, io_u_lat_u);
739 stat_calc_lat_m(ts, io_u_lat_m);
741 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
743 frame = gtk_frame_new("Latency buckets (usec)");
744 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
746 box = gtk_hbox_new(FALSE, 3);
747 gtk_container_add(GTK_CONTAINER(frame), box);
748 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
751 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
753 frame = gtk_frame_new("Latency buckets (msec)");
754 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
756 box = gtk_hbox_new(FALSE, 3);
757 gtk_container_add(GTK_CONTAINER(frame), box);
758 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
762 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
764 GtkWidget *box, *frame, *entry;
765 double usr_cpu, sys_cpu;
766 unsigned long runtime;
769 runtime = ts->total_run_time;
771 double runt = (double) runtime;
773 usr_cpu = (double) ts->usr_time * 100 / runt;
774 sys_cpu = (double) ts->sys_time * 100 / runt;
780 frame = gtk_frame_new("OS resources");
781 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
783 box = gtk_hbox_new(FALSE, 3);
784 gtk_container_add(GTK_CONTAINER(frame), box);
786 entry = new_info_entry_in_frame(box, "User CPU");
787 sprintf(tmp, "%3.2f%%", usr_cpu);
788 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
789 entry = new_info_entry_in_frame(box, "System CPU");
790 sprintf(tmp, "%3.2f%%", sys_cpu);
791 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
792 entry = new_info_entry_in_frame(box, "Context switches");
793 entry_set_int_value(entry, ts->ctx);
794 entry = new_info_entry_in_frame(box, "Major faults");
795 entry_set_int_value(entry, ts->majf);
796 entry = new_info_entry_in_frame(box, "Minor faults");
797 entry_set_int_value(entry, ts->minf);
799 static void gfio_add_sc_depths_tree(GtkListStore *model,
800 struct thread_stat *ts, unsigned int len,
803 double io_u_dist[FIO_IO_U_MAP_NR];
805 /* Bits 0, and 3-8 */
806 const int add_mask = 0x1f9;
810 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
812 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
814 gtk_list_store_append(model, &iter);
816 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
818 for (i = 1, j = 0; i < len; i++) {
821 if (!(add_mask & (1UL << (i - 1))))
822 sprintf(fbuf, "0.0%%");
824 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
828 gtk_list_store_set(model, &iter, i, fbuf, -1);
833 static void gfio_add_total_depths_tree(GtkListStore *model,
834 struct thread_stat *ts, unsigned int len)
836 double io_u_dist[FIO_IO_U_MAP_NR];
838 /* Bits 1-6, and 8 */
839 const int add_mask = 0x17e;
842 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
844 gtk_list_store_append(model, &iter);
846 gtk_list_store_set(model, &iter, 0, "Total", -1);
848 for (i = 1, j = 0; i < len; i++) {
851 if (!(add_mask & (1UL << (i - 1))))
852 sprintf(fbuf, "0.0%%");
854 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
858 gtk_list_store_set(model, &iter, i, fbuf, -1);
863 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
865 GtkWidget *frame, *box, *tree_view;
866 GtkTreeSelection *selection;
868 GType types[FIO_IO_U_MAP_NR + 1];
871 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
873 frame = gtk_frame_new("IO depths");
874 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
876 box = gtk_hbox_new(FALSE, 3);
877 gtk_container_add(GTK_CONTAINER(frame), box);
879 for (i = 0; i < NR_LABELS; i++)
880 types[i] = G_TYPE_STRING;
882 model = gtk_list_store_newv(NR_LABELS, types);
884 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
885 gtk_widget_set_can_focus(tree_view, FALSE);
887 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
888 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
890 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
891 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
893 for (i = 0; i < NR_LABELS; i++)
894 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
896 gfio_add_total_depths_tree(model, ts, NR_LABELS);
897 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
898 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
900 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
903 static gboolean results_window_delete(GtkWidget *w, gpointer data)
905 struct gui_entry *ge = (struct gui_entry *) data;
907 gtk_widget_destroy(w);
908 ge->results_window = NULL;
909 ge->results_notebook = NULL;
913 static GtkWidget *get_results_window(struct gui_entry *ge)
915 GtkWidget *win, *notebook;
917 if (ge->results_window)
918 return ge->results_notebook;
920 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
921 gtk_window_set_title(GTK_WINDOW(win), "Results");
922 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
923 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
924 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
926 notebook = gtk_notebook_new();
927 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
928 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
929 gtk_container_add(GTK_CONTAINER(win), notebook);
931 ge->results_window = win;
932 ge->results_notebook = notebook;
933 return ge->results_notebook;
936 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
937 struct group_run_stats *rs)
939 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
940 struct gfio_client *gc = client->client_data;
944 res_win = get_results_window(gc->ge);
946 scroll = gtk_scrolled_window_new(NULL, NULL);
947 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
948 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
950 vbox = gtk_vbox_new(FALSE, 3);
952 box = gtk_hbox_new(FALSE, 0);
953 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
955 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
957 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
959 gc->results_widget = vbox;
961 entry = new_info_entry_in_frame(box, "Name");
962 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
963 if (strlen(ts->description)) {
964 entry = new_info_entry_in_frame(box, "Description");
965 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
967 entry = new_info_entry_in_frame(box, "Group ID");
968 entry_set_int_value(entry, ts->groupid);
969 entry = new_info_entry_in_frame(box, "Jobs");
970 entry_set_int_value(entry, ts->members);
971 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
972 entry_set_int_value(entry, ts->error);
973 entry = new_info_entry_in_frame(box, "PID");
974 entry_set_int_value(entry, ts->pid);
976 if (ts->io_bytes[DDIR_READ])
977 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
978 if (ts->io_bytes[DDIR_WRITE])
979 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
981 gfio_show_latency_buckets(vbox, ts);
982 gfio_show_cpu_usage(vbox, ts);
983 gfio_show_io_depths(vbox, ts);
985 gtk_widget_show_all(gc->ge->results_window);
989 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
991 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
992 struct gui *ui = &main_ui;
996 char tmp[64], timebuf[80];
999 tm = localtime(&sec);
1000 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1001 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1003 gdk_threads_enter();
1005 gtk_list_store_append(ui->log_model, &iter);
1006 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1007 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1008 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1009 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1011 if (p->level == FIO_LOG_ERR)
1012 view_log(NULL, (gpointer) ui);
1014 gdk_threads_leave();
1017 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1019 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1020 struct gfio_client *gc = client->client_data;
1021 GtkWidget *box, *frame, *entry, *vbox;
1025 gdk_threads_enter();
1027 if (!gc->results_widget)
1030 if (!gc->disk_util_frame) {
1031 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1032 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1035 vbox = gtk_vbox_new(FALSE, 3);
1036 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1038 frame = gtk_frame_new((char *) p->dus.name);
1039 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1041 box = gtk_vbox_new(FALSE, 3);
1042 gtk_container_add(GTK_CONTAINER(frame), box);
1044 frame = gtk_frame_new("Read");
1045 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1046 vbox = gtk_hbox_new(TRUE, 3);
1047 gtk_container_add(GTK_CONTAINER(frame), vbox);
1048 entry = new_info_entry_in_frame(vbox, "IOs");
1049 entry_set_int_value(entry, p->dus.ios[0]);
1050 entry = new_info_entry_in_frame(vbox, "Merges");
1051 entry_set_int_value(entry, p->dus.merges[0]);
1052 entry = new_info_entry_in_frame(vbox, "Sectors");
1053 entry_set_int_value(entry, p->dus.sectors[0]);
1054 entry = new_info_entry_in_frame(vbox, "Ticks");
1055 entry_set_int_value(entry, p->dus.ticks[0]);
1057 frame = gtk_frame_new("Write");
1058 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1059 vbox = gtk_hbox_new(TRUE, 3);
1060 gtk_container_add(GTK_CONTAINER(frame), vbox);
1061 entry = new_info_entry_in_frame(vbox, "IOs");
1062 entry_set_int_value(entry, p->dus.ios[1]);
1063 entry = new_info_entry_in_frame(vbox, "Merges");
1064 entry_set_int_value(entry, p->dus.merges[1]);
1065 entry = new_info_entry_in_frame(vbox, "Sectors");
1066 entry_set_int_value(entry, p->dus.sectors[1]);
1067 entry = new_info_entry_in_frame(vbox, "Ticks");
1068 entry_set_int_value(entry, p->dus.ticks[1]);
1070 frame = gtk_frame_new("Shared");
1071 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1072 vbox = gtk_hbox_new(TRUE, 3);
1073 gtk_container_add(GTK_CONTAINER(frame), vbox);
1074 entry = new_info_entry_in_frame(vbox, "IO ticks");
1075 entry_set_int_value(entry, p->dus.io_ticks);
1076 entry = new_info_entry_in_frame(vbox, "Time in queue");
1077 entry_set_int_value(entry, p->dus.time_in_queue);
1081 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1085 sprintf(tmp, "%3.2f%%", util);
1086 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1087 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1089 gtk_widget_show_all(gc->results_widget);
1091 gdk_threads_leave();
1094 extern int sum_stat_clients;
1095 extern struct thread_stat client_ts;
1096 extern struct group_run_stats client_gs;
1098 static int sum_stat_nr;
1100 static void gfio_thread_status_op(struct fio_client *client,
1101 struct fio_net_cmd *cmd)
1103 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1105 gfio_display_ts(client, &p->ts, &p->rs);
1107 if (sum_stat_clients == 1)
1110 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1111 sum_group_stats(&client_gs, &p->rs);
1113 client_ts.members++;
1114 client_ts.groupid = p->ts.groupid;
1116 if (++sum_stat_nr == sum_stat_clients) {
1117 strcpy(client_ts.name, "All clients");
1118 gfio_display_ts(client, &client_ts, &client_gs);
1122 static void gfio_group_stats_op(struct fio_client *client,
1123 struct fio_net_cmd *cmd)
1125 /* We're ignoring group stats for now */
1128 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1131 struct gfio_graphs *g = data;
1133 g->drawing_area_xdim = w->allocation.width;
1134 g->drawing_area_ydim = w->allocation.height;
1138 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1140 struct gfio_graphs *g = p;
1143 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1144 g->drawing_area_ydim);
1145 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1146 g->drawing_area_ydim);
1147 cr = gdk_cairo_create(w->window);
1149 cairo_set_source_rgb(cr, 0, 0, 0);
1152 cairo_translate(cr, 0, 0);
1153 line_graph_draw(g->bandwidth_graph, cr);
1158 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1159 line_graph_draw(g->iops_graph, cr);
1168 * Client specific ETA
1170 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1172 struct gfio_client *gc = client->client_data;
1173 struct gui_entry *ge = gc->ge;
1174 static int eta_good;
1181 gdk_threads_enter();
1186 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1187 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1188 eta_to_str(eta_str, je->eta_sec);
1191 sprintf(tmp, "%u", je->nr_running);
1192 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1193 sprintf(tmp, "%u", je->files_open);
1194 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1197 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1198 if (je->m_rate || je->t_rate) {
1201 mr = num2str(je->m_rate, 4, 0, i2p);
1202 tr = num2str(je->t_rate, 4, 0, i2p);
1203 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1204 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1207 } else if (je->m_iops || je->t_iops)
1208 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1210 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1211 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1212 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1213 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1216 if (je->eta_sec != INT_MAX && je->nr_running) {
1220 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1221 strcpy(output, "-.-% done");
1225 sprintf(output, "%3.1f%% done", perc);
1228 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1229 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1231 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1232 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1234 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1235 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1236 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1237 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1239 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1240 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1241 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1242 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1251 char *dst = output + strlen(output);
1253 sprintf(dst, " - %s", eta_str);
1256 gfio_update_thread_status(ge, output, perc);
1257 gdk_threads_leave();
1261 * Update ETA in main window for all clients
1263 static void gfio_update_all_eta(struct jobs_eta *je)
1265 struct gui *ui = &main_ui;
1266 static int eta_good;
1272 gdk_threads_enter();
1277 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1278 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1279 eta_to_str(eta_str, je->eta_sec);
1283 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1284 if (je->m_rate || je->t_rate) {
1287 mr = num2str(je->m_rate, 4, 0, i2p);
1288 tr = num2str(je->t_rate, 4, 0, i2p);
1289 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1290 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1293 } else if (je->m_iops || je->t_iops)
1294 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1296 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1297 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1298 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1299 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1302 entry_set_int_value(ui->eta.jobs, je->nr_running);
1304 if (je->eta_sec != INT_MAX && je->nr_running) {
1308 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1309 strcpy(output, "-.-% done");
1313 sprintf(output, "%3.1f%% done", perc);
1316 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1317 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1319 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1320 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1322 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1323 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1324 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1325 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1327 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1328 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1329 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1330 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1339 char *dst = output + strlen(output);
1341 sprintf(dst, " - %s", eta_str);
1344 gfio_update_thread_status_all(output, perc);
1345 gdk_threads_leave();
1348 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1350 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1351 struct gfio_client *gc = client->client_data;
1352 struct gui_entry *ge = gc->ge;
1353 const char *os, *arch;
1356 os = fio_get_os_string(probe->os);
1360 arch = fio_get_arch_string(probe->arch);
1365 client->name = strdup((char *) probe->hostname);
1367 gdk_threads_enter();
1369 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1370 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1371 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1372 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1373 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1375 gfio_set_connected(ge, 1);
1377 gdk_threads_leave();
1380 static void gfio_update_thread_status(struct gui_entry *ge,
1381 char *status_message, double perc)
1383 static char message[100];
1384 const char *m = message;
1386 strncpy(message, status_message, sizeof(message) - 1);
1387 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1388 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1389 gtk_widget_queue_draw(main_ui.window);
1392 static void gfio_update_thread_status_all(char *status_message, double perc)
1394 struct gui *ui = &main_ui;
1395 static char message[100];
1396 const char *m = message;
1398 strncpy(message, status_message, sizeof(message) - 1);
1399 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1400 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1401 gtk_widget_queue_draw(ui->window);
1404 static void gfio_quit_op(struct fio_client *client)
1406 struct gfio_client *gc = client->client_data;
1408 gdk_threads_enter();
1409 gfio_set_connected(gc->ge, 0);
1410 gdk_threads_leave();
1413 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1415 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1416 struct gfio_client *gc = client->client_data;
1417 struct thread_options *o = &gc->o;
1418 struct gui_entry *ge = gc->ge;
1421 convert_thread_options_to_cpu(o, &p->top);
1423 gdk_threads_enter();
1425 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1427 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1428 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1430 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1431 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1433 sprintf(tmp, "%u", o->iodepth);
1434 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1438 gdk_threads_leave();
1441 static void gfio_client_timed_out(struct fio_client *client)
1443 struct gfio_client *gc = client->client_data;
1444 GtkWidget *dialog, *label, *content;
1447 gdk_threads_enter();
1449 gfio_set_connected(gc->ge, 0);
1450 clear_ge_ui_info(gc->ge);
1452 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1454 dialog = gtk_dialog_new_with_buttons("Timed out!",
1455 GTK_WINDOW(main_ui.window),
1456 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1457 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1459 /* gtk_dialog_get_content_area() is 2.14 and newer */
1460 content = GTK_DIALOG(dialog)->vbox;
1462 label = gtk_label_new((const gchar *) buf);
1463 gtk_container_add(GTK_CONTAINER(content), label);
1464 gtk_widget_show_all(dialog);
1465 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1467 gtk_dialog_run(GTK_DIALOG(dialog));
1468 gtk_widget_destroy(dialog);
1470 gdk_threads_leave();
1473 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1475 struct gfio_client *gc = client->client_data;
1477 gdk_threads_enter();
1479 gfio_set_connected(gc->ge, 0);
1482 entry_set_int_value(gc->err_entry, client->error);
1484 gdk_threads_leave();
1487 struct client_ops gfio_client_ops = {
1488 .text_op = gfio_text_op,
1489 .disk_util = gfio_disk_util_op,
1490 .thread_status = gfio_thread_status_op,
1491 .group_stats = gfio_group_stats_op,
1492 .jobs_eta = gfio_update_client_eta,
1493 .eta = gfio_update_all_eta,
1494 .probe = gfio_probe_op,
1495 .quit = gfio_quit_op,
1496 .add_job = gfio_add_job_op,
1497 .timed_out = gfio_client_timed_out,
1498 .stop = gfio_client_stop,
1499 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1500 .stay_connected = 1,
1503 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1504 __attribute__((unused)) gpointer data)
1509 static void *job_thread(void *arg)
1511 struct gui *ui = arg;
1513 ui->handler_running = 1;
1514 fio_handle_clients(&gfio_client_ops);
1515 ui->handler_running = 0;
1519 static int send_job_files(struct gui_entry *ge)
1521 struct gfio_client *gc = ge->client;
1524 for (i = 0; i < ge->nr_job_files; i++) {
1525 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1529 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1530 report_error(error);
1531 g_error_free(error);
1536 free(ge->job_files[i]);
1537 ge->job_files[i] = NULL;
1539 while (i < ge->nr_job_files) {
1540 free(ge->job_files[i]);
1541 ge->job_files[i] = NULL;
1548 static void *server_thread(void *arg)
1551 gfio_server_running = 1;
1552 fio_start_server(NULL);
1553 gfio_server_running = 0;
1557 static void gfio_start_server(void)
1559 struct gui *ui = &main_ui;
1561 if (!gfio_server_running) {
1562 gfio_server_running = 1;
1563 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1564 pthread_detach(ui->server_t);
1568 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1571 struct gui_entry *ge = data;
1572 struct gfio_client *gc = ge->client;
1574 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1575 fio_start_client(gc->client);
1578 static void file_open(GtkWidget *w, gpointer data);
1580 static void connect_clicked(GtkWidget *widget, gpointer data)
1582 struct gui_entry *ge = data;
1583 struct gfio_client *gc = ge->client;
1585 if (!ge->connected) {
1588 if (!ge->nr_job_files)
1589 file_open(widget, data);
1590 if (!ge->nr_job_files)
1593 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1594 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1595 ret = fio_client_connect(gc->client);
1597 if (!ge->ui->handler_running)
1598 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1599 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1600 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1604 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1605 report_error(error);
1606 g_error_free(error);
1609 fio_client_terminate(gc->client);
1610 gfio_set_connected(ge, 0);
1611 clear_ge_ui_info(ge);
1615 static void send_clicked(GtkWidget *widget, gpointer data)
1617 struct gui_entry *ge = data;
1619 if (send_job_files(ge)) {
1622 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);
1623 report_error(error);
1624 g_error_free(error);
1626 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1629 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1630 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1633 static GtkWidget *add_button(GtkWidget *buttonbox,
1634 struct button_spec *buttonspec, gpointer data)
1636 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1638 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1639 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1640 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1641 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1646 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1651 for (i = 0; i < nbuttons; i++)
1652 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1655 static void on_info_bar_response(GtkWidget *widget, gint response,
1658 struct gui *ui = &main_ui;
1660 if (response == GTK_RESPONSE_OK) {
1661 gtk_widget_destroy(widget);
1662 ui->error_info_bar = NULL;
1666 void report_error(GError *error)
1668 struct gui *ui = &main_ui;
1670 if (ui->error_info_bar == NULL) {
1671 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1674 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1675 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1678 ui->error_label = gtk_label_new(error->message);
1679 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1680 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1682 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1683 gtk_widget_show_all(ui->vbox);
1686 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1687 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1691 struct connection_widgets
1698 static void hostname_cb(GtkEntry *entry, gpointer data)
1700 struct connection_widgets *cw = data;
1701 int uses_net = 0, is_localhost = 0;
1706 * Check whether to display the 'auto start backend' box
1707 * or not. Show it if we are a localhost and using network,
1708 * or using a socket.
1710 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1711 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1716 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1717 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1718 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1719 !strcmp(text, "ip6-loopback"))
1723 if (!uses_net || is_localhost) {
1724 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1725 gtk_widget_set_sensitive(cw->button, 1);
1727 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1728 gtk_widget_set_sensitive(cw->button, 0);
1732 static int get_connection_details(char **host, int *port, int *type,
1735 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1736 struct connection_widgets cw;
1739 dialog = gtk_dialog_new_with_buttons("Connection details",
1740 GTK_WINDOW(main_ui.window),
1741 GTK_DIALOG_DESTROY_WITH_PARENT,
1742 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1743 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1745 frame = gtk_frame_new("Hostname / socket name");
1746 /* gtk_dialog_get_content_area() is 2.14 and newer */
1747 vbox = GTK_DIALOG(dialog)->vbox;
1748 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1750 box = gtk_vbox_new(FALSE, 6);
1751 gtk_container_add(GTK_CONTAINER(frame), box);
1753 hbox = gtk_hbox_new(TRUE, 10);
1754 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1755 cw.hentry = gtk_entry_new();
1756 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1757 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1759 frame = gtk_frame_new("Port");
1760 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1761 box = gtk_vbox_new(FALSE, 10);
1762 gtk_container_add(GTK_CONTAINER(frame), box);
1764 hbox = gtk_hbox_new(TRUE, 4);
1765 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1766 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1768 frame = gtk_frame_new("Type");
1769 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1770 box = gtk_vbox_new(FALSE, 10);
1771 gtk_container_add(GTK_CONTAINER(frame), box);
1773 hbox = gtk_hbox_new(TRUE, 4);
1774 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1776 cw.combo = gtk_combo_box_new_text();
1777 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1778 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1779 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1780 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1782 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1784 frame = gtk_frame_new("Options");
1785 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1786 box = gtk_vbox_new(FALSE, 10);
1787 gtk_container_add(GTK_CONTAINER(frame), box);
1789 hbox = gtk_hbox_new(TRUE, 4);
1790 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1792 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1793 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1794 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.");
1795 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1798 * Connect edit signal, so we can show/not-show the auto start button
1800 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1801 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1803 gtk_widget_show_all(dialog);
1805 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1806 gtk_widget_destroy(dialog);
1810 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1811 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1813 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1814 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1815 *type = Fio_client_ipv4;
1816 else if (!strncmp(typeentry, "IPv6", 4))
1817 *type = Fio_client_ipv6;
1819 *type = Fio_client_socket;
1822 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1824 gtk_widget_destroy(dialog);
1828 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1830 struct gfio_client *gc;
1832 gc = malloc(sizeof(*gc));
1833 memset(gc, 0, sizeof(*gc));
1835 gc->client = fio_get_client(client);
1839 client->client_data = gc;
1842 static GtkWidget *new_client_page(struct gui_entry *ge);
1844 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1846 struct gui_entry *ge;
1848 ge = malloc(sizeof(*ge));
1849 memset(ge, 0, sizeof(*ge));
1850 INIT_FLIST_HEAD(&ge->list);
1851 flist_add_tail(&ge->list, &ui->list);
1857 * FIXME: need more handling here
1859 static void ge_destroy(GtkWidget *w, gpointer data)
1861 struct gui_entry *ge = data;
1862 struct gfio_client *gc = ge->client;
1865 fio_put_client(gc->client);
1867 flist_del(&ge->list);
1871 static struct gui_entry *get_new_ge_with_tab(const char *name)
1873 struct gui_entry *ge;
1875 ge = alloc_new_gui_entry(&main_ui);
1877 ge->vbox = new_client_page(ge);
1878 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1880 ge->page_label = gtk_label_new(name);
1881 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1883 gtk_widget_show_all(main_ui.window);
1887 static void file_new(GtkWidget *w, gpointer data)
1889 get_new_ge_with_tab("Untitled");
1893 * Return the 'ge' corresponding to the tab. If the active tab is the
1894 * main tab, open a new tab.
1896 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1898 struct flist_head *entry;
1899 struct gui_entry *ge;
1902 return get_new_ge_with_tab("Untitled");
1904 flist_for_each(entry, &main_ui.list) {
1905 ge = flist_entry(entry, struct gui_entry, list);
1906 if (ge->page_num == cur_page)
1913 static void file_open(GtkWidget *w, gpointer data)
1915 struct gui *ui = data;
1917 GSList *filenames, *fn_glist;
1918 GtkFileFilter *filter;
1920 int port, type, server_start;
1921 struct gui_entry *ge;
1925 * Creates new tab if current tab is the main window, or the
1926 * current tab already has a client.
1928 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1929 ge = get_ge_from_page(cur_page);
1931 ge = get_new_ge_with_tab("Untitled");
1933 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1935 dialog = gtk_file_chooser_dialog_new("Open File",
1936 GTK_WINDOW(ui->window),
1937 GTK_FILE_CHOOSER_ACTION_OPEN,
1938 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1939 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1941 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1943 filter = gtk_file_filter_new();
1944 gtk_file_filter_add_pattern(filter, "*.fio");
1945 gtk_file_filter_add_pattern(filter, "*.job");
1946 gtk_file_filter_add_pattern(filter, "*.ini");
1947 gtk_file_filter_add_mime_type(filter, "text/fio");
1948 gtk_file_filter_set_name(filter, "Fio job file");
1949 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1951 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1952 gtk_widget_destroy(dialog);
1956 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1958 gtk_widget_destroy(dialog);
1960 if (get_connection_details(&host, &port, &type, &server_start))
1963 filenames = fn_glist;
1964 while (filenames != NULL) {
1965 struct fio_client *client;
1967 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1968 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1971 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1975 error = g_error_new(g_quark_from_string("fio"), 1,
1976 "Failed to add client %s", host);
1977 report_error(error);
1978 g_error_free(error);
1980 gfio_client_added(ge, client);
1982 g_free(filenames->data);
1983 filenames = g_slist_next(filenames);
1988 gfio_start_server();
1990 g_slist_free(fn_glist);
1993 static void file_save(GtkWidget *w, gpointer data)
1995 struct gui *ui = data;
1998 dialog = gtk_file_chooser_dialog_new("Save File",
1999 GTK_WINDOW(ui->window),
2000 GTK_FILE_CHOOSER_ACTION_SAVE,
2001 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2002 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2005 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2006 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2008 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2011 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2012 // save_job_file(filename);
2015 gtk_widget_destroy(dialog);
2018 static void view_log_destroy(GtkWidget *w, gpointer data)
2020 struct gui *ui = (struct gui *) data;
2022 gtk_widget_ref(ui->log_tree);
2023 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2024 gtk_widget_destroy(w);
2025 ui->log_view = NULL;
2028 static void view_log(GtkWidget *w, gpointer data)
2030 GtkWidget *win, *scroll, *vbox, *box;
2031 struct gui *ui = (struct gui *) data;
2036 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2037 gtk_window_set_title(GTK_WINDOW(win), "Log");
2038 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2040 scroll = gtk_scrolled_window_new(NULL, NULL);
2042 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2044 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2046 box = gtk_hbox_new(TRUE, 0);
2047 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2048 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2049 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2051 vbox = gtk_vbox_new(TRUE, 5);
2052 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2054 gtk_container_add(GTK_CONTAINER(win), vbox);
2055 gtk_widget_show_all(win);
2058 static void __update_graph_limits(struct gfio_graphs *g)
2060 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2061 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2064 static void update_graph_limits(void)
2066 struct flist_head *entry;
2067 struct gui_entry *ge;
2069 __update_graph_limits(&main_ui.graphs);
2071 flist_for_each(entry, &main_ui.list) {
2072 ge = flist_entry(entry, struct gui_entry, list);
2073 __update_graph_limits(&ge->graphs);
2077 static void preferences(GtkWidget *w, gpointer data)
2079 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2080 GtkWidget *hbox, *spin, *entry, *spin_int;
2083 dialog = gtk_dialog_new_with_buttons("Preferences",
2084 GTK_WINDOW(main_ui.window),
2085 GTK_DIALOG_DESTROY_WITH_PARENT,
2086 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2087 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2090 frame = gtk_frame_new("Graphing");
2091 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2092 vbox = gtk_vbox_new(FALSE, 6);
2093 gtk_container_add(GTK_CONTAINER(frame), vbox);
2095 hbox = gtk_hbox_new(FALSE, 5);
2096 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2097 entry = gtk_label_new("Font face to use for graph labels");
2098 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2100 font = gtk_font_button_new();
2101 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2103 box = gtk_vbox_new(FALSE, 6);
2104 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2106 hbox = gtk_hbox_new(FALSE, 5);
2107 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2108 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2109 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2111 spin = create_spinbutton(hbox, 10, 1000000, 100);
2113 box = gtk_vbox_new(FALSE, 6);
2114 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2116 hbox = gtk_hbox_new(FALSE, 5);
2117 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2118 entry = gtk_label_new("Client ETA request interval (msec)");
2119 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2121 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2122 frame = gtk_frame_new("Debug logging");
2123 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2124 vbox = gtk_vbox_new(FALSE, 6);
2125 gtk_container_add(GTK_CONTAINER(frame), vbox);
2127 box = gtk_hbox_new(FALSE, 6);
2128 gtk_container_add(GTK_CONTAINER(vbox), box);
2130 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2132 for (i = 0; i < FD_DEBUG_MAX; i++) {
2134 box = gtk_hbox_new(FALSE, 6);
2135 gtk_container_add(GTK_CONTAINER(vbox), box);
2139 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2140 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2141 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2144 gtk_widget_show_all(dialog);
2146 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2147 gtk_widget_destroy(dialog);
2151 for (i = 0; i < FD_DEBUG_MAX; i++) {
2154 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2156 fio_debug |= (1UL << i);
2159 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2160 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2161 update_graph_limits();
2162 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2164 gtk_widget_destroy(dialog);
2167 static void about_dialog(GtkWidget *w, gpointer data)
2169 const char *authors[] = {
2170 "Jens Axboe <axboe@kernel.dk>",
2171 "Stephen Carmeron <stephenmcameron@gmail.com>",
2174 const char *license[] = {
2175 "Fio is free software; you can redistribute it and/or modify "
2176 "it under the terms of the GNU General Public License as published by "
2177 "the Free Software Foundation; either version 2 of the License, or "
2178 "(at your option) any later version.\n",
2179 "Fio is distributed in the hope that it will be useful, "
2180 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2181 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2182 "GNU General Public License for more details.\n",
2183 "You should have received a copy of the GNU General Public License "
2184 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2185 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2187 char *license_trans;
2189 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2190 license[2], "\n", NULL);
2192 gtk_show_about_dialog(NULL,
2193 "program-name", "gfio",
2194 "comments", "Gtk2 UI for fio",
2195 "license", license_trans,
2196 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2198 "version", fio_version_string,
2199 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2200 "logo-icon-name", "fio",
2202 "wrap-license", TRUE,
2205 g_free(license_trans);
2208 static GtkActionEntry menu_items[] = {
2209 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2210 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2211 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2212 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2213 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2214 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2215 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2216 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2217 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2218 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2220 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2222 static const gchar *ui_string = " \
2224 <menubar name=\"MainMenu\"> \
2225 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2226 <menuitem name=\"New\" action=\"NewFile\" /> \
2227 <separator name=\"Separator1\"/> \
2228 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2229 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2230 <separator name=\"Separator2\"/> \
2231 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2232 <separator name=\"Separator3\"/> \
2233 <menuitem name=\"Quit\" action=\"Quit\" /> \
2235 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2236 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2238 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2239 <menuitem name=\"About\" action=\"About\" /> \
2245 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2248 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2251 action_group = gtk_action_group_new("Menu");
2252 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2254 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2255 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2257 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2258 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2261 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2262 GtkWidget *vbox, GtkUIManager *ui_manager)
2264 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2267 static GtkWidget *new_client_page(struct gui_entry *ge)
2269 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2272 main_vbox = gtk_vbox_new(FALSE, 3);
2274 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2275 ge->topvbox = gtk_vbox_new(FALSE, 3);
2276 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2277 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2279 probe = gtk_frame_new("Job");
2280 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2281 probe_frame = gtk_vbox_new(FALSE, 3);
2282 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2284 probe_box = gtk_hbox_new(FALSE, 3);
2285 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2286 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2287 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2288 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2289 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2291 probe_box = gtk_hbox_new(FALSE, 3);
2292 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2294 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2295 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2296 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2297 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2298 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2299 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2301 probe_box = gtk_hbox_new(FALSE, 3);
2302 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2303 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2304 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2305 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2306 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2309 * Only add this if we have a commit rate
2312 probe_box = gtk_hbox_new(FALSE, 3);
2313 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2315 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2316 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2318 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2319 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2323 * Set up a drawing area and IOPS and bandwidth graphs
2325 gdk_color_parse("white", &white);
2326 ge->graphs.drawing_area = gtk_drawing_area_new();
2327 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2328 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2329 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2330 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2331 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2332 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2333 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2334 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2335 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2336 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2337 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2338 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2339 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2340 ge->graphs.drawing_area);
2341 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2344 setup_graphs(&ge->graphs);
2347 * Set up alignments for widgets at the bottom of ui,
2348 * align bottom left, expand horizontally but not vertically
2350 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2351 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2352 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2353 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2356 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2359 * Set up thread status progress bar
2361 ge->thread_status_pb = gtk_progress_bar_new();
2362 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2363 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2364 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2370 static GtkWidget *new_main_page(struct gui *ui)
2372 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2375 main_vbox = gtk_vbox_new(FALSE, 3);
2378 * Set up alignments for widgets at the top of ui,
2379 * align top left, expand horizontally but not vertically
2381 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2382 ui->topvbox = gtk_vbox_new(FALSE, 0);
2383 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2384 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2386 probe = gtk_frame_new("Run statistics");
2387 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2388 probe_frame = gtk_vbox_new(FALSE, 3);
2389 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2391 probe_box = gtk_hbox_new(FALSE, 3);
2392 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2393 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2394 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2395 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2396 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2397 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2400 * Only add this if we have a commit rate
2403 probe_box = gtk_hbox_new(FALSE, 3);
2404 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2406 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2407 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2409 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2410 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2414 * Set up a drawing area and IOPS and bandwidth graphs
2416 gdk_color_parse("white", &white);
2417 ui->graphs.drawing_area = gtk_drawing_area_new();
2418 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2419 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2420 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2421 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2422 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2423 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2424 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2425 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2426 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2427 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2428 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2429 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2430 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2431 ui->graphs.drawing_area);
2432 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2435 setup_graphs(&ui->graphs);
2438 * Set up alignments for widgets at the bottom of ui,
2439 * align bottom left, expand horizontally but not vertically
2441 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2442 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2443 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2444 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2448 * Set up thread status progress bar
2450 ui->thread_status_pb = gtk_progress_bar_new();
2451 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2452 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2453 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2458 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2459 guint page, gpointer data)
2465 static void init_ui(int *argc, char **argv[], struct gui *ui)
2467 GtkSettings *settings;
2468 GtkUIManager *uimanager;
2469 GtkWidget *menu, *vbox;
2471 /* Magical g*thread incantation, you just need this thread stuff.
2472 * Without it, the update that happens in gfio_update_thread_status
2473 * doesn't really happen in a timely fashion, you need expose events
2475 if (!g_thread_supported())
2476 g_thread_init(NULL);
2479 gtk_init(argc, argv);
2480 settings = gtk_settings_get_default();
2481 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2484 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2485 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2486 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2488 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2489 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2491 ui->vbox = gtk_vbox_new(FALSE, 0);
2492 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2494 uimanager = gtk_ui_manager_new();
2495 menu = get_menubar_menu(ui->window, uimanager, ui);
2496 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2498 ui->notebook = gtk_notebook_new();
2499 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2500 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2501 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2502 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2504 vbox = new_main_page(ui);
2506 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2508 gfio_ui_setup_log(ui);
2510 gtk_widget_show_all(ui->window);
2513 int main(int argc, char *argv[], char *envp[])
2515 if (initialize_fio(envp))
2517 if (fio_init_options())
2520 memset(&main_ui, 0, sizeof(main_ui));
2521 INIT_FLIST_HEAD(&main_ui.list);
2523 init_ui(&argc, &argv, &main_ui);
2525 gdk_threads_enter();
2527 gdk_threads_leave();