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;
97 struct probe_widget probe;
98 struct eta_widget eta;
102 struct fio_client *client;
109 GtkWidget *results_widget;
110 GtkWidget *disk_util_frame;
113 static void clear_ui_info(struct gui *ui)
115 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
116 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
117 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
118 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
119 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
120 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
121 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
122 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
123 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
124 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
125 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
126 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
127 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
128 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
131 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
133 GtkWidget *entry, *frame;
135 frame = gtk_frame_new(label);
136 entry = gtk_entry_new();
137 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
138 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
139 gtk_container_add(GTK_CONTAINER(frame), entry);
144 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
146 GtkWidget *label_widget;
149 frame = gtk_frame_new(label);
150 label_widget = gtk_label_new(NULL);
151 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
152 gtk_container_add(GTK_CONTAINER(frame), label_widget);
157 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
159 GtkWidget *button, *box;
161 box = gtk_hbox_new(FALSE, 3);
162 gtk_container_add(GTK_CONTAINER(hbox), box);
164 button = gtk_spin_button_new_with_range(min, max, 1.0);
165 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
167 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
168 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
173 static void gfio_set_connected(struct gui *ui, int connected)
176 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
178 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
179 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
182 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
183 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
187 static void label_set_int_value(GtkWidget *entry, unsigned int val)
191 sprintf(tmp, "%u", val);
192 gtk_label_set_text(GTK_LABEL(entry), tmp);
195 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
199 sprintf(tmp, "%u", val);
200 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
204 #define ALIGN_RIGHT 2
208 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
210 GtkCellRenderer *renderer;
211 GtkTreeViewColumn *col;
212 double xalign = 0.0; /* left as default */
213 PangoAlignment align;
216 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
217 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
219 visible = !(flags & INVISIBLE);
221 renderer = gtk_cell_renderer_text_new();
222 col = gtk_tree_view_column_new();
224 gtk_tree_view_column_set_title(col, title);
225 if (!(flags & UNSORTABLE))
226 gtk_tree_view_column_set_sort_column_id(col, index);
227 gtk_tree_view_column_set_resizable(col, TRUE);
228 gtk_tree_view_column_pack_start(col, renderer, TRUE);
229 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
230 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
232 case PANGO_ALIGN_LEFT:
235 case PANGO_ALIGN_CENTER:
238 case PANGO_ALIGN_RIGHT:
242 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
243 gtk_tree_view_column_set_visible(col, visible);
244 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
248 static void gfio_ui_setup_log(struct gui *ui)
250 GtkTreeSelection *selection;
252 GtkWidget *tree_view;
254 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
256 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
257 gtk_widget_set_can_focus(tree_view, FALSE);
259 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
260 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
262 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
263 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
264 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
265 tree_view_column(tree_view, 3, "Text", ALIGN_RIGHT | UNSORTABLE);
267 ui->log_model = model;
268 ui->log_tree = tree_view;
271 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
277 GType types[FIO_IO_U_LIST_MAX_LEN];
278 GtkWidget *tree_view;
279 GtkTreeSelection *selection;
284 for (i = 0; i < len; i++)
285 types[i] = G_TYPE_INT;
287 model = gtk_list_store_newv(len, types);
289 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
290 gtk_widget_set_can_focus(tree_view, FALSE);
292 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
293 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
295 for (i = 0; i < len; i++) {
298 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
299 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
302 gtk_list_store_append(model, &iter);
304 for (i = 0; i < len; i++) {
306 ovals[i] = (ovals[i] + 999) / 1000;
307 gtk_list_store_set(model, &iter, i, ovals[i], -1);
313 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
316 unsigned int *io_u_plat = ts->io_u_plat[ddir];
317 unsigned long nr = ts->clat_stat[ddir].samples;
318 fio_fp64_t *plist = ts->percentile_list;
319 unsigned int *ovals, len, minv, maxv, scale_down;
321 GtkWidget *tree_view, *frame, *hbox;
324 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
329 * We default to usecs, but if the value range is such that we
330 * should scale down to msecs, do that.
332 if (minv > 2000 && maxv > 99999) {
340 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
342 sprintf(tmp, "Completion percentiles (%s)", base);
343 frame = gtk_frame_new(tmp);
344 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
346 hbox = gtk_hbox_new(FALSE, 3);
347 gtk_container_add(GTK_CONTAINER(frame), hbox);
349 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
355 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
356 unsigned long max, double mean, double dev)
358 const char *base = "(usec)";
359 GtkWidget *hbox, *label, *frame;
363 if (!usec_to_msec(&min, &max, &mean, &dev))
366 minp = num2str(min, 6, 1, 0);
367 maxp = num2str(max, 6, 1, 0);
369 sprintf(tmp, "%s %s", name, base);
370 frame = gtk_frame_new(tmp);
371 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
373 hbox = gtk_hbox_new(FALSE, 3);
374 gtk_container_add(GTK_CONTAINER(frame), hbox);
376 label = new_info_label_in_frame(hbox, "Minimum");
377 gtk_label_set_text(GTK_LABEL(label), minp);
378 label = new_info_label_in_frame(hbox, "Maximum");
379 gtk_label_set_text(GTK_LABEL(label), maxp);
380 label = new_info_label_in_frame(hbox, "Average");
381 sprintf(tmp, "%5.02f", mean);
382 gtk_label_set_text(GTK_LABEL(label), tmp);
383 label = new_info_label_in_frame(hbox, "Standard deviation");
384 sprintf(tmp, "%5.02f", dev);
385 gtk_label_set_text(GTK_LABEL(label), tmp);
396 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
397 struct thread_stat *ts, int ddir)
399 const char *ddir_label[2] = { "Read", "Write" };
400 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
401 unsigned long min[3], max[3], runt;
402 unsigned long long bw, iops;
403 unsigned int flags = 0;
404 double mean[3], dev[3];
405 char *io_p, *bw_p, *iops_p;
408 if (!ts->runtime[ddir])
411 i2p = is_power_of_2(rs->kb_base);
412 runt = ts->runtime[ddir];
414 bw = (1000 * ts->io_bytes[ddir]) / runt;
415 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
416 bw_p = num2str(bw, 6, 1, i2p);
418 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
419 iops_p = num2str(iops, 6, 1, 0);
421 box = gtk_hbox_new(FALSE, 3);
422 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
424 frame = gtk_frame_new(ddir_label[ddir]);
425 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
427 main_vbox = gtk_vbox_new(FALSE, 3);
428 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
430 box = gtk_hbox_new(FALSE, 3);
431 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
433 label = new_info_label_in_frame(box, "IO");
434 gtk_label_set_text(GTK_LABEL(label), io_p);
435 label = new_info_label_in_frame(box, "Bandwidth");
436 gtk_label_set_text(GTK_LABEL(label), bw_p);
437 label = new_info_label_in_frame(box, "IOPS");
438 gtk_label_set_text(GTK_LABEL(label), iops_p);
439 label = new_info_label_in_frame(box, "Runtime (msec)");
440 label_set_int_value(label, ts->runtime[ddir]);
442 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
443 double p_of_agg = 100.0;
444 const char *bw_str = "KB";
448 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
449 if (p_of_agg > 100.0)
453 if (mean[0] > 999999.9) {
461 sprintf(tmp, "Bandwidth (%s)", bw_str);
462 frame = gtk_frame_new(tmp);
463 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
465 box = gtk_hbox_new(FALSE, 3);
466 gtk_container_add(GTK_CONTAINER(frame), box);
468 label = new_info_label_in_frame(box, "Minimum");
469 label_set_int_value(label, min[0]);
470 label = new_info_label_in_frame(box, "Maximum");
471 label_set_int_value(label, max[0]);
472 label = new_info_label_in_frame(box, "Percentage of jobs");
473 sprintf(tmp, "%3.2f%%", p_of_agg);
474 gtk_label_set_text(GTK_LABEL(label), tmp);
475 label = new_info_label_in_frame(box, "Average");
476 sprintf(tmp, "%5.02f", mean[0]);
477 gtk_label_set_text(GTK_LABEL(label), tmp);
478 label = new_info_label_in_frame(box, "Standard deviation");
479 sprintf(tmp, "%5.02f", dev[0]);
480 gtk_label_set_text(GTK_LABEL(label), tmp);
483 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
485 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
487 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
491 frame = gtk_frame_new("Latency");
492 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
494 vbox = gtk_vbox_new(FALSE, 3);
495 gtk_container_add(GTK_CONTAINER(frame), vbox);
497 if (flags & GFIO_SLAT)
498 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
499 if (flags & GFIO_CLAT)
500 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
501 if (flags & GFIO_LAT)
502 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
505 if (ts->clat_percentiles)
506 gfio_show_clat_percentiles(main_vbox, ts, ddir);
514 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
517 GtkWidget *tree_view;
518 GtkTreeSelection *selection;
525 * Check if all are empty, in which case don't bother
527 for (i = 0, skipped = 0; i < num; i++)
534 types = malloc(num * sizeof(GType));
536 for (i = 0; i < num; i++)
537 types[i] = G_TYPE_STRING;
539 model = gtk_list_store_newv(num, types);
543 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
544 gtk_widget_set_can_focus(tree_view, FALSE);
546 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
547 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
549 for (i = 0; i < num; i++)
550 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
552 gtk_list_store_append(model, &iter);
554 for (i = 0; i < num; i++) {
558 sprintf(fbuf, "0.00");
560 sprintf(fbuf, "%3.2f%%", lat[i]);
562 gtk_list_store_set(model, &iter, i, fbuf, -1);
568 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
570 GtkWidget *box, *frame, *tree_view;
571 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
572 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
573 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
574 "250", "500", "750", "1000", };
575 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
576 "250", "500", "750", "1000", "2000",
579 stat_calc_lat_u(ts, io_u_lat_u);
580 stat_calc_lat_m(ts, io_u_lat_m);
582 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
584 frame = gtk_frame_new("Latency buckets (usec)");
585 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
587 box = gtk_hbox_new(FALSE, 3);
588 gtk_container_add(GTK_CONTAINER(frame), box);
589 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
592 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
594 frame = gtk_frame_new("Latency buckets (msec)");
595 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
597 box = gtk_hbox_new(FALSE, 3);
598 gtk_container_add(GTK_CONTAINER(frame), box);
599 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
603 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
605 GtkWidget *box, *frame, *entry;
606 double usr_cpu, sys_cpu;
607 unsigned long runtime;
610 runtime = ts->total_run_time;
612 double runt = (double) runtime;
614 usr_cpu = (double) ts->usr_time * 100 / runt;
615 sys_cpu = (double) ts->sys_time * 100 / runt;
621 frame = gtk_frame_new("OS resources");
622 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
624 box = gtk_hbox_new(FALSE, 3);
625 gtk_container_add(GTK_CONTAINER(frame), box);
627 entry = new_info_entry_in_frame(box, "User CPU");
628 sprintf(tmp, "%3.2f%%", usr_cpu);
629 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
630 entry = new_info_entry_in_frame(box, "System CPU");
631 sprintf(tmp, "%3.2f%%", sys_cpu);
632 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
633 entry = new_info_entry_in_frame(box, "Context switches");
634 entry_set_int_value(entry, ts->ctx);
635 entry = new_info_entry_in_frame(box, "Major faults");
636 entry_set_int_value(entry, ts->majf);
637 entry = new_info_entry_in_frame(box, "Minor faults");
638 entry_set_int_value(entry, ts->minf);
640 static void gfio_add_sc_depths_tree(GtkListStore *model,
641 struct thread_stat *ts, unsigned int len,
644 double io_u_dist[FIO_IO_U_MAP_NR];
646 /* Bits 0, and 3-8 */
647 const int add_mask = 0x1f9;
651 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
653 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
655 gtk_list_store_append(model, &iter);
657 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
659 for (i = 1, j = 0; i < len; i++) {
662 if (!(add_mask & (1UL << (i - 1))))
663 sprintf(fbuf, "0.0%%");
665 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
669 gtk_list_store_set(model, &iter, i, fbuf, -1);
674 static void gfio_add_total_depths_tree(GtkListStore *model,
675 struct thread_stat *ts, unsigned int len)
677 double io_u_dist[FIO_IO_U_MAP_NR];
679 /* Bits 1-6, and 8 */
680 const int add_mask = 0x17e;
683 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
685 gtk_list_store_append(model, &iter);
687 gtk_list_store_set(model, &iter, 0, "Total", -1);
689 for (i = 1, j = 0; i < len; i++) {
692 if (!(add_mask & (1UL << (i - 1))))
693 sprintf(fbuf, "0.0%%");
695 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
699 gtk_list_store_set(model, &iter, i, fbuf, -1);
704 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
706 GtkWidget *frame, *box, *tree_view;
707 GtkTreeSelection *selection;
709 GType types[FIO_IO_U_MAP_NR + 1];
712 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
714 frame = gtk_frame_new("IO depths");
715 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
717 box = gtk_hbox_new(FALSE, 3);
718 gtk_container_add(GTK_CONTAINER(frame), box);
720 for (i = 0; i < NR_LABELS; i++)
721 types[i] = G_TYPE_STRING;
723 model = gtk_list_store_newv(NR_LABELS, types);
725 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
726 gtk_widget_set_can_focus(tree_view, FALSE);
728 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
729 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
731 for (i = 0; i < NR_LABELS; i++)
732 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
734 gfio_add_total_depths_tree(model, ts, NR_LABELS);
735 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
736 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
738 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
741 static gboolean results_window_delete(GtkWidget *w, gpointer data)
743 struct gui *ui = (struct gui *) data;
745 gtk_widget_destroy(w);
746 ui->results_window = NULL;
747 ui->results_notebook = NULL;
751 static GtkWidget *get_results_window(struct gui *ui)
753 GtkWidget *win, *notebook;
755 if (ui->results_window)
756 return ui->results_notebook;
758 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
759 gtk_window_set_title(GTK_WINDOW(win), "Results");
760 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
761 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
763 notebook = gtk_notebook_new();
764 gtk_container_add(GTK_CONTAINER(win), notebook);
766 ui->results_window = win;
767 ui->results_notebook = notebook;
768 return ui->results_notebook;
771 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
772 struct group_run_stats *rs)
774 GtkWidget *res_win, *box, *vbox, *entry;
775 struct gfio_client *gc = client->client_data;
779 res_win = get_results_window(gc->ui);
781 vbox = gtk_vbox_new(FALSE, 3);
783 box = gtk_hbox_new(TRUE, 3);
784 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
786 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
788 gc->results_widget = vbox;
790 entry = new_info_entry_in_frame(box, "Name");
791 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
792 if (strlen(ts->description)) {
793 entry = new_info_entry_in_frame(box, "Description");
794 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
796 entry = new_info_entry_in_frame(box, "Group ID");
797 entry_set_int_value(entry, ts->groupid);
798 entry = new_info_entry_in_frame(box, "Jobs");
799 entry_set_int_value(entry, ts->members);
800 entry = new_info_entry_in_frame(box, "Error");
801 entry_set_int_value(entry, ts->error);
802 entry = new_info_entry_in_frame(box, "PID");
803 entry_set_int_value(entry, ts->pid);
805 if (ts->io_bytes[DDIR_READ])
806 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
807 if (ts->io_bytes[DDIR_WRITE])
808 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
810 gfio_show_latency_buckets(vbox, ts);
811 gfio_show_cpu_usage(vbox, ts);
812 gfio_show_io_depths(vbox, ts);
814 gtk_widget_show_all(gc->ui->results_window);
818 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
820 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
821 struct gfio_client *gc = client->client_data;
825 char tmp[64], timebuf[80];
828 tm = localtime(&sec);
829 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
830 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
834 gtk_list_store_append(gc->ui->log_model, &iter);
835 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
836 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
837 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
838 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
843 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
845 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
846 struct gfio_client *gc = client->client_data;
847 GtkWidget *box, *frame, *entry, *vbox;
851 if (!gc->results_widget) {
852 printf("no results!\n");
856 if (!gc->disk_util_frame) {
857 gc->disk_util_frame = gtk_frame_new("Disk utilization");
858 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
861 vbox = gtk_vbox_new(FALSE, 3);
862 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
864 frame = gtk_frame_new((char *) p->dus.name);
865 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
867 box = gtk_vbox_new(FALSE, 3);
868 gtk_container_add(GTK_CONTAINER(frame), box);
870 frame = gtk_frame_new("Read");
871 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
872 vbox = gtk_hbox_new(TRUE, 3);
873 gtk_container_add(GTK_CONTAINER(frame), vbox);
874 entry = new_info_entry_in_frame(vbox, "IOs");
875 entry_set_int_value(entry, p->dus.ios[0]);
876 entry = new_info_entry_in_frame(vbox, "Merges");
877 entry_set_int_value(entry, p->dus.merges[0]);
878 entry = new_info_entry_in_frame(vbox, "Sectors");
879 entry_set_int_value(entry, p->dus.sectors[0]);
880 entry = new_info_entry_in_frame(vbox, "Ticks");
881 entry_set_int_value(entry, p->dus.ticks[0]);
883 frame = gtk_frame_new("Write");
884 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
885 vbox = gtk_hbox_new(TRUE, 3);
886 gtk_container_add(GTK_CONTAINER(frame), vbox);
887 entry = new_info_entry_in_frame(vbox, "IOs");
888 entry_set_int_value(entry, p->dus.ios[1]);
889 entry = new_info_entry_in_frame(vbox, "Merges");
890 entry_set_int_value(entry, p->dus.merges[1]);
891 entry = new_info_entry_in_frame(vbox, "Sectors");
892 entry_set_int_value(entry, p->dus.sectors[1]);
893 entry = new_info_entry_in_frame(vbox, "Ticks");
894 entry_set_int_value(entry, p->dus.ticks[1]);
896 frame = gtk_frame_new("Shared");
897 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
898 vbox = gtk_hbox_new(TRUE, 3);
899 gtk_container_add(GTK_CONTAINER(frame), vbox);
900 entry = new_info_entry_in_frame(vbox, "IO ticks");
901 entry_set_int_value(entry, p->dus.io_ticks);
902 entry = new_info_entry_in_frame(vbox, "Time in queue");
903 entry_set_int_value(entry, p->dus.time_in_queue);
905 gtk_widget_show_all(gc->results_widget);
910 extern int sum_stat_clients;
911 extern struct thread_stat client_ts;
912 extern struct group_run_stats client_gs;
914 static int sum_stat_nr;
916 static void gfio_thread_status_op(struct fio_client *client,
917 struct fio_net_cmd *cmd)
919 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
921 gfio_display_ts(client, &p->ts, &p->rs);
923 if (sum_stat_clients == 1)
926 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
927 sum_group_stats(&client_gs, &p->rs);
930 client_ts.groupid = p->ts.groupid;
932 if (++sum_stat_nr == sum_stat_clients) {
933 strcpy(client_ts.name, "All clients");
934 gfio_display_ts(client, &client_ts, &client_gs);
938 static void gfio_group_stats_op(struct fio_client *client,
939 struct fio_net_cmd *cmd)
942 printf("gfio_group_stats_op called\n");
943 fio_client_ops.group_stats(client, cmd);
947 static void gfio_update_eta(struct jobs_eta *je)
961 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
962 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
963 eta_to_str(eta_str, je->eta_sec);
966 sprintf(tmp, "%u", je->nr_running);
967 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
968 sprintf(tmp, "%u", je->files_open);
969 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
972 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
973 if (je->m_rate || je->t_rate) {
976 mr = num2str(je->m_rate, 4, 0, i2p);
977 tr = num2str(je->t_rate, 4, 0, i2p);
978 gtk_entry_set_text(GTK_ENTRY(ui.eta);
979 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
982 } else if (je->m_iops || je->t_iops)
983 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
985 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
986 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
987 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
988 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
991 if (je->eta_sec != INT_MAX && je->nr_running) {
995 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
996 strcpy(output, "-.-% done");
1000 sprintf(output, "%3.1f%% done", perc);
1003 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1004 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1006 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1007 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1009 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1010 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1011 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1012 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1021 char *dst = output + strlen(output);
1023 sprintf(dst, " - %s", eta_str);
1026 gfio_update_thread_status(output, perc);
1027 gdk_threads_leave();
1030 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1032 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1033 struct gfio_client *gc = client->client_data;
1034 struct gui *ui = gc->ui;
1035 const char *os, *arch;
1038 os = fio_get_os_string(probe->os);
1042 arch = fio_get_arch_string(probe->arch);
1047 client->name = strdup((char *) probe->hostname);
1049 gdk_threads_enter();
1051 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1052 gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1053 gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1054 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1055 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1057 gfio_set_connected(ui, 1);
1059 gdk_threads_leave();
1062 static void gfio_update_thread_status(char *status_message, double perc)
1064 static char message[100];
1065 const char *m = message;
1067 strncpy(message, status_message, sizeof(message) - 1);
1068 gtk_progress_bar_set_text(
1069 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1070 gtk_progress_bar_set_fraction(
1071 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1072 gtk_widget_queue_draw(ui.window);
1075 static void gfio_quit_op(struct fio_client *client)
1077 struct gfio_client *gc = client->client_data;
1079 gdk_threads_enter();
1080 gfio_set_connected(gc->ui, 0);
1081 gdk_threads_leave();
1084 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1086 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1087 struct gfio_client *gc = client->client_data;
1088 struct gui *ui = gc->ui;
1092 p->iodepth = le32_to_cpu(p->iodepth);
1093 p->rw = le32_to_cpu(p->rw);
1095 for (i = 0; i < 2; i++) {
1096 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
1097 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
1100 p->numjobs = le32_to_cpu(p->numjobs);
1101 p->group_reporting = le32_to_cpu(p->group_reporting);
1103 gdk_threads_enter();
1105 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1106 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1107 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1109 sprintf(tmp, "%u", p->iodepth);
1110 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1112 gdk_threads_leave();
1115 static void gfio_client_timed_out(struct fio_client *client)
1117 struct gfio_client *gc = client->client_data;
1118 GtkWidget *dialog, *label, *content;
1121 gdk_threads_enter();
1123 gfio_set_connected(gc->ui, 0);
1124 clear_ui_info(gc->ui);
1126 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1128 dialog = gtk_dialog_new_with_buttons("Timed out!",
1129 GTK_WINDOW(gc->ui->window),
1130 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1131 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1133 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1134 label = gtk_label_new((const gchar *) buf);
1135 gtk_container_add(GTK_CONTAINER(content), label);
1136 gtk_widget_show_all(dialog);
1137 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1139 gtk_dialog_run(GTK_DIALOG(dialog));
1140 gtk_widget_destroy(dialog);
1142 gdk_threads_leave();
1145 struct client_ops gfio_client_ops = {
1146 .text_op = gfio_text_op,
1147 .disk_util = gfio_disk_util_op,
1148 .thread_status = gfio_thread_status_op,
1149 .group_stats = gfio_group_stats_op,
1150 .eta = gfio_update_eta,
1151 .probe = gfio_probe_op,
1152 .quit = gfio_quit_op,
1153 .add_job = gfio_add_job_op,
1154 .timed_out = gfio_client_timed_out,
1155 .stay_connected = 1,
1158 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1159 __attribute__((unused)) gpointer data)
1164 static void *job_thread(void *arg)
1166 fio_handle_clients(&gfio_client_ops);
1170 static int send_job_files(struct gui *ui)
1174 for (i = 0; i < ui->nr_job_files; i++) {
1175 ret = fio_clients_send_ini(ui->job_files[i]);
1179 free(ui->job_files[i]);
1180 ui->job_files[i] = NULL;
1182 while (i < ui->nr_job_files) {
1183 free(ui->job_files[i]);
1184 ui->job_files[i] = NULL;
1191 static void start_job_thread(struct gui *ui)
1193 if (send_job_files(ui)) {
1194 printf("Yeah, I didn't really like those options too much.\n");
1195 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1200 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1203 struct gui *ui = data;
1205 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1206 start_job_thread(ui);
1209 static void file_open(GtkWidget *w, gpointer data);
1211 static void connect_clicked(GtkWidget *widget, gpointer data)
1213 struct gui *ui = data;
1215 if (!ui->connected) {
1216 if (!ui->nr_job_files)
1217 file_open(widget, data);
1218 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1219 fio_clients_connect();
1220 pthread_create(&ui->t, NULL, job_thread, NULL);
1221 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1223 fio_clients_terminate();
1224 gfio_set_connected(ui, 0);
1229 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1230 struct button_spec *buttonspec)
1232 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1233 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1234 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1235 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1236 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1239 static void add_buttons(struct gui *ui,
1240 struct button_spec *buttonlist,
1245 for (i = 0; i < nbuttons; i++)
1246 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1249 static void on_info_bar_response(GtkWidget *widget, gint response,
1252 if (response == GTK_RESPONSE_OK) {
1253 gtk_widget_destroy(widget);
1254 ui.error_info_bar = NULL;
1258 void report_error(GError *error)
1260 if (ui.error_info_bar == NULL) {
1261 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1264 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1265 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1268 ui.error_label = gtk_label_new(error->message);
1269 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1270 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1272 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1273 gtk_widget_show_all(ui.vbox);
1276 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1277 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1281 static int get_connection_details(char **host, int *port, int *type,
1284 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1288 dialog = gtk_dialog_new_with_buttons("Connection details",
1289 GTK_WINDOW(ui.window),
1290 GTK_DIALOG_DESTROY_WITH_PARENT,
1291 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1292 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1294 frame = gtk_frame_new("Hostname / socket name");
1295 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1296 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1298 box = gtk_vbox_new(FALSE, 6);
1299 gtk_container_add(GTK_CONTAINER(frame), box);
1301 hbox = gtk_hbox_new(TRUE, 10);
1302 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1303 hentry = gtk_entry_new();
1304 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1305 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1307 frame = gtk_frame_new("Port");
1308 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1309 box = gtk_vbox_new(FALSE, 10);
1310 gtk_container_add(GTK_CONTAINER(frame), box);
1312 hbox = gtk_hbox_new(TRUE, 4);
1313 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1314 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1316 frame = gtk_frame_new("Type");
1317 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1318 box = gtk_vbox_new(FALSE, 10);
1319 gtk_container_add(GTK_CONTAINER(frame), box);
1321 hbox = gtk_hbox_new(TRUE, 4);
1322 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1324 combo = gtk_combo_box_new_text();
1325 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1326 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1327 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
1328 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1330 gtk_container_add(GTK_CONTAINER(hbox), combo);
1332 frame = gtk_frame_new("Options");
1333 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1334 box = gtk_vbox_new(FALSE, 10);
1335 gtk_container_add(GTK_CONTAINER(frame), box);
1337 hbox = gtk_hbox_new(TRUE, 4);
1338 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1340 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1341 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1342 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.");
1343 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1345 gtk_widget_show_all(dialog);
1347 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1348 gtk_widget_destroy(dialog);
1352 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1353 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1355 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1356 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1357 *type = Fio_client_ipv4;
1358 else if (!strncmp(typeentry, "IPv6", 4))
1359 *type = Fio_client_ipv6;
1361 *type = Fio_client_socket;
1364 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1366 gtk_widget_destroy(dialog);
1370 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1372 struct gfio_client *gc;
1374 gc = malloc(sizeof(*gc));
1375 memset(gc, 0, sizeof(*gc));
1378 client->client_data = gc;
1381 static void file_open(GtkWidget *w, gpointer data)
1384 GSList *filenames, *fn_glist;
1385 GtkFileFilter *filter;
1387 int port, type, server_start;
1389 dialog = gtk_file_chooser_dialog_new("Open File",
1390 GTK_WINDOW(ui.window),
1391 GTK_FILE_CHOOSER_ACTION_OPEN,
1392 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1393 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1395 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1397 filter = gtk_file_filter_new();
1398 gtk_file_filter_add_pattern(filter, "*.fio");
1399 gtk_file_filter_add_pattern(filter, "*.job");
1400 gtk_file_filter_add_mime_type(filter, "text/fio");
1401 gtk_file_filter_set_name(filter, "Fio job file");
1402 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1404 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1405 gtk_widget_destroy(dialog);
1409 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1411 gtk_widget_destroy(dialog);
1413 if (get_connection_details(&host, &port, &type, &server_start))
1416 filenames = fn_glist;
1417 while (filenames != NULL) {
1418 struct fio_client *client;
1420 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1421 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1424 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1428 error = g_error_new(g_quark_from_string("fio"), 1,
1429 "Failed to add client %s", host);
1430 report_error(error);
1431 g_error_free(error);
1433 gfio_client_added(&ui, client);
1435 g_free(filenames->data);
1436 filenames = g_slist_next(filenames);
1440 g_slist_free(fn_glist);
1443 static void file_save(GtkWidget *w, gpointer data)
1447 dialog = gtk_file_chooser_dialog_new("Save File",
1448 GTK_WINDOW(ui.window),
1449 GTK_FILE_CHOOSER_ACTION_SAVE,
1450 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1451 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1454 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1455 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1457 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1460 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1461 // save_job_file(filename);
1464 gtk_widget_destroy(dialog);
1467 static void view_log_destroy(GtkWidget *w, gpointer data)
1469 struct gui *ui = (struct gui *) data;
1471 gtk_widget_ref(ui->log_tree);
1472 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1473 gtk_widget_destroy(w);
1474 ui->log_view = NULL;
1477 static void view_log(GtkWidget *w, gpointer data)
1479 GtkWidget *win, *scroll, *vbox, *box;
1480 struct gui *ui = (struct gui *) data;
1485 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1486 gtk_window_set_title(GTK_WINDOW(win), "Log");
1487 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1489 scroll = gtk_scrolled_window_new(NULL, NULL);
1491 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1493 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1495 box = gtk_hbox_new(TRUE, 0);
1496 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1497 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1498 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1500 vbox = gtk_vbox_new(TRUE, 5);
1501 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1503 gtk_container_add(GTK_CONTAINER(win), vbox);
1504 gtk_widget_show_all(win);
1507 static void preferences(GtkWidget *w, gpointer data)
1509 GtkWidget *dialog, *frame, *box, **buttons;
1512 dialog = gtk_dialog_new_with_buttons("Preferences",
1513 GTK_WINDOW(ui.window),
1514 GTK_DIALOG_DESTROY_WITH_PARENT,
1515 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1516 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1519 frame = gtk_frame_new("Debug logging");
1520 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1521 box = gtk_hbox_new(FALSE, 6);
1522 gtk_container_add(GTK_CONTAINER(frame), box);
1524 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1526 for (i = 0; i < FD_DEBUG_MAX; i++) {
1527 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1528 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1529 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1532 gtk_widget_show_all(dialog);
1534 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1535 gtk_widget_destroy(dialog);
1539 for (i = 0; i < FD_DEBUG_MAX; i++) {
1542 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1544 fio_debug |= (1UL << i);
1547 gtk_widget_destroy(dialog);
1550 static void about_dialog(GtkWidget *w, gpointer data)
1552 gtk_show_about_dialog(NULL,
1553 "program-name", "gfio",
1554 "comments", "Gtk2 UI for fio",
1556 "version", fio_version_string,
1557 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1558 "logo-icon-name", "fio",
1564 static GtkActionEntry menu_items[] = {
1565 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1566 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1567 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1568 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1569 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1570 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1571 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1572 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1573 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1575 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1577 static const gchar *ui_string = " \
1579 <menubar name=\"MainMenu\"> \
1580 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1581 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1582 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1583 <separator name=\"Separator\"/> \
1584 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1585 <separator name=\"Separator2\"/> \
1586 <menuitem name=\"Quit\" action=\"Quit\" /> \
1588 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1589 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1591 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1592 <menuitem name=\"About\" action=\"About\" /> \
1598 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1601 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1604 action_group = gtk_action_group_new("Menu");
1605 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1607 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1608 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1610 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1611 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1614 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1615 GtkWidget *vbox, GtkUIManager *ui_manager)
1617 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1620 static void init_ui(int *argc, char **argv[], struct gui *ui)
1622 GtkSettings *settings;
1623 GtkUIManager *uimanager;
1624 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1626 memset(ui, 0, sizeof(*ui));
1628 /* Magical g*thread incantation, you just need this thread stuff.
1629 * Without it, the update that happens in gfio_update_thread_status
1630 * doesn't really happen in a timely fashion, you need expose events
1632 if (!g_thread_supported())
1633 g_thread_init(NULL);
1636 gtk_init(argc, argv);
1637 settings = gtk_settings_get_default();
1638 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1641 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1642 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1643 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1645 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1646 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1648 ui->vbox = gtk_vbox_new(FALSE, 0);
1649 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1651 uimanager = gtk_ui_manager_new();
1652 menu = get_menubar_menu(ui->window, uimanager, ui);
1653 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1656 * Set up alignments for widgets at the top of ui,
1657 * align top left, expand horizontally but not vertically
1659 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1660 ui->topvbox = gtk_vbox_new(FALSE, 3);
1661 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1662 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1664 probe = gtk_frame_new("Job");
1665 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1666 probe_frame = gtk_vbox_new(FALSE, 3);
1667 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1669 probe_box = gtk_hbox_new(FALSE, 3);
1670 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1671 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1672 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1673 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1674 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1676 probe_box = gtk_hbox_new(FALSE, 3);
1677 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1679 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1680 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1681 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1682 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1683 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1684 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1686 probe_box = gtk_hbox_new(FALSE, 3);
1687 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1688 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1689 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1690 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1691 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1694 * Only add this if we have a commit rate
1697 probe_box = gtk_hbox_new(FALSE, 3);
1698 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1700 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1701 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1703 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1704 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1708 * Add a text box for text op messages
1710 ui->textview = gtk_text_view_new();
1711 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1712 gtk_text_buffer_set_text(ui->text, "", -1);
1713 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1714 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1715 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1716 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1717 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1718 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1719 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1723 * Set up alignments for widgets at the bottom of ui,
1724 * align bottom left, expand horizontally but not vertically
1726 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1727 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1728 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1729 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1732 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1735 * Set up thread status progress bar
1737 ui->thread_status_pb = gtk_progress_bar_new();
1738 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1739 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1740 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1742 gfio_ui_setup_log(ui);
1744 gtk_widget_show_all(ui->window);
1747 int main(int argc, char *argv[], char *envp[])
1749 if (initialize_fio(envp))
1751 if (fio_init_options())
1754 init_ui(&argc, &argv, &ui);
1756 gdk_threads_enter();
1758 gdk_threads_leave();