2 * gfio - gui front end for fio - the flexible io tester
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
6 * The license below covers all files distributed with fio unless otherwise
7 * noted in the file itself.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 static void gfio_update_thread_status(char *status_message, double perc);
33 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
35 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
37 static void connect_clicked(GtkWidget *widget, gpointer data);
38 static void start_job_clicked(GtkWidget *widget, gpointer data);
40 static struct button_spec {
41 const char *buttontext;
43 const char *tooltiptext;
44 const int start_insensitive;
45 } buttonspeclist[] = {
46 #define CONNECT_BUTTON 0
47 #define START_JOB_BUTTON 1
48 { "Connect", connect_clicked, "Connect to host", 0 },
51 "Send current fio job to fio server to be executed", 1 },
73 GtkWidget *write_iops;
83 GtkWidget *bottomalign;
84 GtkWidget *thread_status_pb;
86 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
87 GtkWidget *scrolled_window;
89 GtkWidget *error_info_bar;
90 GtkWidget *error_label;
91 GtkWidget *results_notebook;
92 GtkWidget *results_window;
93 GtkListStore *log_model;
96 struct probe_widget probe;
97 struct eta_widget eta;
101 struct fio_client *client;
108 GtkWidget *results_widget;
109 GtkWidget *disk_util_frame;
112 static void clear_ui_info(struct gui *ui)
114 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
115 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
116 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
117 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
118 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
119 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
120 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
121 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
122 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
123 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
124 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
125 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
126 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
127 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
130 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
132 GtkWidget *entry, *frame;
134 frame = gtk_frame_new(label);
135 entry = gtk_entry_new();
136 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
137 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
138 gtk_container_add(GTK_CONTAINER(frame), entry);
143 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
145 GtkWidget *label_widget;
148 frame = gtk_frame_new(label);
149 label_widget = gtk_label_new(NULL);
150 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
151 gtk_container_add(GTK_CONTAINER(frame), label_widget);
156 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
158 GtkWidget *button, *box;
160 box = gtk_hbox_new(FALSE, 3);
161 gtk_container_add(GTK_CONTAINER(hbox), box);
163 button = gtk_spin_button_new_with_range(min, max, 1.0);
164 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
166 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
167 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
172 static void gfio_set_connected(struct gui *ui, int connected)
175 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
177 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
178 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
181 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
182 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
186 static void label_set_int_value(GtkWidget *entry, unsigned int val)
190 sprintf(tmp, "%u", val);
191 gtk_label_set_text(GTK_LABEL(entry), tmp);
194 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
198 sprintf(tmp, "%u", val);
199 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
203 #define ALIGN_RIGHT 2
207 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
209 GtkCellRenderer *renderer;
210 GtkTreeViewColumn *col;
211 double xalign = 0.0; /* left as default */
212 PangoAlignment align;
215 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
216 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
218 visible = !(flags & INVISIBLE);
220 renderer = gtk_cell_renderer_text_new();
221 col = gtk_tree_view_column_new();
223 gtk_tree_view_column_set_title(col, title);
224 if (!(flags & UNSORTABLE))
225 gtk_tree_view_column_set_sort_column_id(col, index);
226 gtk_tree_view_column_set_resizable(col, TRUE);
227 gtk_tree_view_column_pack_start(col, renderer, TRUE);
228 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
229 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
231 case PANGO_ALIGN_LEFT:
234 case PANGO_ALIGN_CENTER:
237 case PANGO_ALIGN_RIGHT:
241 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
242 gtk_tree_view_column_set_visible(col, visible);
243 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
247 static void gfio_ui_setup_log(struct gui *ui)
249 GtkTreeSelection *selection;
251 GtkWidget *tree_view;
253 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
255 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
256 gtk_widget_set_can_focus(tree_view, FALSE);
258 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
259 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
261 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
262 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
263 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
264 tree_view_column(tree_view, 3, "Text", ALIGN_RIGHT | UNSORTABLE);
266 ui->log_model = model;
267 ui->log_tree = tree_view;
270 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
276 GType types[FIO_IO_U_LIST_MAX_LEN];
277 GtkWidget *tree_view;
278 GtkTreeSelection *selection;
283 for (i = 0; i < len; i++)
284 types[i] = G_TYPE_INT;
286 model = gtk_list_store_newv(len, types);
288 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
289 gtk_widget_set_can_focus(tree_view, FALSE);
291 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
292 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
294 for (i = 0; i < len; i++) {
297 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
298 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
301 gtk_list_store_append(model, &iter);
303 for (i = 0; i < len; i++) {
305 ovals[i] = (ovals[i] + 999) / 1000;
306 gtk_list_store_set(model, &iter, i, ovals[i], -1);
312 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
315 unsigned int *io_u_plat = ts->io_u_plat[ddir];
316 unsigned long nr = ts->clat_stat[ddir].samples;
317 fio_fp64_t *plist = ts->percentile_list;
318 unsigned int *ovals, len, minv, maxv, scale_down;
320 GtkWidget *tree_view, *frame, *hbox;
323 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
328 * We default to usecs, but if the value range is such that we
329 * should scale down to msecs, do that.
331 if (minv > 2000 && maxv > 99999) {
339 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
341 sprintf(tmp, "Completion percentiles (%s)", base);
342 frame = gtk_frame_new(tmp);
343 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
345 hbox = gtk_hbox_new(FALSE, 3);
346 gtk_container_add(GTK_CONTAINER(frame), hbox);
348 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
354 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
355 unsigned long max, double mean, double dev)
357 const char *base = "(usec)";
358 GtkWidget *hbox, *label, *frame;
362 if (!usec_to_msec(&min, &max, &mean, &dev))
365 minp = num2str(min, 6, 1, 0);
366 maxp = num2str(max, 6, 1, 0);
368 sprintf(tmp, "%s %s", name, base);
369 frame = gtk_frame_new(tmp);
370 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
372 hbox = gtk_hbox_new(FALSE, 3);
373 gtk_container_add(GTK_CONTAINER(frame), hbox);
375 label = new_info_label_in_frame(hbox, "Minimum");
376 gtk_label_set_text(GTK_LABEL(label), minp);
377 label = new_info_label_in_frame(hbox, "Maximum");
378 gtk_label_set_text(GTK_LABEL(label), maxp);
379 label = new_info_label_in_frame(hbox, "Average");
380 sprintf(tmp, "%5.02f", mean);
381 gtk_label_set_text(GTK_LABEL(label), tmp);
382 label = new_info_label_in_frame(hbox, "Standard deviation");
383 sprintf(tmp, "%5.02f", dev);
384 gtk_label_set_text(GTK_LABEL(label), tmp);
395 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
396 struct thread_stat *ts, int ddir)
398 const char *ddir_label[2] = { "Read", "Write" };
399 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
400 unsigned long min[3], max[3], runt;
401 unsigned long long bw, iops;
402 unsigned int flags = 0;
403 double mean[3], dev[3];
404 char *io_p, *bw_p, *iops_p;
407 if (!ts->runtime[ddir])
410 i2p = is_power_of_2(rs->kb_base);
411 runt = ts->runtime[ddir];
413 bw = (1000 * ts->io_bytes[ddir]) / runt;
414 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
415 bw_p = num2str(bw, 6, 1, i2p);
417 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
418 iops_p = num2str(iops, 6, 1, 0);
420 box = gtk_hbox_new(FALSE, 3);
421 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
423 frame = gtk_frame_new(ddir_label[ddir]);
424 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
426 main_vbox = gtk_vbox_new(FALSE, 3);
427 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
429 box = gtk_hbox_new(FALSE, 3);
430 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
432 label = new_info_label_in_frame(box, "IO");
433 gtk_label_set_text(GTK_LABEL(label), io_p);
434 label = new_info_label_in_frame(box, "Bandwidth");
435 gtk_label_set_text(GTK_LABEL(label), bw_p);
436 label = new_info_label_in_frame(box, "IOPS");
437 gtk_label_set_text(GTK_LABEL(label), iops_p);
438 label = new_info_label_in_frame(box, "Runtime (msec)");
439 label_set_int_value(label, ts->runtime[ddir]);
441 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
442 double p_of_agg = 100.0;
443 const char *bw_str = "KB";
447 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
448 if (p_of_agg > 100.0)
452 if (mean[0] > 999999.9) {
460 sprintf(tmp, "Bandwidth (%s)", bw_str);
461 frame = gtk_frame_new(tmp);
462 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
464 box = gtk_hbox_new(FALSE, 3);
465 gtk_container_add(GTK_CONTAINER(frame), box);
467 label = new_info_label_in_frame(box, "Minimum");
468 label_set_int_value(label, min[0]);
469 label = new_info_label_in_frame(box, "Maximum");
470 label_set_int_value(label, max[0]);
471 label = new_info_label_in_frame(box, "Percentage of jobs");
472 sprintf(tmp, "%3.2f%%", p_of_agg);
473 gtk_label_set_text(GTK_LABEL(label), tmp);
474 label = new_info_label_in_frame(box, "Average");
475 sprintf(tmp, "%5.02f", mean[0]);
476 gtk_label_set_text(GTK_LABEL(label), tmp);
477 label = new_info_label_in_frame(box, "Standard deviation");
478 sprintf(tmp, "%5.02f", dev[0]);
479 gtk_label_set_text(GTK_LABEL(label), tmp);
482 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
484 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
486 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
490 frame = gtk_frame_new("Latency");
491 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
493 vbox = gtk_vbox_new(FALSE, 3);
494 gtk_container_add(GTK_CONTAINER(frame), vbox);
496 if (flags & GFIO_SLAT)
497 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
498 if (flags & GFIO_CLAT)
499 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
500 if (flags & GFIO_LAT)
501 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
504 if (ts->clat_percentiles)
505 gfio_show_clat_percentiles(main_vbox, ts, ddir);
513 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
516 GtkWidget *tree_view;
517 GtkTreeSelection *selection;
524 * Check if all are empty, in which case don't bother
526 for (i = 0, skipped = 0; i < num; i++)
533 types = malloc(num * sizeof(GType));
535 for (i = 0; i < num; i++)
536 types[i] = G_TYPE_STRING;
538 model = gtk_list_store_newv(num, types);
542 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
543 gtk_widget_set_can_focus(tree_view, FALSE);
545 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
546 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
548 for (i = 0; i < num; i++)
549 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
551 gtk_list_store_append(model, &iter);
553 for (i = 0; i < num; i++) {
557 sprintf(fbuf, "0.00");
559 sprintf(fbuf, "%3.2f%%", lat[i]);
561 gtk_list_store_set(model, &iter, i, fbuf, -1);
567 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
569 GtkWidget *box, *frame, *tree_view;
570 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
571 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
572 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
573 "250", "500", "750", "1000", };
574 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
575 "250", "500", "750", "1000", "2000",
578 stat_calc_lat_u(ts, io_u_lat_u);
579 stat_calc_lat_m(ts, io_u_lat_m);
581 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
583 frame = gtk_frame_new("Latency buckets (usec)");
584 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
586 box = gtk_hbox_new(FALSE, 3);
587 gtk_container_add(GTK_CONTAINER(frame), box);
588 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
591 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
593 frame = gtk_frame_new("Latency buckets (msec)");
594 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
596 box = gtk_hbox_new(FALSE, 3);
597 gtk_container_add(GTK_CONTAINER(frame), box);
598 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
602 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
604 GtkWidget *box, *frame, *entry;
605 double usr_cpu, sys_cpu;
606 unsigned long runtime;
609 runtime = ts->total_run_time;
611 double runt = (double) runtime;
613 usr_cpu = (double) ts->usr_time * 100 / runt;
614 sys_cpu = (double) ts->sys_time * 100 / runt;
620 frame = gtk_frame_new("OS resources");
621 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
623 box = gtk_hbox_new(FALSE, 3);
624 gtk_container_add(GTK_CONTAINER(frame), box);
626 entry = new_info_entry_in_frame(box, "User CPU");
627 sprintf(tmp, "%3.2f%%", usr_cpu);
628 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
629 entry = new_info_entry_in_frame(box, "System CPU");
630 sprintf(tmp, "%3.2f%%", sys_cpu);
631 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
632 entry = new_info_entry_in_frame(box, "Context switches");
633 entry_set_int_value(entry, ts->ctx);
634 entry = new_info_entry_in_frame(box, "Major faults");
635 entry_set_int_value(entry, ts->majf);
636 entry = new_info_entry_in_frame(box, "Minor faults");
637 entry_set_int_value(entry, ts->minf);
639 static void gfio_add_sc_depths_tree(GtkListStore *model,
640 struct thread_stat *ts, unsigned int len,
643 double io_u_dist[FIO_IO_U_MAP_NR];
645 /* Bits 0, and 3-8 */
646 const int add_mask = 0x1f9;
650 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
652 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
654 gtk_list_store_append(model, &iter);
656 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
658 for (i = 1, j = 0; i < len; i++) {
661 if (!(add_mask & (1UL << (i - 1))))
662 sprintf(fbuf, "0.0%%");
664 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
668 gtk_list_store_set(model, &iter, i, fbuf, -1);
673 static void gfio_add_total_depths_tree(GtkListStore *model,
674 struct thread_stat *ts, unsigned int len)
676 double io_u_dist[FIO_IO_U_MAP_NR];
678 /* Bits 1-6, and 8 */
679 const int add_mask = 0x17e;
682 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
684 gtk_list_store_append(model, &iter);
686 gtk_list_store_set(model, &iter, 0, "Total", -1);
688 for (i = 1, j = 0; i < len; i++) {
691 if (!(add_mask & (1UL << (i - 1))))
692 sprintf(fbuf, "0.0%%");
694 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
698 gtk_list_store_set(model, &iter, i, fbuf, -1);
703 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
705 GtkWidget *frame, *box, *tree_view;
706 GtkTreeSelection *selection;
708 GType types[FIO_IO_U_MAP_NR + 1];
711 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
713 frame = gtk_frame_new("IO depths");
714 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
716 box = gtk_hbox_new(FALSE, 3);
717 gtk_container_add(GTK_CONTAINER(frame), box);
719 for (i = 0; i < NR_LABELS; i++)
720 types[i] = G_TYPE_STRING;
722 model = gtk_list_store_newv(NR_LABELS, types);
724 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
725 gtk_widget_set_can_focus(tree_view, FALSE);
727 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
728 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
730 for (i = 0; i < NR_LABELS; i++)
731 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
733 gfio_add_total_depths_tree(model, ts, NR_LABELS);
734 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
735 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
737 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
740 static gboolean results_window_delete(GtkWidget *w, gpointer data)
742 struct gui *ui = (struct gui *) data;
744 gtk_widget_destroy(w);
745 ui->results_window = NULL;
746 ui->results_notebook = NULL;
750 static GtkWidget *get_results_window(struct gui *ui)
752 GtkWidget *win, *notebook;
754 if (ui->results_window)
755 return ui->results_notebook;
757 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
758 gtk_window_set_title(GTK_WINDOW(win), "Results");
759 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
760 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
762 notebook = gtk_notebook_new();
763 gtk_container_add(GTK_CONTAINER(win), notebook);
765 ui->results_window = win;
766 ui->results_notebook = notebook;
767 return ui->results_notebook;
770 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
771 struct group_run_stats *rs)
773 GtkWidget *res_win, *box, *vbox, *entry;
774 struct gfio_client *gc = client->client_data;
778 res_win = get_results_window(gc->ui);
780 vbox = gtk_vbox_new(FALSE, 3);
782 box = gtk_hbox_new(TRUE, 3);
783 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
785 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
787 gc->results_widget = vbox;
789 entry = new_info_entry_in_frame(box, "Name");
790 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
791 if (strlen(ts->description)) {
792 entry = new_info_entry_in_frame(box, "Description");
793 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
795 entry = new_info_entry_in_frame(box, "Group ID");
796 entry_set_int_value(entry, ts->groupid);
797 entry = new_info_entry_in_frame(box, "Jobs");
798 entry_set_int_value(entry, ts->members);
799 entry = new_info_entry_in_frame(box, "Error");
800 entry_set_int_value(entry, ts->error);
801 entry = new_info_entry_in_frame(box, "PID");
802 entry_set_int_value(entry, ts->pid);
804 if (ts->io_bytes[DDIR_READ])
805 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
806 if (ts->io_bytes[DDIR_WRITE])
807 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
809 gfio_show_latency_buckets(vbox, ts);
810 gfio_show_cpu_usage(vbox, ts);
811 gfio_show_io_depths(vbox, ts);
813 gtk_widget_show_all(gc->ui->results_window);
817 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
819 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
820 struct gfio_client *gc = client->client_data;
824 char tmp[64], timebuf[80];
827 tm = localtime(&sec);
828 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
829 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
833 gtk_list_store_append(gc->ui->log_model, &iter);
834 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
835 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
836 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
837 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
842 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
844 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
845 struct gfio_client *gc = client->client_data;
846 GtkWidget *box, *frame, *entry, *vbox;
850 if (!gc->results_widget) {
851 printf("no results!\n");
855 if (!gc->disk_util_frame) {
856 gc->disk_util_frame = gtk_frame_new("Disk utilization");
857 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
860 vbox = gtk_vbox_new(FALSE, 3);
861 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
863 frame = gtk_frame_new((char *) p->dus.name);
864 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
866 box = gtk_vbox_new(FALSE, 3);
867 gtk_container_add(GTK_CONTAINER(frame), box);
869 frame = gtk_frame_new("Read");
870 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
871 vbox = gtk_hbox_new(TRUE, 3);
872 gtk_container_add(GTK_CONTAINER(frame), vbox);
873 entry = new_info_entry_in_frame(vbox, "IOs");
874 entry_set_int_value(entry, p->dus.ios[0]);
875 entry = new_info_entry_in_frame(vbox, "Merges");
876 entry_set_int_value(entry, p->dus.merges[0]);
877 entry = new_info_entry_in_frame(vbox, "Sectors");
878 entry_set_int_value(entry, p->dus.sectors[0]);
879 entry = new_info_entry_in_frame(vbox, "Ticks");
880 entry_set_int_value(entry, p->dus.ticks[0]);
882 frame = gtk_frame_new("Write");
883 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
884 vbox = gtk_hbox_new(TRUE, 3);
885 gtk_container_add(GTK_CONTAINER(frame), vbox);
886 entry = new_info_entry_in_frame(vbox, "IOs");
887 entry_set_int_value(entry, p->dus.ios[1]);
888 entry = new_info_entry_in_frame(vbox, "Merges");
889 entry_set_int_value(entry, p->dus.merges[1]);
890 entry = new_info_entry_in_frame(vbox, "Sectors");
891 entry_set_int_value(entry, p->dus.sectors[1]);
892 entry = new_info_entry_in_frame(vbox, "Ticks");
893 entry_set_int_value(entry, p->dus.ticks[1]);
895 frame = gtk_frame_new("Shared");
896 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
897 vbox = gtk_hbox_new(TRUE, 3);
898 gtk_container_add(GTK_CONTAINER(frame), vbox);
899 entry = new_info_entry_in_frame(vbox, "IO ticks");
900 entry_set_int_value(entry, p->dus.io_ticks);
901 entry = new_info_entry_in_frame(vbox, "Time in queue");
902 entry_set_int_value(entry, p->dus.time_in_queue);
904 gtk_widget_show_all(gc->results_widget);
909 extern int sum_stat_clients;
910 extern struct thread_stat client_ts;
911 extern struct group_run_stats client_gs;
913 static int sum_stat_nr;
915 static void gfio_thread_status_op(struct fio_client *client,
916 struct fio_net_cmd *cmd)
918 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
920 gfio_display_ts(client, &p->ts, &p->rs);
922 if (sum_stat_clients == 1)
925 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
926 sum_group_stats(&client_gs, &p->rs);
929 client_ts.groupid = p->ts.groupid;
931 if (++sum_stat_nr == sum_stat_clients) {
932 strcpy(client_ts.name, "All clients");
933 gfio_display_ts(client, &client_ts, &client_gs);
937 static void gfio_group_stats_op(struct fio_client *client,
938 struct fio_net_cmd *cmd)
941 printf("gfio_group_stats_op called\n");
942 fio_client_ops.group_stats(client, cmd);
946 static void gfio_update_eta(struct jobs_eta *je)
960 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
961 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
962 eta_to_str(eta_str, je->eta_sec);
965 sprintf(tmp, "%u", je->nr_running);
966 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
967 sprintf(tmp, "%u", je->files_open);
968 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
971 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
972 if (je->m_rate || je->t_rate) {
975 mr = num2str(je->m_rate, 4, 0, i2p);
976 tr = num2str(je->t_rate, 4, 0, i2p);
977 gtk_entry_set_text(GTK_ENTRY(ui.eta);
978 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
981 } else if (je->m_iops || je->t_iops)
982 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
984 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
985 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
986 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
987 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
990 if (je->eta_sec != INT_MAX && je->nr_running) {
994 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
995 strcpy(output, "-.-% done");
999 sprintf(output, "%3.1f%% done", perc);
1002 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1003 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1005 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1006 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1008 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1009 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1010 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1011 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1020 char *dst = output + strlen(output);
1022 sprintf(dst, " - %s", eta_str);
1025 gfio_update_thread_status(output, perc);
1026 gdk_threads_leave();
1029 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1031 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1032 struct gfio_client *gc = client->client_data;
1033 struct gui *ui = gc->ui;
1034 const char *os, *arch;
1037 os = fio_get_os_string(probe->os);
1041 arch = fio_get_arch_string(probe->arch);
1046 client->name = strdup((char *) probe->hostname);
1048 gdk_threads_enter();
1050 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1051 gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1052 gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1053 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1054 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1056 gfio_set_connected(ui, 1);
1058 gdk_threads_leave();
1061 static void gfio_update_thread_status(char *status_message, double perc)
1063 static char message[100];
1064 const char *m = message;
1066 strncpy(message, status_message, sizeof(message) - 1);
1067 gtk_progress_bar_set_text(
1068 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1069 gtk_progress_bar_set_fraction(
1070 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1071 gtk_widget_queue_draw(ui.window);
1074 static void gfio_quit_op(struct fio_client *client)
1076 struct gfio_client *gc = client->client_data;
1078 gdk_threads_enter();
1079 gfio_set_connected(gc->ui, 0);
1080 gdk_threads_leave();
1083 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1085 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1086 struct gfio_client *gc = client->client_data;
1087 struct gui *ui = gc->ui;
1091 p->iodepth = le32_to_cpu(p->iodepth);
1092 p->rw = le32_to_cpu(p->rw);
1094 for (i = 0; i < 2; i++) {
1095 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
1096 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
1099 p->numjobs = le32_to_cpu(p->numjobs);
1100 p->group_reporting = le32_to_cpu(p->group_reporting);
1102 gdk_threads_enter();
1104 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1105 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1106 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1108 sprintf(tmp, "%u", p->iodepth);
1109 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1111 gdk_threads_leave();
1114 static void gfio_client_timed_out(struct fio_client *client)
1116 struct gfio_client *gc = client->client_data;
1117 GtkWidget *dialog, *label, *content;
1120 gdk_threads_enter();
1122 gfio_set_connected(gc->ui, 0);
1123 clear_ui_info(gc->ui);
1125 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1127 dialog = gtk_dialog_new_with_buttons("Timed out!",
1128 GTK_WINDOW(gc->ui->window),
1129 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1130 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1132 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1133 label = gtk_label_new((const gchar *) buf);
1134 gtk_container_add(GTK_CONTAINER(content), label);
1135 gtk_widget_show_all(dialog);
1136 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1138 gtk_dialog_run(GTK_DIALOG(dialog));
1139 gtk_widget_destroy(dialog);
1141 gdk_threads_leave();
1144 struct client_ops gfio_client_ops = {
1145 .text_op = gfio_text_op,
1146 .disk_util = gfio_disk_util_op,
1147 .thread_status = gfio_thread_status_op,
1148 .group_stats = gfio_group_stats_op,
1149 .eta = gfio_update_eta,
1150 .probe = gfio_probe_op,
1151 .quit = gfio_quit_op,
1152 .add_job = gfio_add_job_op,
1153 .timed_out = gfio_client_timed_out,
1154 .stay_connected = 1,
1157 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1158 __attribute__((unused)) gpointer data)
1163 static void *job_thread(void *arg)
1165 fio_handle_clients(&gfio_client_ops);
1169 static int send_job_files(struct gui *ui)
1173 for (i = 0; i < ui->nr_job_files; i++) {
1174 ret = fio_clients_send_ini(ui->job_files[i]);
1178 free(ui->job_files[i]);
1179 ui->job_files[i] = NULL;
1181 while (i < ui->nr_job_files) {
1182 free(ui->job_files[i]);
1183 ui->job_files[i] = NULL;
1190 static void start_job_thread(struct gui *ui)
1192 if (send_job_files(ui)) {
1193 printf("Yeah, I didn't really like those options too much.\n");
1194 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1199 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1202 struct gui *ui = data;
1204 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1205 start_job_thread(ui);
1208 static void file_open(GtkWidget *w, gpointer data);
1210 static void connect_clicked(GtkWidget *widget, gpointer data)
1212 struct gui *ui = data;
1214 if (!ui->connected) {
1215 if (!ui->nr_job_files)
1216 file_open(widget, data);
1217 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1218 fio_clients_connect();
1219 pthread_create(&ui->t, NULL, job_thread, NULL);
1220 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1222 fio_clients_terminate();
1223 gfio_set_connected(ui, 0);
1228 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1229 struct button_spec *buttonspec)
1231 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1232 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1233 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1234 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1235 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1238 static void add_buttons(struct gui *ui,
1239 struct button_spec *buttonlist,
1244 for (i = 0; i < nbuttons; i++)
1245 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1248 static void on_info_bar_response(GtkWidget *widget, gint response,
1251 if (response == GTK_RESPONSE_OK) {
1252 gtk_widget_destroy(widget);
1253 ui.error_info_bar = NULL;
1257 void report_error(GError *error)
1259 if (ui.error_info_bar == NULL) {
1260 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1263 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1264 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1267 ui.error_label = gtk_label_new(error->message);
1268 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1269 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1271 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1272 gtk_widget_show_all(ui.vbox);
1275 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1276 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1280 static int get_connection_details(char **host, int *port, int *type,
1283 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1287 dialog = gtk_dialog_new_with_buttons("Connection details",
1288 GTK_WINDOW(ui.window),
1289 GTK_DIALOG_DESTROY_WITH_PARENT,
1290 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1291 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1293 frame = gtk_frame_new("Hostname / socket name");
1294 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1295 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1297 box = gtk_vbox_new(FALSE, 6);
1298 gtk_container_add(GTK_CONTAINER(frame), box);
1300 hbox = gtk_hbox_new(TRUE, 10);
1301 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1302 hentry = gtk_entry_new();
1303 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1304 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1306 frame = gtk_frame_new("Port");
1307 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1308 box = gtk_vbox_new(FALSE, 10);
1309 gtk_container_add(GTK_CONTAINER(frame), box);
1311 hbox = gtk_hbox_new(TRUE, 4);
1312 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1313 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1315 frame = gtk_frame_new("Type");
1316 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1317 box = gtk_vbox_new(FALSE, 10);
1318 gtk_container_add(GTK_CONTAINER(frame), box);
1320 hbox = gtk_hbox_new(TRUE, 4);
1321 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1323 combo = gtk_combo_box_text_new();
1324 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1325 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1326 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1327 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1329 gtk_container_add(GTK_CONTAINER(hbox), combo);
1331 frame = gtk_frame_new("Options");
1332 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1333 box = gtk_vbox_new(FALSE, 10);
1334 gtk_container_add(GTK_CONTAINER(frame), box);
1336 hbox = gtk_hbox_new(TRUE, 4);
1337 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1339 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1340 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1341 gtk_widget_set_tooltip_text(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.");
1342 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1344 gtk_widget_show_all(dialog);
1346 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1347 gtk_widget_destroy(dialog);
1351 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1352 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1354 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1355 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1356 *type = Fio_client_ipv4;
1357 else if (!strncmp(typeentry, "IPv6", 4))
1358 *type = Fio_client_ipv6;
1360 *type = Fio_client_socket;
1363 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1365 gtk_widget_destroy(dialog);
1369 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1371 struct gfio_client *gc;
1373 gc = malloc(sizeof(*gc));
1374 memset(gc, 0, sizeof(*gc));
1377 client->client_data = gc;
1380 static void file_open(GtkWidget *w, gpointer data)
1383 GSList *filenames, *fn_glist;
1384 GtkFileFilter *filter;
1386 int port, type, server_start;
1388 dialog = gtk_file_chooser_dialog_new("Open File",
1389 GTK_WINDOW(ui.window),
1390 GTK_FILE_CHOOSER_ACTION_OPEN,
1391 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1392 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1394 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1396 filter = gtk_file_filter_new();
1397 gtk_file_filter_add_pattern(filter, "*.fio");
1398 gtk_file_filter_add_pattern(filter, "*.job");
1399 gtk_file_filter_add_mime_type(filter, "text/fio");
1400 gtk_file_filter_set_name(filter, "Fio job file");
1401 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1403 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1404 gtk_widget_destroy(dialog);
1408 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1410 gtk_widget_destroy(dialog);
1412 if (get_connection_details(&host, &port, &type, &server_start))
1415 filenames = fn_glist;
1416 while (filenames != NULL) {
1417 struct fio_client *client;
1419 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1420 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1423 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1427 error = g_error_new(g_quark_from_string("fio"), 1,
1428 "Failed to add client %s", host);
1429 report_error(error);
1430 g_error_free(error);
1432 gfio_client_added(&ui, client);
1434 g_free(filenames->data);
1435 filenames = g_slist_next(filenames);
1439 g_slist_free(fn_glist);
1442 static void file_save(GtkWidget *w, gpointer data)
1446 dialog = gtk_file_chooser_dialog_new("Save File",
1447 GTK_WINDOW(ui.window),
1448 GTK_FILE_CHOOSER_ACTION_SAVE,
1449 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1450 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1453 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1454 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1456 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1459 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1460 // save_job_file(filename);
1463 gtk_widget_destroy(dialog);
1466 static void view_log_destroy(GtkWidget *w, gpointer data)
1468 struct gui *ui = (struct gui *) data;
1470 gtk_widget_ref(ui->log_tree);
1471 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1472 gtk_widget_destroy(w);
1475 static void view_log(GtkWidget *w, gpointer data)
1477 GtkWidget *win, *box;
1479 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1480 gtk_window_set_title(GTK_WINDOW(win), "Log");
1482 box = gtk_hbox_new(FALSE, 3);
1483 gtk_container_add(GTK_CONTAINER(win), box);
1485 g_signal_connect(box, "delete-event", G_CALLBACK(view_log_destroy), (gpointer) &ui);
1486 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), (gpointer) &ui);
1487 gtk_container_add(GTK_CONTAINER(box), ui.log_tree);
1488 gtk_widget_show_all(win);
1491 static void preferences(GtkWidget *w, gpointer data)
1493 GtkWidget *dialog, *frame, *box, **buttons;
1496 dialog = gtk_dialog_new_with_buttons("Preferences",
1497 GTK_WINDOW(ui.window),
1498 GTK_DIALOG_DESTROY_WITH_PARENT,
1499 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1500 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1503 frame = gtk_frame_new("Debug logging");
1504 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1505 box = gtk_hbox_new(FALSE, 6);
1506 gtk_container_add(GTK_CONTAINER(frame), box);
1508 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1510 for (i = 0; i < FD_DEBUG_MAX; i++) {
1511 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1512 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1513 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1516 gtk_widget_show_all(dialog);
1518 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1519 gtk_widget_destroy(dialog);
1523 for (i = 0; i < FD_DEBUG_MAX; i++) {
1526 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1528 fio_debug |= (1UL << i);
1531 gtk_widget_destroy(dialog);
1534 static void about_dialog(GtkWidget *w, gpointer data)
1536 gtk_show_about_dialog(NULL,
1537 "program-name", "gfio",
1538 "comments", "Gtk2 UI for fio",
1540 "version", fio_version_string,
1541 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1542 "logo-icon-name", "fio",
1548 static GtkActionEntry menu_items[] = {
1549 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1550 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1551 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1552 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1553 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1554 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1555 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1556 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1557 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1559 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1561 static const gchar *ui_string = " \
1563 <menubar name=\"MainMenu\"> \
1564 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1565 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1566 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1567 <separator name=\"Separator\"/> \
1568 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1569 <separator name=\"Separator2\"/> \
1570 <menuitem name=\"Quit\" action=\"Quit\" /> \
1572 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1573 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1575 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1576 <menuitem name=\"About\" action=\"About\" /> \
1582 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1584 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1587 action_group = gtk_action_group_new("Menu");
1588 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1590 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1591 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1593 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1594 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1597 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1598 GtkWidget *vbox, GtkUIManager *ui_manager)
1600 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1603 static void init_ui(int *argc, char **argv[], struct gui *ui)
1605 GtkSettings *settings;
1606 GtkUIManager *uimanager;
1607 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1609 memset(ui, 0, sizeof(*ui));
1611 /* Magical g*thread incantation, you just need this thread stuff.
1612 * Without it, the update that happens in gfio_update_thread_status
1613 * doesn't really happen in a timely fashion, you need expose events
1615 if (!g_thread_supported())
1616 g_thread_init(NULL);
1619 gtk_init(argc, argv);
1620 settings = gtk_settings_get_default();
1621 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1624 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1625 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1626 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1628 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1629 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1631 ui->vbox = gtk_vbox_new(FALSE, 0);
1632 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1634 uimanager = gtk_ui_manager_new();
1635 menu = get_menubar_menu(ui->window, uimanager);
1636 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1639 * Set up alignments for widgets at the top of ui,
1640 * align top left, expand horizontally but not vertically
1642 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1643 ui->topvbox = gtk_vbox_new(FALSE, 3);
1644 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1645 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1647 probe = gtk_frame_new("Job");
1648 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1649 probe_frame = gtk_vbox_new(FALSE, 3);
1650 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1652 probe_box = gtk_hbox_new(FALSE, 3);
1653 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1654 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1655 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1656 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1657 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1659 probe_box = gtk_hbox_new(FALSE, 3);
1660 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1662 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1663 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1664 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1665 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1666 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1667 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1669 probe_box = gtk_hbox_new(FALSE, 3);
1670 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1671 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1672 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1673 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1674 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1677 * Only add this if we have a commit rate
1680 probe_box = gtk_hbox_new(FALSE, 3);
1681 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1683 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1684 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1686 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1687 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1691 * Add a text box for text op messages
1693 ui->textview = gtk_text_view_new();
1694 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1695 gtk_text_buffer_set_text(ui->text, "", -1);
1696 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1697 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1698 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1699 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1700 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1701 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1702 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1706 * Set up alignments for widgets at the bottom of ui,
1707 * align bottom left, expand horizontally but not vertically
1709 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1710 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1711 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1712 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1715 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1718 * Set up thread status progress bar
1720 ui->thread_status_pb = gtk_progress_bar_new();
1721 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1722 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1723 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1725 gfio_ui_setup_log(ui);
1727 gtk_widget_show_all(ui->window);
1730 int main(int argc, char *argv[], char *envp[])
1732 if (initialize_fio(envp))
1734 if (fio_init_options())
1737 init_ui(&argc, &argv, &ui);
1739 gdk_threads_enter();
1741 gdk_threads_leave();