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");
180 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
181 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
185 static void label_set_int_value(GtkWidget *entry, unsigned int val)
189 sprintf(tmp, "%u", val);
190 gtk_label_set_text(GTK_LABEL(entry), tmp);
193 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
197 sprintf(tmp, "%u", val);
198 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
202 #define ALIGN_RIGHT 2
206 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
208 GtkCellRenderer *renderer;
209 GtkTreeViewColumn *col;
210 double xalign = 0.0; /* left as default */
211 PangoAlignment align;
214 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
215 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
217 visible = !(flags & INVISIBLE);
219 renderer = gtk_cell_renderer_text_new();
220 col = gtk_tree_view_column_new();
222 gtk_tree_view_column_set_title(col, title);
223 if (!(flags & UNSORTABLE))
224 gtk_tree_view_column_set_sort_column_id(col, index);
225 gtk_tree_view_column_set_resizable(col, TRUE);
226 gtk_tree_view_column_pack_start(col, renderer, TRUE);
227 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
228 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
230 case PANGO_ALIGN_LEFT:
233 case PANGO_ALIGN_CENTER:
236 case PANGO_ALIGN_RIGHT:
240 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
241 gtk_tree_view_column_set_visible(col, visible);
242 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
246 static void gfio_ui_setup_log(struct gui *ui)
248 GtkTreeSelection *selection;
250 GtkWidget *tree_view;
252 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
254 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
255 gtk_widget_set_can_focus(tree_view, FALSE);
257 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
258 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
260 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
261 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
262 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
263 tree_view_column(tree_view, 3, "Text", ALIGN_RIGHT | UNSORTABLE);
265 ui->log_model = model;
266 ui->log_tree = tree_view;
269 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
275 GType types[FIO_IO_U_LIST_MAX_LEN];
276 GtkWidget *tree_view;
277 GtkTreeSelection *selection;
282 for (i = 0; i < len; i++)
283 types[i] = G_TYPE_INT;
285 model = gtk_list_store_newv(len, types);
287 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
288 gtk_widget_set_can_focus(tree_view, FALSE);
290 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
291 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
293 for (i = 0; i < len; i++) {
296 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
297 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
300 gtk_list_store_append(model, &iter);
302 for (i = 0; i < len; i++) {
304 ovals[i] = (ovals[i] + 999) / 1000;
305 gtk_list_store_set(model, &iter, i, ovals[i], -1);
311 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
314 unsigned int *io_u_plat = ts->io_u_plat[ddir];
315 unsigned long nr = ts->clat_stat[ddir].samples;
316 fio_fp64_t *plist = ts->percentile_list;
317 unsigned int *ovals, len, minv, maxv, scale_down;
319 GtkWidget *tree_view, *frame, *hbox;
322 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
327 * We default to usecs, but if the value range is such that we
328 * should scale down to msecs, do that.
330 if (minv > 2000 && maxv > 99999) {
338 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
340 sprintf(tmp, "Completion percentiles (%s)", base);
341 frame = gtk_frame_new(tmp);
342 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
344 hbox = gtk_hbox_new(FALSE, 3);
345 gtk_container_add(GTK_CONTAINER(frame), hbox);
347 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
353 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
354 unsigned long max, double mean, double dev)
356 const char *base = "(usec)";
357 GtkWidget *hbox, *label, *frame;
361 if (!usec_to_msec(&min, &max, &mean, &dev))
364 minp = num2str(min, 6, 1, 0);
365 maxp = num2str(max, 6, 1, 0);
367 sprintf(tmp, "%s %s", name, base);
368 frame = gtk_frame_new(tmp);
369 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
371 hbox = gtk_hbox_new(FALSE, 3);
372 gtk_container_add(GTK_CONTAINER(frame), hbox);
374 label = new_info_label_in_frame(hbox, "Minimum");
375 gtk_label_set_text(GTK_LABEL(label), minp);
376 label = new_info_label_in_frame(hbox, "Maximum");
377 gtk_label_set_text(GTK_LABEL(label), maxp);
378 label = new_info_label_in_frame(hbox, "Average");
379 sprintf(tmp, "%5.02f", mean);
380 gtk_label_set_text(GTK_LABEL(label), tmp);
381 label = new_info_label_in_frame(hbox, "Standard deviation");
382 sprintf(tmp, "%5.02f", dev);
383 gtk_label_set_text(GTK_LABEL(label), tmp);
394 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
395 struct thread_stat *ts, int ddir)
397 const char *ddir_label[2] = { "Read", "Write" };
398 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
399 unsigned long min[3], max[3], runt;
400 unsigned long long bw, iops;
401 unsigned int flags = 0;
402 double mean[3], dev[3];
403 char *io_p, *bw_p, *iops_p;
406 if (!ts->runtime[ddir])
409 i2p = is_power_of_2(rs->kb_base);
410 runt = ts->runtime[ddir];
412 bw = (1000 * ts->io_bytes[ddir]) / runt;
413 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
414 bw_p = num2str(bw, 6, 1, i2p);
416 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
417 iops_p = num2str(iops, 6, 1, 0);
419 box = gtk_hbox_new(FALSE, 3);
420 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
422 frame = gtk_frame_new(ddir_label[ddir]);
423 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
425 main_vbox = gtk_vbox_new(FALSE, 3);
426 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
428 box = gtk_hbox_new(FALSE, 3);
429 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
431 label = new_info_label_in_frame(box, "IO");
432 gtk_label_set_text(GTK_LABEL(label), io_p);
433 label = new_info_label_in_frame(box, "Bandwidth");
434 gtk_label_set_text(GTK_LABEL(label), bw_p);
435 label = new_info_label_in_frame(box, "IOPS");
436 gtk_label_set_text(GTK_LABEL(label), iops_p);
437 label = new_info_label_in_frame(box, "Runtime (msec)");
438 label_set_int_value(label, ts->runtime[ddir]);
440 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
441 double p_of_agg = 100.0;
442 const char *bw_str = "KB";
446 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
447 if (p_of_agg > 100.0)
451 if (mean[0] > 999999.9) {
459 sprintf(tmp, "Bandwidth (%s)", bw_str);
460 frame = gtk_frame_new(tmp);
461 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
463 box = gtk_hbox_new(FALSE, 3);
464 gtk_container_add(GTK_CONTAINER(frame), box);
466 label = new_info_label_in_frame(box, "Minimum");
467 label_set_int_value(label, min[0]);
468 label = new_info_label_in_frame(box, "Maximum");
469 label_set_int_value(label, max[0]);
470 label = new_info_label_in_frame(box, "Percentage of jobs");
471 sprintf(tmp, "%3.2f%%", p_of_agg);
472 gtk_label_set_text(GTK_LABEL(label), tmp);
473 label = new_info_label_in_frame(box, "Average");
474 sprintf(tmp, "%5.02f", mean[0]);
475 gtk_label_set_text(GTK_LABEL(label), tmp);
476 label = new_info_label_in_frame(box, "Standard deviation");
477 sprintf(tmp, "%5.02f", dev[0]);
478 gtk_label_set_text(GTK_LABEL(label), tmp);
481 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
483 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
485 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
489 frame = gtk_frame_new("Latency");
490 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
492 vbox = gtk_vbox_new(FALSE, 3);
493 gtk_container_add(GTK_CONTAINER(frame), vbox);
495 if (flags & GFIO_SLAT)
496 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
497 if (flags & GFIO_CLAT)
498 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
499 if (flags & GFIO_LAT)
500 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
503 if (ts->clat_percentiles)
504 gfio_show_clat_percentiles(main_vbox, ts, ddir);
512 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
515 GtkWidget *tree_view;
516 GtkTreeSelection *selection;
523 * Check if all are empty, in which case don't bother
525 for (i = 0, skipped = 0; i < num; i++)
532 types = malloc(num * sizeof(GType));
534 for (i = 0; i < num; i++)
535 types[i] = G_TYPE_STRING;
537 model = gtk_list_store_newv(num, types);
541 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
542 gtk_widget_set_can_focus(tree_view, FALSE);
544 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
545 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
547 for (i = 0; i < num; i++)
548 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
550 gtk_list_store_append(model, &iter);
552 for (i = 0; i < num; i++) {
556 sprintf(fbuf, "0.00");
558 sprintf(fbuf, "%3.2f%%", lat[i]);
560 gtk_list_store_set(model, &iter, i, fbuf, -1);
566 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
568 GtkWidget *box, *frame, *tree_view;
569 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
570 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
571 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
572 "250", "500", "750", "1000", };
573 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
574 "250", "500", "750", "1000", "2000",
577 stat_calc_lat_u(ts, io_u_lat_u);
578 stat_calc_lat_m(ts, io_u_lat_m);
580 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
582 frame = gtk_frame_new("Latency buckets (usec)");
583 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
585 box = gtk_hbox_new(FALSE, 3);
586 gtk_container_add(GTK_CONTAINER(frame), box);
587 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
590 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
592 frame = gtk_frame_new("Latency buckets (msec)");
593 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
595 box = gtk_hbox_new(FALSE, 3);
596 gtk_container_add(GTK_CONTAINER(frame), box);
597 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
601 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
603 GtkWidget *box, *frame, *entry;
604 double usr_cpu, sys_cpu;
605 unsigned long runtime;
608 runtime = ts->total_run_time;
610 double runt = (double) runtime;
612 usr_cpu = (double) ts->usr_time * 100 / runt;
613 sys_cpu = (double) ts->sys_time * 100 / runt;
619 frame = gtk_frame_new("OS resources");
620 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
622 box = gtk_hbox_new(FALSE, 3);
623 gtk_container_add(GTK_CONTAINER(frame), box);
625 entry = new_info_entry_in_frame(box, "User CPU");
626 sprintf(tmp, "%3.2f%%", usr_cpu);
627 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
628 entry = new_info_entry_in_frame(box, "System CPU");
629 sprintf(tmp, "%3.2f%%", sys_cpu);
630 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
631 entry = new_info_entry_in_frame(box, "Context switches");
632 entry_set_int_value(entry, ts->ctx);
633 entry = new_info_entry_in_frame(box, "Major faults");
634 entry_set_int_value(entry, ts->majf);
635 entry = new_info_entry_in_frame(box, "Minor faults");
636 entry_set_int_value(entry, ts->minf);
638 static void gfio_add_sc_depths_tree(GtkListStore *model,
639 struct thread_stat *ts, unsigned int len,
642 double io_u_dist[FIO_IO_U_MAP_NR];
644 /* Bits 0, and 3-8 */
645 const int add_mask = 0x1f9;
649 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
651 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
653 gtk_list_store_append(model, &iter);
655 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
657 for (i = 1, j = 0; i < len; i++) {
660 if (!(add_mask & (1UL << (i - 1))))
661 sprintf(fbuf, "0.0%%");
663 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
667 gtk_list_store_set(model, &iter, i, fbuf, -1);
672 static void gfio_add_total_depths_tree(GtkListStore *model,
673 struct thread_stat *ts, unsigned int len)
675 double io_u_dist[FIO_IO_U_MAP_NR];
677 /* Bits 1-6, and 8 */
678 const int add_mask = 0x17e;
681 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
683 gtk_list_store_append(model, &iter);
685 gtk_list_store_set(model, &iter, 0, "Total", -1);
687 for (i = 1, j = 0; i < len; i++) {
690 if (!(add_mask & (1UL << (i - 1))))
691 sprintf(fbuf, "0.0%%");
693 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
697 gtk_list_store_set(model, &iter, i, fbuf, -1);
702 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
704 GtkWidget *frame, *box, *tree_view;
705 GtkTreeSelection *selection;
707 GType types[FIO_IO_U_MAP_NR + 1];
710 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
712 frame = gtk_frame_new("IO depths");
713 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
715 box = gtk_hbox_new(FALSE, 3);
716 gtk_container_add(GTK_CONTAINER(frame), box);
718 for (i = 0; i < NR_LABELS; i++)
719 types[i] = G_TYPE_STRING;
721 model = gtk_list_store_newv(NR_LABELS, types);
723 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
724 gtk_widget_set_can_focus(tree_view, FALSE);
726 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
727 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
729 for (i = 0; i < NR_LABELS; i++)
730 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
732 gfio_add_total_depths_tree(model, ts, NR_LABELS);
733 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
734 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
736 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
739 static gboolean results_window_delete(GtkWidget *w, gpointer data)
741 struct gui *ui = (struct gui *) data;
743 gtk_widget_destroy(w);
744 ui->results_window = NULL;
745 ui->results_notebook = NULL;
749 static GtkWidget *get_results_window(struct gui *ui)
751 GtkWidget *win, *notebook;
753 if (ui->results_window)
754 return ui->results_notebook;
756 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
757 gtk_window_set_title(GTK_WINDOW(win), "Results");
758 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
759 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
761 notebook = gtk_notebook_new();
762 gtk_container_add(GTK_CONTAINER(win), notebook);
764 ui->results_window = win;
765 ui->results_notebook = notebook;
766 return ui->results_notebook;
769 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
770 struct group_run_stats *rs)
772 GtkWidget *res_win, *box, *vbox, *entry;
773 struct gfio_client *gc = client->client_data;
777 res_win = get_results_window(gc->ui);
779 vbox = gtk_vbox_new(FALSE, 3);
781 box = gtk_hbox_new(TRUE, 3);
782 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
784 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
786 gc->results_widget = vbox;
788 entry = new_info_entry_in_frame(box, "Name");
789 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
790 if (strlen(ts->description)) {
791 entry = new_info_entry_in_frame(box, "Description");
792 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
794 entry = new_info_entry_in_frame(box, "Group ID");
795 entry_set_int_value(entry, ts->groupid);
796 entry = new_info_entry_in_frame(box, "Jobs");
797 entry_set_int_value(entry, ts->members);
798 entry = new_info_entry_in_frame(box, "Error");
799 entry_set_int_value(entry, ts->error);
800 entry = new_info_entry_in_frame(box, "PID");
801 entry_set_int_value(entry, ts->pid);
803 if (ts->io_bytes[DDIR_READ])
804 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
805 if (ts->io_bytes[DDIR_WRITE])
806 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
808 gfio_show_latency_buckets(vbox, ts);
809 gfio_show_cpu_usage(vbox, ts);
810 gfio_show_io_depths(vbox, ts);
812 gtk_widget_show_all(gc->ui->results_window);
816 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
818 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
819 struct gfio_client *gc = client->client_data;
823 char tmp[64], timebuf[80];
826 tm = localtime(&sec);
827 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
828 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
832 gtk_list_store_append(gc->ui->log_model, &iter);
833 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
834 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
835 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
836 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
841 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
843 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
844 struct gfio_client *gc = client->client_data;
845 GtkWidget *box, *frame, *entry, *vbox;
849 if (!gc->results_widget) {
850 printf("no results!\n");
854 if (!gc->disk_util_frame) {
855 gc->disk_util_frame = gtk_frame_new("Disk utilization");
856 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
859 vbox = gtk_vbox_new(FALSE, 3);
860 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
862 frame = gtk_frame_new((char *) p->dus.name);
863 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
865 box = gtk_vbox_new(FALSE, 3);
866 gtk_container_add(GTK_CONTAINER(frame), box);
868 frame = gtk_frame_new("Read");
869 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
870 vbox = gtk_hbox_new(TRUE, 3);
871 gtk_container_add(GTK_CONTAINER(frame), vbox);
872 entry = new_info_entry_in_frame(vbox, "IOs");
873 entry_set_int_value(entry, p->dus.ios[0]);
874 entry = new_info_entry_in_frame(vbox, "Merges");
875 entry_set_int_value(entry, p->dus.merges[0]);
876 entry = new_info_entry_in_frame(vbox, "Sectors");
877 entry_set_int_value(entry, p->dus.sectors[0]);
878 entry = new_info_entry_in_frame(vbox, "Ticks");
879 entry_set_int_value(entry, p->dus.ticks[0]);
881 frame = gtk_frame_new("Write");
882 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
883 vbox = gtk_hbox_new(TRUE, 3);
884 gtk_container_add(GTK_CONTAINER(frame), vbox);
885 entry = new_info_entry_in_frame(vbox, "IOs");
886 entry_set_int_value(entry, p->dus.ios[1]);
887 entry = new_info_entry_in_frame(vbox, "Merges");
888 entry_set_int_value(entry, p->dus.merges[1]);
889 entry = new_info_entry_in_frame(vbox, "Sectors");
890 entry_set_int_value(entry, p->dus.sectors[1]);
891 entry = new_info_entry_in_frame(vbox, "Ticks");
892 entry_set_int_value(entry, p->dus.ticks[1]);
894 frame = gtk_frame_new("Shared");
895 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
896 vbox = gtk_hbox_new(TRUE, 3);
897 gtk_container_add(GTK_CONTAINER(frame), vbox);
898 entry = new_info_entry_in_frame(vbox, "IO ticks");
899 entry_set_int_value(entry, p->dus.io_ticks);
900 entry = new_info_entry_in_frame(vbox, "Time in queue");
901 entry_set_int_value(entry, p->dus.time_in_queue);
903 gtk_widget_show_all(gc->results_widget);
908 extern int sum_stat_clients;
909 extern struct thread_stat client_ts;
910 extern struct group_run_stats client_gs;
912 static int sum_stat_nr;
914 static void gfio_thread_status_op(struct fio_client *client,
915 struct fio_net_cmd *cmd)
917 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
919 gfio_display_ts(client, &p->ts, &p->rs);
921 if (sum_stat_clients == 1)
924 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
925 sum_group_stats(&client_gs, &p->rs);
928 client_ts.groupid = p->ts.groupid;
930 if (++sum_stat_nr == sum_stat_clients) {
931 strcpy(client_ts.name, "All clients");
932 gfio_display_ts(client, &client_ts, &client_gs);
936 static void gfio_group_stats_op(struct fio_client *client,
937 struct fio_net_cmd *cmd)
940 printf("gfio_group_stats_op called\n");
941 fio_client_ops.group_stats(client, cmd);
945 static void gfio_update_eta(struct jobs_eta *je)
959 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
960 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
961 eta_to_str(eta_str, je->eta_sec);
964 sprintf(tmp, "%u", je->nr_running);
965 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
966 sprintf(tmp, "%u", je->files_open);
967 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
970 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
971 if (je->m_rate || je->t_rate) {
974 mr = num2str(je->m_rate, 4, 0, i2p);
975 tr = num2str(je->t_rate, 4, 0, i2p);
976 gtk_entry_set_text(GTK_ENTRY(ui.eta);
977 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
980 } else if (je->m_iops || je->t_iops)
981 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
983 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
984 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
985 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
986 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
989 if (je->eta_sec != INT_MAX && je->nr_running) {
993 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
994 strcpy(output, "-.-% done");
998 sprintf(output, "%3.1f%% done", perc);
1001 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1002 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1004 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1005 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1007 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1008 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1009 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1010 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1019 char *dst = output + strlen(output);
1021 sprintf(dst, " - %s", eta_str);
1024 gfio_update_thread_status(output, perc);
1025 gdk_threads_leave();
1028 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1030 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1031 const char *os, *arch;
1034 os = fio_get_os_string(probe->os);
1038 arch = fio_get_arch_string(probe->arch);
1043 client->name = strdup((char *) probe->hostname);
1045 gdk_threads_enter();
1047 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
1048 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
1049 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
1050 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1051 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
1053 gdk_threads_leave();
1056 static void gfio_update_thread_status(char *status_message, double perc)
1058 static char message[100];
1059 const char *m = message;
1061 strncpy(message, status_message, sizeof(message) - 1);
1062 gtk_progress_bar_set_text(
1063 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1064 gtk_progress_bar_set_fraction(
1065 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1066 gtk_widget_queue_draw(ui.window);
1069 static void gfio_quit_op(struct fio_client *client)
1071 struct gfio_client *gc = client->client_data;
1073 gdk_threads_enter();
1074 gfio_set_connected(gc->ui, 0);
1075 gdk_threads_leave();
1078 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1080 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1081 struct gfio_client *gc = client->client_data;
1082 struct gui *ui = gc->ui;
1086 p->iodepth = le32_to_cpu(p->iodepth);
1087 p->rw = le32_to_cpu(p->rw);
1089 for (i = 0; i < 2; i++) {
1090 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
1091 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
1094 p->numjobs = le32_to_cpu(p->numjobs);
1095 p->group_reporting = le32_to_cpu(p->group_reporting);
1097 gdk_threads_enter();
1099 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1100 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1101 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1103 sprintf(tmp, "%u", p->iodepth);
1104 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1106 gdk_threads_leave();
1109 static void gfio_client_timed_out(struct fio_client *client)
1111 struct gfio_client *gc = client->client_data;
1112 GtkWidget *dialog, *label, *content;
1115 gdk_threads_enter();
1117 gfio_set_connected(gc->ui, 0);
1118 clear_ui_info(gc->ui);
1120 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1122 dialog = gtk_dialog_new_with_buttons("Timed out!",
1123 GTK_WINDOW(gc->ui->window),
1124 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1125 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1127 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1128 label = gtk_label_new((const gchar *) buf);
1129 gtk_container_add(GTK_CONTAINER(content), label);
1130 gtk_widget_show_all(dialog);
1131 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1133 gtk_dialog_run(GTK_DIALOG(dialog));
1134 gtk_widget_destroy(dialog);
1136 gdk_threads_leave();
1139 struct client_ops gfio_client_ops = {
1140 .text_op = gfio_text_op,
1141 .disk_util = gfio_disk_util_op,
1142 .thread_status = gfio_thread_status_op,
1143 .group_stats = gfio_group_stats_op,
1144 .eta = gfio_update_eta,
1145 .probe = gfio_probe_op,
1146 .quit = gfio_quit_op,
1147 .add_job = gfio_add_job_op,
1148 .timed_out = gfio_client_timed_out,
1149 .stay_connected = 1,
1152 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1153 __attribute__((unused)) gpointer data)
1158 static void *job_thread(void *arg)
1160 fio_handle_clients(&gfio_client_ops);
1164 static int send_job_files(struct gui *ui)
1168 for (i = 0; i < ui->nr_job_files; i++) {
1169 ret = fio_clients_send_ini(ui->job_files[i]);
1173 free(ui->job_files[i]);
1174 ui->job_files[i] = NULL;
1176 while (i < ui->nr_job_files) {
1177 free(ui->job_files[i]);
1178 ui->job_files[i] = NULL;
1185 static void start_job_thread(struct gui *ui)
1187 if (send_job_files(ui)) {
1188 printf("Yeah, I didn't really like those options too much.\n");
1189 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1194 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1197 struct gui *ui = data;
1199 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1200 start_job_thread(ui);
1203 static void file_open(GtkWidget *w, gpointer data);
1205 static void connect_clicked(GtkWidget *widget, gpointer data)
1207 struct gui *ui = data;
1209 if (!ui->connected) {
1210 if (!ui->nr_job_files)
1211 file_open(widget, data);
1212 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1213 fio_clients_connect();
1214 pthread_create(&ui->t, NULL, job_thread, NULL);
1215 gfio_set_connected(ui, 1);
1217 fio_clients_terminate();
1218 gfio_set_connected(ui, 0);
1223 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1224 struct button_spec *buttonspec)
1226 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1227 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1228 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1229 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1230 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1233 static void add_buttons(struct gui *ui,
1234 struct button_spec *buttonlist,
1239 for (i = 0; i < nbuttons; i++)
1240 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1243 static void on_info_bar_response(GtkWidget *widget, gint response,
1246 if (response == GTK_RESPONSE_OK) {
1247 gtk_widget_destroy(widget);
1248 ui.error_info_bar = NULL;
1252 void report_error(GError *error)
1254 if (ui.error_info_bar == NULL) {
1255 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1258 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1259 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1262 ui.error_label = gtk_label_new(error->message);
1263 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1264 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1266 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1267 gtk_widget_show_all(ui.vbox);
1270 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1271 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1275 static int get_connection_details(char **host, int *port, int *type,
1278 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1282 dialog = gtk_dialog_new_with_buttons("Connection details",
1283 GTK_WINDOW(ui.window),
1284 GTK_DIALOG_DESTROY_WITH_PARENT,
1285 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1286 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1288 frame = gtk_frame_new("Hostname / socket name");
1289 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1290 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1292 box = gtk_vbox_new(FALSE, 6);
1293 gtk_container_add(GTK_CONTAINER(frame), box);
1295 hbox = gtk_hbox_new(TRUE, 10);
1296 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1297 hentry = gtk_entry_new();
1298 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1299 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1301 frame = gtk_frame_new("Port");
1302 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1303 box = gtk_vbox_new(FALSE, 10);
1304 gtk_container_add(GTK_CONTAINER(frame), box);
1306 hbox = gtk_hbox_new(TRUE, 4);
1307 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1308 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1310 frame = gtk_frame_new("Type");
1311 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1312 box = gtk_vbox_new(FALSE, 10);
1313 gtk_container_add(GTK_CONTAINER(frame), box);
1315 hbox = gtk_hbox_new(TRUE, 4);
1316 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1318 combo = gtk_combo_box_text_new();
1319 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1320 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1321 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1322 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1324 gtk_container_add(GTK_CONTAINER(hbox), combo);
1326 frame = gtk_frame_new("Options");
1327 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1328 box = gtk_vbox_new(FALSE, 10);
1329 gtk_container_add(GTK_CONTAINER(frame), box);
1331 hbox = gtk_hbox_new(TRUE, 4);
1332 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1334 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1335 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1336 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.");
1337 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1339 gtk_widget_show_all(dialog);
1341 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1342 gtk_widget_destroy(dialog);
1346 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1347 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1349 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1350 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1351 *type = Fio_client_ipv4;
1352 else if (!strncmp(typeentry, "IPv6", 4))
1353 *type = Fio_client_ipv6;
1355 *type = Fio_client_socket;
1358 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1360 gtk_widget_destroy(dialog);
1364 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1366 struct gfio_client *gc;
1368 gc = malloc(sizeof(*gc));
1369 memset(gc, 0, sizeof(*gc));
1372 client->client_data = gc;
1375 static void file_open(GtkWidget *w, gpointer data)
1378 GSList *filenames, *fn_glist;
1379 GtkFileFilter *filter;
1381 int port, type, server_start;
1383 dialog = gtk_file_chooser_dialog_new("Open File",
1384 GTK_WINDOW(ui.window),
1385 GTK_FILE_CHOOSER_ACTION_OPEN,
1386 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1387 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1389 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1391 filter = gtk_file_filter_new();
1392 gtk_file_filter_add_pattern(filter, "*.fio");
1393 gtk_file_filter_add_pattern(filter, "*.job");
1394 gtk_file_filter_add_mime_type(filter, "text/fio");
1395 gtk_file_filter_set_name(filter, "Fio job file");
1396 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1398 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1399 gtk_widget_destroy(dialog);
1403 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1405 gtk_widget_destroy(dialog);
1407 if (get_connection_details(&host, &port, &type, &server_start))
1410 filenames = fn_glist;
1411 while (filenames != NULL) {
1412 struct fio_client *client;
1414 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1415 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1418 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1422 error = g_error_new(g_quark_from_string("fio"), 1,
1423 "Failed to add client %s", host);
1424 report_error(error);
1425 g_error_free(error);
1427 gfio_client_added(&ui, client);
1429 g_free(filenames->data);
1430 filenames = g_slist_next(filenames);
1434 g_slist_free(fn_glist);
1437 static void file_save(GtkWidget *w, gpointer data)
1441 dialog = gtk_file_chooser_dialog_new("Save File",
1442 GTK_WINDOW(ui.window),
1443 GTK_FILE_CHOOSER_ACTION_SAVE,
1444 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1445 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1448 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1449 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1451 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1454 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1455 // save_job_file(filename);
1458 gtk_widget_destroy(dialog);
1461 static void view_log_destroy(GtkWidget *w, gpointer data)
1463 struct gui *ui = (struct gui *) data;
1465 gtk_widget_ref(ui->log_tree);
1466 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1467 gtk_widget_destroy(w);
1470 static void view_log(GtkWidget *w, gpointer data)
1472 GtkWidget *win, *box;
1474 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1475 gtk_window_set_title(GTK_WINDOW(win), "Log");
1477 box = gtk_hbox_new(FALSE, 3);
1478 gtk_container_add(GTK_CONTAINER(win), box);
1480 g_signal_connect(box, "delete-event", G_CALLBACK(view_log_destroy), (gpointer) &ui);
1481 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), (gpointer) &ui);
1482 gtk_container_add(GTK_CONTAINER(box), ui.log_tree);
1483 gtk_widget_show_all(win);
1486 static void preferences(GtkWidget *w, gpointer data)
1488 GtkWidget *dialog, *frame, *box, **buttons;
1491 dialog = gtk_dialog_new_with_buttons("Preferences",
1492 GTK_WINDOW(ui.window),
1493 GTK_DIALOG_DESTROY_WITH_PARENT,
1494 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1495 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1498 frame = gtk_frame_new("Debug logging");
1499 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1500 box = gtk_hbox_new(FALSE, 6);
1501 gtk_container_add(GTK_CONTAINER(frame), box);
1503 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1505 for (i = 0; i < FD_DEBUG_MAX; i++) {
1506 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1507 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1508 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1511 gtk_widget_show_all(dialog);
1513 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1514 gtk_widget_destroy(dialog);
1518 for (i = 0; i < FD_DEBUG_MAX; i++) {
1521 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1523 fio_debug |= (1UL << i);
1526 gtk_widget_destroy(dialog);
1529 static void about_dialog(GtkWidget *w, gpointer data)
1531 gtk_show_about_dialog(NULL,
1532 "program-name", "gfio",
1533 "comments", "Gtk2 UI for fio",
1535 "version", fio_version_string,
1536 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1537 "logo-icon-name", "fio",
1543 static GtkActionEntry menu_items[] = {
1544 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1545 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1546 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1547 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1548 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1549 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1550 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1551 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1552 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1554 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1556 static const gchar *ui_string = " \
1558 <menubar name=\"MainMenu\"> \
1559 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1560 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1561 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1562 <separator name=\"Separator\"/> \
1563 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1564 <separator name=\"Separator2\"/> \
1565 <menuitem name=\"Quit\" action=\"Quit\" /> \
1567 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1568 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1570 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1571 <menuitem name=\"About\" action=\"About\" /> \
1577 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1579 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1582 action_group = gtk_action_group_new("Menu");
1583 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1585 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1586 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1588 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1589 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1592 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1593 GtkWidget *vbox, GtkUIManager *ui_manager)
1595 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1598 static void init_ui(int *argc, char **argv[], struct gui *ui)
1600 GtkSettings *settings;
1601 GtkUIManager *uimanager;
1602 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1604 memset(ui, 0, sizeof(*ui));
1606 /* Magical g*thread incantation, you just need this thread stuff.
1607 * Without it, the update that happens in gfio_update_thread_status
1608 * doesn't really happen in a timely fashion, you need expose events
1610 if (!g_thread_supported())
1611 g_thread_init(NULL);
1614 gtk_init(argc, argv);
1615 settings = gtk_settings_get_default();
1616 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1619 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1620 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1621 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1623 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1624 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1626 ui->vbox = gtk_vbox_new(FALSE, 0);
1627 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1629 uimanager = gtk_ui_manager_new();
1630 menu = get_menubar_menu(ui->window, uimanager);
1631 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1634 * Set up alignments for widgets at the top of ui,
1635 * align top left, expand horizontally but not vertically
1637 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1638 ui->topvbox = gtk_vbox_new(FALSE, 3);
1639 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1640 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1642 probe = gtk_frame_new("Job");
1643 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1644 probe_frame = gtk_vbox_new(FALSE, 3);
1645 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1647 probe_box = gtk_hbox_new(FALSE, 3);
1648 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1649 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1650 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1651 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1652 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1654 probe_box = gtk_hbox_new(FALSE, 3);
1655 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1657 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1658 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1659 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1660 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1661 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1662 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1664 probe_box = gtk_hbox_new(FALSE, 3);
1665 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1666 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1667 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1668 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1669 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1672 * Only add this if we have a commit rate
1675 probe_box = gtk_hbox_new(FALSE, 3);
1676 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1678 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1679 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1681 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1682 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1686 * Add a text box for text op messages
1688 ui->textview = gtk_text_view_new();
1689 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1690 gtk_text_buffer_set_text(ui->text, "", -1);
1691 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1692 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1693 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1694 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1695 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1696 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1697 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1701 * Set up alignments for widgets at the bottom of ui,
1702 * align bottom left, expand horizontally but not vertically
1704 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1705 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1706 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1707 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1710 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1713 * Set up thread status progress bar
1715 ui->thread_status_pb = gtk_progress_bar_new();
1716 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1717 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1718 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1720 gfio_ui_setup_log(ui);
1722 gtk_widget_show_all(ui->window);
1725 int main(int argc, char *argv[], char *envp[])
1727 if (initialize_fio(envp))
1729 if (fio_init_options())
1732 init_ui(&argc, &argv, &ui);
1734 gdk_threads_enter();
1736 gdk_threads_leave();