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 struct graph *iops_graph;
93 struct graph *bandwidth_graph;
97 * Main window widgets and data
104 GtkWidget *bottomalign;
105 GtkWidget *thread_status_pb;
106 GtkWidget *buttonbox;
107 GtkWidget *scrolled_window;
109 GtkWidget *error_info_bar;
110 GtkWidget *error_label;
111 GtkListStore *log_model;
114 struct gfio_graphs graphs;
115 struct probe_widget probe;
116 struct eta_widget eta;
122 struct flist_head list;
129 struct flist_head list;
135 GtkWidget *bottomalign;
136 GtkWidget *thread_status_pb;
137 GtkWidget *buttonbox;
138 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
139 GtkWidget *scrolled_window;
141 GtkWidget *error_info_bar;
142 GtkWidget *error_label;
143 GtkWidget *results_notebook;
144 GtkWidget *results_window;
145 GtkListStore *log_model;
148 struct gfio_graphs graphs;
149 struct probe_widget probe;
150 struct eta_widget eta;
151 GtkWidget *page_label;
155 struct gfio_client *client;
161 struct gui_entry *ge;
162 struct fio_client *client;
163 GtkWidget *results_widget;
164 GtkWidget *disk_util_frame;
165 GtkWidget *err_entry;
166 unsigned int job_added;
167 struct thread_options o;
170 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
171 static void gfio_update_thread_status_all(char *status_message, double perc);
172 void report_error(GError *error);
174 static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
176 switch (power_of_ten) {
177 case 9: graph_y_title(g, "Billions of IOs / sec");
179 case 6: graph_y_title(g, "Millions of IOs / sec");
181 case 3: graph_y_title(g, "Thousands of IOs / sec");
184 default: graph_y_title(g, "IOs / sec");
189 static struct graph *setup_iops_graph(void)
193 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
194 graph_title(g, "IOPS");
195 graph_x_title(g, "Time (secs)");
196 graph_y_title(g, "IOs / sec");
197 graph_add_label(g, "Read IOPS");
198 graph_add_label(g, "Write IOPS");
199 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
200 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
201 line_graph_set_data_count_limit(g, gfio_graph_limit);
202 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
203 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
207 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
209 switch (power_of_ten) {
210 case 9: graph_y_title(g, "Petabytes / sec");
212 case 6: graph_y_title(g, "Gigabytes / sec");
214 case 3: graph_y_title(g, "Megabytes / sec");
217 default: graph_y_title(g, "Kilobytes / sec");
222 static struct graph *setup_bandwidth_graph(void)
226 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
227 graph_title(g, "Bandwidth");
228 graph_x_title(g, "Time (secs)");
229 graph_y_title(g, "Kbytes / sec");
230 graph_add_label(g, "Read Bandwidth");
231 graph_add_label(g, "Write Bandwidth");
232 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
233 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
234 line_graph_set_data_count_limit(g, 100);
235 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
236 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
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 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1134 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1135 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1136 graph_set_position(g->bandwidth_graph, 0, 0);
1140 static void draw_graph(struct graph *g, cairo_t *cr)
1142 line_graph_draw(g, cr);
1146 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1148 struct gfio_graphs *g = p;
1151 cr = gdk_cairo_create(w->window);
1152 cairo_set_source_rgb(cr, 0, 0, 0);
1153 draw_graph(g->iops_graph, cr);
1154 draw_graph(g->bandwidth_graph, cr);
1161 * Client specific ETA
1163 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1165 struct gfio_client *gc = client->client_data;
1166 struct gui_entry *ge = gc->ge;
1167 static int eta_good;
1174 gdk_threads_enter();
1179 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1180 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1181 eta_to_str(eta_str, je->eta_sec);
1184 sprintf(tmp, "%u", je->nr_running);
1185 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1186 sprintf(tmp, "%u", je->files_open);
1187 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1190 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1191 if (je->m_rate || je->t_rate) {
1194 mr = num2str(je->m_rate, 4, 0, i2p);
1195 tr = num2str(je->t_rate, 4, 0, i2p);
1196 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1197 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1200 } else if (je->m_iops || je->t_iops)
1201 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1203 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1204 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1205 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1206 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1209 if (je->eta_sec != INT_MAX && je->nr_running) {
1213 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1214 strcpy(output, "-.-% done");
1218 sprintf(output, "%3.1f%% done", perc);
1221 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1222 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1224 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1225 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1227 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1228 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1229 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1230 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1232 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1233 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1234 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1235 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1244 char *dst = output + strlen(output);
1246 sprintf(dst, " - %s", eta_str);
1249 gfio_update_thread_status(ge, output, perc);
1250 gdk_threads_leave();
1254 * Update ETA in main window for all clients
1256 static void gfio_update_all_eta(struct jobs_eta *je)
1258 struct gui *ui = &main_ui;
1259 static int eta_good;
1265 gdk_threads_enter();
1270 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1271 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1272 eta_to_str(eta_str, je->eta_sec);
1276 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1277 if (je->m_rate || je->t_rate) {
1280 mr = num2str(je->m_rate, 4, 0, i2p);
1281 tr = num2str(je->t_rate, 4, 0, i2p);
1282 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1283 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1286 } else if (je->m_iops || je->t_iops)
1287 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1289 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1290 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1291 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1292 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1295 entry_set_int_value(ui->eta.jobs, je->nr_running);
1297 if (je->eta_sec != INT_MAX && je->nr_running) {
1301 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1302 strcpy(output, "-.-% done");
1306 sprintf(output, "%3.1f%% done", perc);
1309 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1310 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1312 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1313 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1315 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1316 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1317 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1318 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1320 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1321 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1322 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1323 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1332 char *dst = output + strlen(output);
1334 sprintf(dst, " - %s", eta_str);
1337 gfio_update_thread_status_all(output, perc);
1338 gdk_threads_leave();
1341 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1343 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1344 struct gfio_client *gc = client->client_data;
1345 struct gui_entry *ge = gc->ge;
1346 const char *os, *arch;
1349 os = fio_get_os_string(probe->os);
1353 arch = fio_get_arch_string(probe->arch);
1358 client->name = strdup((char *) probe->hostname);
1360 gdk_threads_enter();
1362 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1363 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1364 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1365 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1366 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1368 gfio_set_connected(ge, 1);
1370 gdk_threads_leave();
1373 static void gfio_update_thread_status(struct gui_entry *ge,
1374 char *status_message, double perc)
1376 static char message[100];
1377 const char *m = message;
1379 strncpy(message, status_message, sizeof(message) - 1);
1380 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1381 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1382 gtk_widget_queue_draw(main_ui.window);
1385 static void gfio_update_thread_status_all(char *status_message, double perc)
1387 struct gui *ui = &main_ui;
1388 static char message[100];
1389 const char *m = message;
1391 strncpy(message, status_message, sizeof(message) - 1);
1392 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1393 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1394 gtk_widget_queue_draw(ui->window);
1397 static void gfio_quit_op(struct fio_client *client)
1399 struct gfio_client *gc = client->client_data;
1401 gdk_threads_enter();
1402 gfio_set_connected(gc->ge, 0);
1403 gdk_threads_leave();
1406 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1408 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1409 struct gfio_client *gc = client->client_data;
1410 struct thread_options *o = &gc->o;
1411 struct gui_entry *ge = gc->ge;
1414 convert_thread_options_to_cpu(o, &p->top);
1416 gdk_threads_enter();
1418 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1420 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1421 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1423 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1424 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1426 sprintf(tmp, "%u", o->iodepth);
1427 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1431 gdk_threads_leave();
1434 static void gfio_client_timed_out(struct fio_client *client)
1436 struct gfio_client *gc = client->client_data;
1437 GtkWidget *dialog, *label, *content;
1440 gdk_threads_enter();
1442 gfio_set_connected(gc->ge, 0);
1443 clear_ge_ui_info(gc->ge);
1445 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1447 dialog = gtk_dialog_new_with_buttons("Timed out!",
1448 GTK_WINDOW(main_ui.window),
1449 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1450 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1452 /* gtk_dialog_get_content_area() is 2.14 and newer */
1453 content = GTK_DIALOG(dialog)->vbox;
1455 label = gtk_label_new((const gchar *) buf);
1456 gtk_container_add(GTK_CONTAINER(content), label);
1457 gtk_widget_show_all(dialog);
1458 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1460 gtk_dialog_run(GTK_DIALOG(dialog));
1461 gtk_widget_destroy(dialog);
1463 gdk_threads_leave();
1466 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1468 struct gfio_client *gc = client->client_data;
1470 gdk_threads_enter();
1472 gfio_set_connected(gc->ge, 0);
1475 entry_set_int_value(gc->err_entry, client->error);
1477 gdk_threads_leave();
1480 struct client_ops gfio_client_ops = {
1481 .text_op = gfio_text_op,
1482 .disk_util = gfio_disk_util_op,
1483 .thread_status = gfio_thread_status_op,
1484 .group_stats = gfio_group_stats_op,
1485 .jobs_eta = gfio_update_client_eta,
1486 .eta = gfio_update_all_eta,
1487 .probe = gfio_probe_op,
1488 .quit = gfio_quit_op,
1489 .add_job = gfio_add_job_op,
1490 .timed_out = gfio_client_timed_out,
1491 .stop = gfio_client_stop,
1492 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1493 .stay_connected = 1,
1496 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1497 __attribute__((unused)) gpointer data)
1502 static void *job_thread(void *arg)
1504 struct gui *ui = arg;
1506 ui->handler_running = 1;
1507 fio_handle_clients(&gfio_client_ops);
1508 ui->handler_running = 0;
1512 static int send_job_files(struct gui_entry *ge)
1514 struct gfio_client *gc = ge->client;
1517 for (i = 0; i < ge->nr_job_files; i++) {
1518 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1522 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1523 report_error(error);
1524 g_error_free(error);
1529 free(ge->job_files[i]);
1530 ge->job_files[i] = NULL;
1532 while (i < ge->nr_job_files) {
1533 free(ge->job_files[i]);
1534 ge->job_files[i] = NULL;
1541 static void *server_thread(void *arg)
1544 gfio_server_running = 1;
1545 fio_start_server(NULL);
1546 gfio_server_running = 0;
1550 static void gfio_start_server(void)
1552 struct gui *ui = &main_ui;
1554 if (!gfio_server_running) {
1555 gfio_server_running = 1;
1556 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1557 pthread_detach(ui->server_t);
1561 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1564 struct gui_entry *ge = data;
1565 struct gfio_client *gc = ge->client;
1567 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1568 fio_start_client(gc->client);
1571 static void file_open(GtkWidget *w, gpointer data);
1573 static void connect_clicked(GtkWidget *widget, gpointer data)
1575 struct gui_entry *ge = data;
1576 struct gfio_client *gc = ge->client;
1578 if (!ge->connected) {
1581 if (!ge->nr_job_files)
1582 file_open(widget, data);
1583 if (!ge->nr_job_files)
1586 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1587 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1588 ret = fio_client_connect(gc->client);
1590 if (!ge->ui->handler_running)
1591 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1592 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1593 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1597 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1598 report_error(error);
1599 g_error_free(error);
1602 fio_client_terminate(gc->client);
1603 gfio_set_connected(ge, 0);
1604 clear_ge_ui_info(ge);
1608 static void send_clicked(GtkWidget *widget, gpointer data)
1610 struct gui_entry *ge = data;
1612 if (send_job_files(ge)) {
1615 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);
1616 report_error(error);
1617 g_error_free(error);
1619 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1622 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1623 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1626 static GtkWidget *add_button(GtkWidget *buttonbox,
1627 struct button_spec *buttonspec, gpointer data)
1629 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1631 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1632 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1633 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1634 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1639 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1644 for (i = 0; i < nbuttons; i++)
1645 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1648 static void on_info_bar_response(GtkWidget *widget, gint response,
1651 struct gui *ui = &main_ui;
1653 if (response == GTK_RESPONSE_OK) {
1654 gtk_widget_destroy(widget);
1655 ui->error_info_bar = NULL;
1659 void report_error(GError *error)
1661 struct gui *ui = &main_ui;
1663 if (ui->error_info_bar == NULL) {
1664 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1667 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1668 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1671 ui->error_label = gtk_label_new(error->message);
1672 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1673 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1675 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1676 gtk_widget_show_all(ui->vbox);
1679 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1680 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1684 struct connection_widgets
1691 static void hostname_cb(GtkEntry *entry, gpointer data)
1693 struct connection_widgets *cw = data;
1694 int uses_net = 0, is_localhost = 0;
1699 * Check whether to display the 'auto start backend' box
1700 * or not. Show it if we are a localhost and using network,
1701 * or using a socket.
1703 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1704 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1709 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1710 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1711 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1712 !strcmp(text, "ip6-loopback"))
1716 if (!uses_net || is_localhost) {
1717 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1718 gtk_widget_set_sensitive(cw->button, 1);
1720 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1721 gtk_widget_set_sensitive(cw->button, 0);
1725 static int get_connection_details(char **host, int *port, int *type,
1728 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1729 struct connection_widgets cw;
1732 dialog = gtk_dialog_new_with_buttons("Connection details",
1733 GTK_WINDOW(main_ui.window),
1734 GTK_DIALOG_DESTROY_WITH_PARENT,
1735 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1736 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1738 frame = gtk_frame_new("Hostname / socket name");
1739 /* gtk_dialog_get_content_area() is 2.14 and newer */
1740 vbox = GTK_DIALOG(dialog)->vbox;
1741 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1743 box = gtk_vbox_new(FALSE, 6);
1744 gtk_container_add(GTK_CONTAINER(frame), box);
1746 hbox = gtk_hbox_new(TRUE, 10);
1747 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1748 cw.hentry = gtk_entry_new();
1749 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1750 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1752 frame = gtk_frame_new("Port");
1753 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1754 box = gtk_vbox_new(FALSE, 10);
1755 gtk_container_add(GTK_CONTAINER(frame), box);
1757 hbox = gtk_hbox_new(TRUE, 4);
1758 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1759 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1761 frame = gtk_frame_new("Type");
1762 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1763 box = gtk_vbox_new(FALSE, 10);
1764 gtk_container_add(GTK_CONTAINER(frame), box);
1766 hbox = gtk_hbox_new(TRUE, 4);
1767 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1769 cw.combo = gtk_combo_box_new_text();
1770 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1771 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1772 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1773 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1775 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1777 frame = gtk_frame_new("Options");
1778 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1779 box = gtk_vbox_new(FALSE, 10);
1780 gtk_container_add(GTK_CONTAINER(frame), box);
1782 hbox = gtk_hbox_new(TRUE, 4);
1783 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1785 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1786 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1787 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.");
1788 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1791 * Connect edit signal, so we can show/not-show the auto start button
1793 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1794 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1796 gtk_widget_show_all(dialog);
1798 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1799 gtk_widget_destroy(dialog);
1803 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1804 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1806 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1807 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1808 *type = Fio_client_ipv4;
1809 else if (!strncmp(typeentry, "IPv6", 4))
1810 *type = Fio_client_ipv6;
1812 *type = Fio_client_socket;
1815 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1817 gtk_widget_destroy(dialog);
1821 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1823 struct gfio_client *gc;
1825 gc = malloc(sizeof(*gc));
1826 memset(gc, 0, sizeof(*gc));
1828 gc->client = fio_get_client(client);
1832 client->client_data = gc;
1835 static GtkWidget *new_client_page(struct gui_entry *ge);
1837 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1839 struct gui_entry *ge;
1841 ge = malloc(sizeof(*ge));
1842 memset(ge, 0, sizeof(*ge));
1843 INIT_FLIST_HEAD(&ge->list);
1844 flist_add_tail(&ge->list, &ui->list);
1850 * FIXME: need more handling here
1852 static void ge_destroy(GtkWidget *w, gpointer data)
1854 struct gui_entry *ge = data;
1855 struct gfio_client *gc = ge->client;
1858 fio_put_client(gc->client);
1860 flist_del(&ge->list);
1864 static struct gui_entry *get_new_ge_with_tab(const char *name)
1866 struct gui_entry *ge;
1868 ge = alloc_new_gui_entry(&main_ui);
1870 ge->vbox = new_client_page(ge);
1871 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1873 ge->page_label = gtk_label_new(name);
1874 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1876 gtk_widget_show_all(main_ui.window);
1880 static void file_new(GtkWidget *w, gpointer data)
1882 get_new_ge_with_tab("Untitled");
1886 * Return the 'ge' corresponding to the tab. If the active tab is the
1887 * main tab, open a new tab.
1889 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1891 struct flist_head *entry;
1892 struct gui_entry *ge;
1895 return get_new_ge_with_tab("Untitled");
1897 flist_for_each(entry, &main_ui.list) {
1898 ge = flist_entry(entry, struct gui_entry, list);
1899 if (ge->page_num == cur_page)
1906 static void file_open(GtkWidget *w, gpointer data)
1908 struct gui *ui = data;
1910 GSList *filenames, *fn_glist;
1911 GtkFileFilter *filter;
1913 int port, type, server_start;
1914 struct gui_entry *ge;
1918 * Creates new tab if current tab is the main window, or the
1919 * current tab already has a client.
1921 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1922 ge = get_ge_from_page(cur_page);
1924 ge = get_new_ge_with_tab("Untitled");
1926 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1928 dialog = gtk_file_chooser_dialog_new("Open File",
1929 GTK_WINDOW(ui->window),
1930 GTK_FILE_CHOOSER_ACTION_OPEN,
1931 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1932 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1934 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1936 filter = gtk_file_filter_new();
1937 gtk_file_filter_add_pattern(filter, "*.fio");
1938 gtk_file_filter_add_pattern(filter, "*.job");
1939 gtk_file_filter_add_pattern(filter, "*.ini");
1940 gtk_file_filter_add_mime_type(filter, "text/fio");
1941 gtk_file_filter_set_name(filter, "Fio job file");
1942 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1944 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1945 gtk_widget_destroy(dialog);
1949 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1951 gtk_widget_destroy(dialog);
1953 if (get_connection_details(&host, &port, &type, &server_start))
1956 filenames = fn_glist;
1957 while (filenames != NULL) {
1958 struct fio_client *client;
1960 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1961 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1964 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1968 error = g_error_new(g_quark_from_string("fio"), 1,
1969 "Failed to add client %s", host);
1970 report_error(error);
1971 g_error_free(error);
1973 gfio_client_added(ge, client);
1975 g_free(filenames->data);
1976 filenames = g_slist_next(filenames);
1981 gfio_start_server();
1983 g_slist_free(fn_glist);
1986 static void file_save(GtkWidget *w, gpointer data)
1988 struct gui *ui = data;
1991 dialog = gtk_file_chooser_dialog_new("Save File",
1992 GTK_WINDOW(ui->window),
1993 GTK_FILE_CHOOSER_ACTION_SAVE,
1994 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1995 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1998 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1999 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2001 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2004 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2005 // save_job_file(filename);
2008 gtk_widget_destroy(dialog);
2011 static void view_log_destroy(GtkWidget *w, gpointer data)
2013 struct gui *ui = (struct gui *) data;
2015 gtk_widget_ref(ui->log_tree);
2016 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2017 gtk_widget_destroy(w);
2018 ui->log_view = NULL;
2021 static void view_log(GtkWidget *w, gpointer data)
2023 GtkWidget *win, *scroll, *vbox, *box;
2024 struct gui *ui = (struct gui *) data;
2029 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2030 gtk_window_set_title(GTK_WINDOW(win), "Log");
2031 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2033 scroll = gtk_scrolled_window_new(NULL, NULL);
2035 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2037 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2039 box = gtk_hbox_new(TRUE, 0);
2040 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2041 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2042 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2044 vbox = gtk_vbox_new(TRUE, 5);
2045 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2047 gtk_container_add(GTK_CONTAINER(win), vbox);
2048 gtk_widget_show_all(win);
2051 static void __update_graph_limits(struct gfio_graphs *g)
2053 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2054 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2057 static void update_graph_limits(void)
2059 struct flist_head *entry;
2060 struct gui_entry *ge;
2062 __update_graph_limits(&main_ui.graphs);
2064 flist_for_each(entry, &main_ui.list) {
2065 ge = flist_entry(entry, struct gui_entry, list);
2066 __update_graph_limits(&ge->graphs);
2070 static void preferences(GtkWidget *w, gpointer data)
2072 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2073 GtkWidget *hbox, *spin, *entry, *spin_int;
2076 dialog = gtk_dialog_new_with_buttons("Preferences",
2077 GTK_WINDOW(main_ui.window),
2078 GTK_DIALOG_DESTROY_WITH_PARENT,
2079 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2080 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2083 frame = gtk_frame_new("Graphing");
2084 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2085 vbox = gtk_vbox_new(FALSE, 6);
2086 gtk_container_add(GTK_CONTAINER(frame), vbox);
2088 hbox = gtk_hbox_new(FALSE, 5);
2089 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2090 entry = gtk_label_new("Font face to use for graph labels");
2091 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2093 font = gtk_font_button_new();
2094 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2096 box = gtk_vbox_new(FALSE, 6);
2097 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2099 hbox = gtk_hbox_new(FALSE, 5);
2100 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2101 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2102 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2104 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2106 box = gtk_vbox_new(FALSE, 6);
2107 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2109 hbox = gtk_hbox_new(FALSE, 5);
2110 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2111 entry = gtk_label_new("Client ETA request interval (msec)");
2112 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2114 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2115 frame = gtk_frame_new("Debug logging");
2116 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2117 vbox = gtk_vbox_new(FALSE, 6);
2118 gtk_container_add(GTK_CONTAINER(frame), vbox);
2120 box = gtk_hbox_new(FALSE, 6);
2121 gtk_container_add(GTK_CONTAINER(vbox), box);
2123 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2125 for (i = 0; i < FD_DEBUG_MAX; i++) {
2127 box = gtk_hbox_new(FALSE, 6);
2128 gtk_container_add(GTK_CONTAINER(vbox), box);
2132 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2133 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2134 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2137 gtk_widget_show_all(dialog);
2139 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2140 gtk_widget_destroy(dialog);
2144 for (i = 0; i < FD_DEBUG_MAX; i++) {
2147 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2149 fio_debug |= (1UL << i);
2152 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2153 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2154 update_graph_limits();
2155 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2157 gtk_widget_destroy(dialog);
2160 static void about_dialog(GtkWidget *w, gpointer data)
2162 const char *authors[] = {
2163 "Jens Axboe <axboe@kernel.dk>",
2164 "Stephen Carmeron <stephenmcameron@gmail.com>",
2167 const char *license[] = {
2168 "Fio is free software; you can redistribute it and/or modify "
2169 "it under the terms of the GNU General Public License as published by "
2170 "the Free Software Foundation; either version 2 of the License, or "
2171 "(at your option) any later version.\n",
2172 "Fio is distributed in the hope that it will be useful, "
2173 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2174 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2175 "GNU General Public License for more details.\n",
2176 "You should have received a copy of the GNU General Public License "
2177 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2178 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2180 char *license_trans;
2182 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2183 license[2], "\n", NULL);
2185 gtk_show_about_dialog(NULL,
2186 "program-name", "gfio",
2187 "comments", "Gtk2 UI for fio",
2188 "license", license_trans,
2189 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2191 "version", fio_version_string,
2192 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2193 "logo-icon-name", "fio",
2195 "wrap-license", TRUE,
2198 g_free(license_trans);
2201 static GtkActionEntry menu_items[] = {
2202 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2203 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2204 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2205 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2206 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2207 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2208 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2209 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2210 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2211 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2213 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2215 static const gchar *ui_string = " \
2217 <menubar name=\"MainMenu\"> \
2218 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2219 <menuitem name=\"New\" action=\"NewFile\" /> \
2220 <separator name=\"Separator1\"/> \
2221 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2222 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2223 <separator name=\"Separator2\"/> \
2224 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2225 <separator name=\"Separator3\"/> \
2226 <menuitem name=\"Quit\" action=\"Quit\" /> \
2228 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2229 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2231 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2232 <menuitem name=\"About\" action=\"About\" /> \
2238 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2241 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2244 action_group = gtk_action_group_new("Menu");
2245 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2247 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2248 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2250 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2251 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2254 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2255 GtkWidget *vbox, GtkUIManager *ui_manager)
2257 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2260 static GtkWidget *new_client_page(struct gui_entry *ge)
2262 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2265 main_vbox = gtk_vbox_new(FALSE, 3);
2267 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2268 ge->topvbox = gtk_vbox_new(FALSE, 3);
2269 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2270 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2272 probe = gtk_frame_new("Job");
2273 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2274 probe_frame = gtk_vbox_new(FALSE, 3);
2275 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2277 probe_box = gtk_hbox_new(FALSE, 3);
2278 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2279 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2280 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2281 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2282 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2284 probe_box = gtk_hbox_new(FALSE, 3);
2285 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2287 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2288 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2289 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2290 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2291 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2292 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2294 probe_box = gtk_hbox_new(FALSE, 3);
2295 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2296 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2297 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2298 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2299 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2302 * Only add this if we have a commit rate
2305 probe_box = gtk_hbox_new(FALSE, 3);
2306 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2308 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2309 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2311 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2312 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2316 * Set up a drawing area and IOPS and bandwidth graphs
2318 gdk_color_parse("white", &white);
2319 ge->graphs.drawing_area = gtk_drawing_area_new();
2320 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2321 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2322 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2323 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2324 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2325 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2326 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2327 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2328 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2329 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2330 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2331 ge->graphs.drawing_area);
2332 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2335 setup_graphs(&ge->graphs);
2338 * Set up alignments for widgets at the bottom of ui,
2339 * align bottom left, expand horizontally but not vertically
2341 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2342 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2343 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2344 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2347 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2350 * Set up thread status progress bar
2352 ge->thread_status_pb = gtk_progress_bar_new();
2353 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2354 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2355 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2361 static GtkWidget *new_main_page(struct gui *ui)
2363 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2366 main_vbox = gtk_vbox_new(FALSE, 3);
2369 * Set up alignments for widgets at the top of ui,
2370 * align top left, expand horizontally but not vertically
2372 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2373 ui->topvbox = gtk_vbox_new(FALSE, 0);
2374 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2375 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2377 probe = gtk_frame_new("Run statistics");
2378 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2379 probe_frame = gtk_vbox_new(FALSE, 3);
2380 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2382 probe_box = gtk_hbox_new(FALSE, 3);
2383 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2384 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2385 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2386 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2387 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2388 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2391 * Only add this if we have a commit rate
2394 probe_box = gtk_hbox_new(FALSE, 3);
2395 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2397 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2398 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2400 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2401 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2405 * Set up a drawing area and IOPS and bandwidth graphs
2407 gdk_color_parse("white", &white);
2408 ui->graphs.drawing_area = gtk_drawing_area_new();
2409 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2410 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2411 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2412 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2413 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2414 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2415 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2416 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2417 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2418 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2419 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2420 ui->graphs.drawing_area);
2421 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2424 setup_graphs(&ui->graphs);
2427 * Set up alignments for widgets at the bottom of ui,
2428 * align bottom left, expand horizontally but not vertically
2430 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2431 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2432 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2433 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2437 * Set up thread status progress bar
2439 ui->thread_status_pb = gtk_progress_bar_new();
2440 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2441 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2442 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2447 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2448 guint page, gpointer data)
2454 static void init_ui(int *argc, char **argv[], struct gui *ui)
2456 GtkSettings *settings;
2457 GtkUIManager *uimanager;
2458 GtkWidget *menu, *vbox;
2460 /* Magical g*thread incantation, you just need this thread stuff.
2461 * Without it, the update that happens in gfio_update_thread_status
2462 * doesn't really happen in a timely fashion, you need expose events
2464 if (!g_thread_supported())
2465 g_thread_init(NULL);
2468 gtk_init(argc, argv);
2469 settings = gtk_settings_get_default();
2470 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2473 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2474 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2475 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2477 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2478 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2480 ui->vbox = gtk_vbox_new(FALSE, 0);
2481 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2483 uimanager = gtk_ui_manager_new();
2484 menu = get_menubar_menu(ui->window, uimanager, ui);
2485 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2487 ui->notebook = gtk_notebook_new();
2488 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2489 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2490 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2491 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2493 vbox = new_main_page(ui);
2495 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2497 gfio_ui_setup_log(ui);
2499 gtk_widget_show_all(ui->window);
2502 int main(int argc, char *argv[], char *envp[])
2504 if (initialize_fio(envp))
2506 if (fio_init_options())
2509 memset(&main_ui, 0, sizeof(main_ui));
2510 INIT_FLIST_HEAD(&main_ui.list);
2512 init_ui(&argc, &argv, &main_ui);
2514 gdk_threads_enter();
2516 gdk_threads_leave();