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);
261 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
262 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
264 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
265 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
266 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
267 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
269 ui->log_model = model;
270 ui->log_tree = tree_view;
273 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
279 GType types[FIO_IO_U_LIST_MAX_LEN];
280 GtkWidget *tree_view;
281 GtkTreeSelection *selection;
286 for (i = 0; i < len; i++)
287 types[i] = G_TYPE_INT;
289 model = gtk_list_store_newv(len, types);
291 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
292 gtk_widget_set_can_focus(tree_view, FALSE);
294 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
295 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
297 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
298 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
300 for (i = 0; i < len; i++) {
303 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
304 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
307 gtk_list_store_append(model, &iter);
309 for (i = 0; i < len; i++) {
311 ovals[i] = (ovals[i] + 999) / 1000;
312 gtk_list_store_set(model, &iter, i, ovals[i], -1);
318 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
321 unsigned int *io_u_plat = ts->io_u_plat[ddir];
322 unsigned long nr = ts->clat_stat[ddir].samples;
323 fio_fp64_t *plist = ts->percentile_list;
324 unsigned int *ovals, len, minv, maxv, scale_down;
326 GtkWidget *tree_view, *frame, *hbox;
329 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
334 * We default to usecs, but if the value range is such that we
335 * should scale down to msecs, do that.
337 if (minv > 2000 && maxv > 99999) {
345 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
347 sprintf(tmp, "Completion percentiles (%s)", base);
348 frame = gtk_frame_new(tmp);
349 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
351 hbox = gtk_hbox_new(FALSE, 3);
352 gtk_container_add(GTK_CONTAINER(frame), hbox);
354 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
360 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
361 unsigned long max, double mean, double dev)
363 const char *base = "(usec)";
364 GtkWidget *hbox, *label, *frame;
368 if (!usec_to_msec(&min, &max, &mean, &dev))
371 minp = num2str(min, 6, 1, 0);
372 maxp = num2str(max, 6, 1, 0);
374 sprintf(tmp, "%s %s", name, base);
375 frame = gtk_frame_new(tmp);
376 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
378 hbox = gtk_hbox_new(FALSE, 3);
379 gtk_container_add(GTK_CONTAINER(frame), hbox);
381 label = new_info_label_in_frame(hbox, "Minimum");
382 gtk_label_set_text(GTK_LABEL(label), minp);
383 label = new_info_label_in_frame(hbox, "Maximum");
384 gtk_label_set_text(GTK_LABEL(label), maxp);
385 label = new_info_label_in_frame(hbox, "Average");
386 sprintf(tmp, "%5.02f", mean);
387 gtk_label_set_text(GTK_LABEL(label), tmp);
388 label = new_info_label_in_frame(hbox, "Standard deviation");
389 sprintf(tmp, "%5.02f", dev);
390 gtk_label_set_text(GTK_LABEL(label), tmp);
401 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
402 struct thread_stat *ts, int ddir)
404 const char *ddir_label[2] = { "Read", "Write" };
405 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
406 unsigned long min[3], max[3], runt;
407 unsigned long long bw, iops;
408 unsigned int flags = 0;
409 double mean[3], dev[3];
410 char *io_p, *bw_p, *iops_p;
413 if (!ts->runtime[ddir])
416 i2p = is_power_of_2(rs->kb_base);
417 runt = ts->runtime[ddir];
419 bw = (1000 * ts->io_bytes[ddir]) / runt;
420 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
421 bw_p = num2str(bw, 6, 1, i2p);
423 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
424 iops_p = num2str(iops, 6, 1, 0);
426 box = gtk_hbox_new(FALSE, 3);
427 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
429 frame = gtk_frame_new(ddir_label[ddir]);
430 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
432 main_vbox = gtk_vbox_new(FALSE, 3);
433 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
435 box = gtk_hbox_new(FALSE, 3);
436 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
438 label = new_info_label_in_frame(box, "IO");
439 gtk_label_set_text(GTK_LABEL(label), io_p);
440 label = new_info_label_in_frame(box, "Bandwidth");
441 gtk_label_set_text(GTK_LABEL(label), bw_p);
442 label = new_info_label_in_frame(box, "IOPS");
443 gtk_label_set_text(GTK_LABEL(label), iops_p);
444 label = new_info_label_in_frame(box, "Runtime (msec)");
445 label_set_int_value(label, ts->runtime[ddir]);
447 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
448 double p_of_agg = 100.0;
449 const char *bw_str = "KB";
453 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
454 if (p_of_agg > 100.0)
458 if (mean[0] > 999999.9) {
466 sprintf(tmp, "Bandwidth (%s)", bw_str);
467 frame = gtk_frame_new(tmp);
468 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
470 box = gtk_hbox_new(FALSE, 3);
471 gtk_container_add(GTK_CONTAINER(frame), box);
473 label = new_info_label_in_frame(box, "Minimum");
474 label_set_int_value(label, min[0]);
475 label = new_info_label_in_frame(box, "Maximum");
476 label_set_int_value(label, max[0]);
477 label = new_info_label_in_frame(box, "Percentage of jobs");
478 sprintf(tmp, "%3.2f%%", p_of_agg);
479 gtk_label_set_text(GTK_LABEL(label), tmp);
480 label = new_info_label_in_frame(box, "Average");
481 sprintf(tmp, "%5.02f", mean[0]);
482 gtk_label_set_text(GTK_LABEL(label), tmp);
483 label = new_info_label_in_frame(box, "Standard deviation");
484 sprintf(tmp, "%5.02f", dev[0]);
485 gtk_label_set_text(GTK_LABEL(label), tmp);
488 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
490 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
492 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
496 frame = gtk_frame_new("Latency");
497 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
499 vbox = gtk_vbox_new(FALSE, 3);
500 gtk_container_add(GTK_CONTAINER(frame), vbox);
502 if (flags & GFIO_SLAT)
503 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
504 if (flags & GFIO_CLAT)
505 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
506 if (flags & GFIO_LAT)
507 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
510 if (ts->clat_percentiles)
511 gfio_show_clat_percentiles(main_vbox, ts, ddir);
519 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
522 GtkWidget *tree_view;
523 GtkTreeSelection *selection;
530 * Check if all are empty, in which case don't bother
532 for (i = 0, skipped = 0; i < num; i++)
539 types = malloc(num * sizeof(GType));
541 for (i = 0; i < num; i++)
542 types[i] = G_TYPE_STRING;
544 model = gtk_list_store_newv(num, types);
548 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
549 gtk_widget_set_can_focus(tree_view, FALSE);
551 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
552 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
554 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
555 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
557 for (i = 0; i < num; i++)
558 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
560 gtk_list_store_append(model, &iter);
562 for (i = 0; i < num; i++) {
566 sprintf(fbuf, "0.00");
568 sprintf(fbuf, "%3.2f%%", lat[i]);
570 gtk_list_store_set(model, &iter, i, fbuf, -1);
576 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
578 GtkWidget *box, *frame, *tree_view;
579 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
580 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
581 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
582 "250", "500", "750", "1000", };
583 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
584 "250", "500", "750", "1000", "2000",
587 stat_calc_lat_u(ts, io_u_lat_u);
588 stat_calc_lat_m(ts, io_u_lat_m);
590 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
592 frame = gtk_frame_new("Latency buckets (usec)");
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);
600 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
602 frame = gtk_frame_new("Latency buckets (msec)");
603 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
605 box = gtk_hbox_new(FALSE, 3);
606 gtk_container_add(GTK_CONTAINER(frame), box);
607 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
611 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
613 GtkWidget *box, *frame, *entry;
614 double usr_cpu, sys_cpu;
615 unsigned long runtime;
618 runtime = ts->total_run_time;
620 double runt = (double) runtime;
622 usr_cpu = (double) ts->usr_time * 100 / runt;
623 sys_cpu = (double) ts->sys_time * 100 / runt;
629 frame = gtk_frame_new("OS resources");
630 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
632 box = gtk_hbox_new(FALSE, 3);
633 gtk_container_add(GTK_CONTAINER(frame), box);
635 entry = new_info_entry_in_frame(box, "User CPU");
636 sprintf(tmp, "%3.2f%%", usr_cpu);
637 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
638 entry = new_info_entry_in_frame(box, "System CPU");
639 sprintf(tmp, "%3.2f%%", sys_cpu);
640 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
641 entry = new_info_entry_in_frame(box, "Context switches");
642 entry_set_int_value(entry, ts->ctx);
643 entry = new_info_entry_in_frame(box, "Major faults");
644 entry_set_int_value(entry, ts->majf);
645 entry = new_info_entry_in_frame(box, "Minor faults");
646 entry_set_int_value(entry, ts->minf);
648 static void gfio_add_sc_depths_tree(GtkListStore *model,
649 struct thread_stat *ts, unsigned int len,
652 double io_u_dist[FIO_IO_U_MAP_NR];
654 /* Bits 0, and 3-8 */
655 const int add_mask = 0x1f9;
659 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
661 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
663 gtk_list_store_append(model, &iter);
665 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
667 for (i = 1, j = 0; i < len; i++) {
670 if (!(add_mask & (1UL << (i - 1))))
671 sprintf(fbuf, "0.0%%");
673 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
677 gtk_list_store_set(model, &iter, i, fbuf, -1);
682 static void gfio_add_total_depths_tree(GtkListStore *model,
683 struct thread_stat *ts, unsigned int len)
685 double io_u_dist[FIO_IO_U_MAP_NR];
687 /* Bits 1-6, and 8 */
688 const int add_mask = 0x17e;
691 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
693 gtk_list_store_append(model, &iter);
695 gtk_list_store_set(model, &iter, 0, "Total", -1);
697 for (i = 1, j = 0; i < len; i++) {
700 if (!(add_mask & (1UL << (i - 1))))
701 sprintf(fbuf, "0.0%%");
703 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
707 gtk_list_store_set(model, &iter, i, fbuf, -1);
712 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
714 GtkWidget *frame, *box, *tree_view;
715 GtkTreeSelection *selection;
717 GType types[FIO_IO_U_MAP_NR + 1];
720 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
722 frame = gtk_frame_new("IO depths");
723 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
725 box = gtk_hbox_new(FALSE, 3);
726 gtk_container_add(GTK_CONTAINER(frame), box);
728 for (i = 0; i < NR_LABELS; i++)
729 types[i] = G_TYPE_STRING;
731 model = gtk_list_store_newv(NR_LABELS, types);
733 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
734 gtk_widget_set_can_focus(tree_view, FALSE);
736 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
737 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
739 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
740 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
742 for (i = 0; i < NR_LABELS; i++)
743 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
745 gfio_add_total_depths_tree(model, ts, NR_LABELS);
746 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
747 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
749 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
752 static gboolean results_window_delete(GtkWidget *w, gpointer data)
754 struct gui *ui = (struct gui *) data;
756 gtk_widget_destroy(w);
757 ui->results_window = NULL;
758 ui->results_notebook = NULL;
762 static GtkWidget *get_results_window(struct gui *ui)
764 GtkWidget *win, *notebook;
766 if (ui->results_window)
767 return ui->results_notebook;
769 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
770 gtk_window_set_title(GTK_WINDOW(win), "Results");
771 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
772 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
774 notebook = gtk_notebook_new();
775 gtk_container_add(GTK_CONTAINER(win), notebook);
777 ui->results_window = win;
778 ui->results_notebook = notebook;
779 return ui->results_notebook;
782 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
783 struct group_run_stats *rs)
785 GtkWidget *res_win, *box, *vbox, *entry;
786 struct gfio_client *gc = client->client_data;
790 res_win = get_results_window(gc->ui);
792 vbox = gtk_vbox_new(FALSE, 3);
794 box = gtk_hbox_new(TRUE, 3);
795 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
797 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
799 gc->results_widget = vbox;
801 entry = new_info_entry_in_frame(box, "Name");
802 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
803 if (strlen(ts->description)) {
804 entry = new_info_entry_in_frame(box, "Description");
805 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
807 entry = new_info_entry_in_frame(box, "Group ID");
808 entry_set_int_value(entry, ts->groupid);
809 entry = new_info_entry_in_frame(box, "Jobs");
810 entry_set_int_value(entry, ts->members);
811 entry = new_info_entry_in_frame(box, "Error");
812 entry_set_int_value(entry, ts->error);
813 entry = new_info_entry_in_frame(box, "PID");
814 entry_set_int_value(entry, ts->pid);
816 if (ts->io_bytes[DDIR_READ])
817 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
818 if (ts->io_bytes[DDIR_WRITE])
819 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
821 gfio_show_latency_buckets(vbox, ts);
822 gfio_show_cpu_usage(vbox, ts);
823 gfio_show_io_depths(vbox, ts);
825 gtk_widget_show_all(gc->ui->results_window);
829 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
831 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
832 struct gfio_client *gc = client->client_data;
836 char tmp[64], timebuf[80];
839 tm = localtime(&sec);
840 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
841 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
845 gtk_list_store_append(gc->ui->log_model, &iter);
846 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
847 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
848 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
849 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
854 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
856 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
857 struct gfio_client *gc = client->client_data;
858 GtkWidget *box, *frame, *entry, *vbox;
862 if (!gc->results_widget) {
863 printf("no results!\n");
867 if (!gc->disk_util_frame) {
868 gc->disk_util_frame = gtk_frame_new("Disk utilization");
869 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
872 vbox = gtk_vbox_new(FALSE, 3);
873 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
875 frame = gtk_frame_new((char *) p->dus.name);
876 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
878 box = gtk_vbox_new(FALSE, 3);
879 gtk_container_add(GTK_CONTAINER(frame), box);
881 frame = gtk_frame_new("Read");
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[0]);
887 entry = new_info_entry_in_frame(vbox, "Merges");
888 entry_set_int_value(entry, p->dus.merges[0]);
889 entry = new_info_entry_in_frame(vbox, "Sectors");
890 entry_set_int_value(entry, p->dus.sectors[0]);
891 entry = new_info_entry_in_frame(vbox, "Ticks");
892 entry_set_int_value(entry, p->dus.ticks[0]);
894 frame = gtk_frame_new("Write");
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, "IOs");
899 entry_set_int_value(entry, p->dus.ios[1]);
900 entry = new_info_entry_in_frame(vbox, "Merges");
901 entry_set_int_value(entry, p->dus.merges[1]);
902 entry = new_info_entry_in_frame(vbox, "Sectors");
903 entry_set_int_value(entry, p->dus.sectors[1]);
904 entry = new_info_entry_in_frame(vbox, "Ticks");
905 entry_set_int_value(entry, p->dus.ticks[1]);
907 frame = gtk_frame_new("Shared");
908 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
909 vbox = gtk_hbox_new(TRUE, 3);
910 gtk_container_add(GTK_CONTAINER(frame), vbox);
911 entry = new_info_entry_in_frame(vbox, "IO ticks");
912 entry_set_int_value(entry, p->dus.io_ticks);
913 entry = new_info_entry_in_frame(vbox, "Time in queue");
914 entry_set_int_value(entry, p->dus.time_in_queue);
916 gtk_widget_show_all(gc->results_widget);
921 extern int sum_stat_clients;
922 extern struct thread_stat client_ts;
923 extern struct group_run_stats client_gs;
925 static int sum_stat_nr;
927 static void gfio_thread_status_op(struct fio_client *client,
928 struct fio_net_cmd *cmd)
930 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
932 gfio_display_ts(client, &p->ts, &p->rs);
934 if (sum_stat_clients == 1)
937 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
938 sum_group_stats(&client_gs, &p->rs);
941 client_ts.groupid = p->ts.groupid;
943 if (++sum_stat_nr == sum_stat_clients) {
944 strcpy(client_ts.name, "All clients");
945 gfio_display_ts(client, &client_ts, &client_gs);
949 static void gfio_group_stats_op(struct fio_client *client,
950 struct fio_net_cmd *cmd)
953 printf("gfio_group_stats_op called\n");
954 fio_client_ops.group_stats(client, cmd);
958 static void gfio_update_eta(struct jobs_eta *je)
972 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
973 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
974 eta_to_str(eta_str, je->eta_sec);
977 sprintf(tmp, "%u", je->nr_running);
978 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
979 sprintf(tmp, "%u", je->files_open);
980 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
983 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
984 if (je->m_rate || je->t_rate) {
987 mr = num2str(je->m_rate, 4, 0, i2p);
988 tr = num2str(je->t_rate, 4, 0, i2p);
989 gtk_entry_set_text(GTK_ENTRY(ui.eta);
990 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
993 } else if (je->m_iops || je->t_iops)
994 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
996 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
997 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
998 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
999 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
1002 if (je->eta_sec != INT_MAX && je->nr_running) {
1006 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1007 strcpy(output, "-.-% done");
1011 sprintf(output, "%3.1f%% done", perc);
1014 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1015 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1017 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1018 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1020 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1021 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1022 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1023 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1032 char *dst = output + strlen(output);
1034 sprintf(dst, " - %s", eta_str);
1037 gfio_update_thread_status(output, perc);
1038 gdk_threads_leave();
1041 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1043 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1044 struct gfio_client *gc = client->client_data;
1045 struct gui *ui = gc->ui;
1046 const char *os, *arch;
1049 os = fio_get_os_string(probe->os);
1053 arch = fio_get_arch_string(probe->arch);
1058 client->name = strdup((char *) probe->hostname);
1060 gdk_threads_enter();
1062 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1063 gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1064 gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1065 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1066 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1068 gfio_set_connected(ui, 1);
1070 gdk_threads_leave();
1073 static void gfio_update_thread_status(char *status_message, double perc)
1075 static char message[100];
1076 const char *m = message;
1078 strncpy(message, status_message, sizeof(message) - 1);
1079 gtk_progress_bar_set_text(
1080 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1081 gtk_progress_bar_set_fraction(
1082 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1083 gtk_widget_queue_draw(ui.window);
1086 static void gfio_quit_op(struct fio_client *client)
1088 struct gfio_client *gc = client->client_data;
1090 gdk_threads_enter();
1091 gfio_set_connected(gc->ui, 0);
1092 gdk_threads_leave();
1095 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1097 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1098 struct gfio_client *gc = client->client_data;
1099 struct gui *ui = gc->ui;
1103 p->iodepth = le32_to_cpu(p->iodepth);
1104 p->rw = le32_to_cpu(p->rw);
1106 for (i = 0; i < 2; i++) {
1107 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
1108 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
1111 p->numjobs = le32_to_cpu(p->numjobs);
1112 p->group_reporting = le32_to_cpu(p->group_reporting);
1114 gdk_threads_enter();
1116 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1117 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1118 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1120 sprintf(tmp, "%u", p->iodepth);
1121 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1123 gdk_threads_leave();
1126 static void gfio_client_timed_out(struct fio_client *client)
1128 struct gfio_client *gc = client->client_data;
1129 GtkWidget *dialog, *label, *content;
1132 gdk_threads_enter();
1134 gfio_set_connected(gc->ui, 0);
1135 clear_ui_info(gc->ui);
1137 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1139 dialog = gtk_dialog_new_with_buttons("Timed out!",
1140 GTK_WINDOW(gc->ui->window),
1141 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1142 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1144 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1145 label = gtk_label_new((const gchar *) buf);
1146 gtk_container_add(GTK_CONTAINER(content), label);
1147 gtk_widget_show_all(dialog);
1148 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1150 gtk_dialog_run(GTK_DIALOG(dialog));
1151 gtk_widget_destroy(dialog);
1153 gdk_threads_leave();
1156 struct client_ops gfio_client_ops = {
1157 .text_op = gfio_text_op,
1158 .disk_util = gfio_disk_util_op,
1159 .thread_status = gfio_thread_status_op,
1160 .group_stats = gfio_group_stats_op,
1161 .eta = gfio_update_eta,
1162 .probe = gfio_probe_op,
1163 .quit = gfio_quit_op,
1164 .add_job = gfio_add_job_op,
1165 .timed_out = gfio_client_timed_out,
1166 .stay_connected = 1,
1169 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1170 __attribute__((unused)) gpointer data)
1175 static void *job_thread(void *arg)
1177 fio_handle_clients(&gfio_client_ops);
1181 static int send_job_files(struct gui *ui)
1185 for (i = 0; i < ui->nr_job_files; i++) {
1186 ret = fio_clients_send_ini(ui->job_files[i]);
1190 free(ui->job_files[i]);
1191 ui->job_files[i] = NULL;
1193 while (i < ui->nr_job_files) {
1194 free(ui->job_files[i]);
1195 ui->job_files[i] = NULL;
1202 static void start_job_thread(struct gui *ui)
1204 if (send_job_files(ui)) {
1205 printf("Yeah, I didn't really like those options too much.\n");
1206 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1211 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1214 struct gui *ui = data;
1216 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1217 start_job_thread(ui);
1220 static void file_open(GtkWidget *w, gpointer data);
1222 static void connect_clicked(GtkWidget *widget, gpointer data)
1224 struct gui *ui = data;
1226 if (!ui->connected) {
1227 if (!ui->nr_job_files)
1228 file_open(widget, data);
1229 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1230 fio_clients_connect();
1231 pthread_create(&ui->t, NULL, job_thread, NULL);
1232 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1234 fio_clients_terminate();
1235 gfio_set_connected(ui, 0);
1240 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1241 struct button_spec *buttonspec)
1243 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1244 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1245 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1246 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1247 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1250 static void add_buttons(struct gui *ui,
1251 struct button_spec *buttonlist,
1256 for (i = 0; i < nbuttons; i++)
1257 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1260 static void on_info_bar_response(GtkWidget *widget, gint response,
1263 if (response == GTK_RESPONSE_OK) {
1264 gtk_widget_destroy(widget);
1265 ui.error_info_bar = NULL;
1269 void report_error(GError *error)
1271 if (ui.error_info_bar == NULL) {
1272 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1275 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1276 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1279 ui.error_label = gtk_label_new(error->message);
1280 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1281 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1283 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1284 gtk_widget_show_all(ui.vbox);
1287 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1288 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1292 static int get_connection_details(char **host, int *port, int *type,
1295 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1299 dialog = gtk_dialog_new_with_buttons("Connection details",
1300 GTK_WINDOW(ui.window),
1301 GTK_DIALOG_DESTROY_WITH_PARENT,
1302 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1303 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1305 frame = gtk_frame_new("Hostname / socket name");
1306 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1307 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1309 box = gtk_vbox_new(FALSE, 6);
1310 gtk_container_add(GTK_CONTAINER(frame), box);
1312 hbox = gtk_hbox_new(TRUE, 10);
1313 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1314 hentry = gtk_entry_new();
1315 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1316 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1318 frame = gtk_frame_new("Port");
1319 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1320 box = gtk_vbox_new(FALSE, 10);
1321 gtk_container_add(GTK_CONTAINER(frame), box);
1323 hbox = gtk_hbox_new(TRUE, 4);
1324 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1325 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1327 frame = gtk_frame_new("Type");
1328 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1329 box = gtk_vbox_new(FALSE, 10);
1330 gtk_container_add(GTK_CONTAINER(frame), box);
1332 hbox = gtk_hbox_new(TRUE, 4);
1333 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1335 combo = gtk_combo_box_new_text();
1336 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1337 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1338 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
1339 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1341 gtk_container_add(GTK_CONTAINER(hbox), combo);
1343 frame = gtk_frame_new("Options");
1344 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1345 box = gtk_vbox_new(FALSE, 10);
1346 gtk_container_add(GTK_CONTAINER(frame), box);
1348 hbox = gtk_hbox_new(TRUE, 4);
1349 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1351 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1352 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1353 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.");
1354 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1356 gtk_widget_show_all(dialog);
1358 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1359 gtk_widget_destroy(dialog);
1363 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1364 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1366 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1367 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1368 *type = Fio_client_ipv4;
1369 else if (!strncmp(typeentry, "IPv6", 4))
1370 *type = Fio_client_ipv6;
1372 *type = Fio_client_socket;
1375 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1377 gtk_widget_destroy(dialog);
1381 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1383 struct gfio_client *gc;
1385 gc = malloc(sizeof(*gc));
1386 memset(gc, 0, sizeof(*gc));
1389 client->client_data = gc;
1392 static void file_open(GtkWidget *w, gpointer data)
1395 GSList *filenames, *fn_glist;
1396 GtkFileFilter *filter;
1398 int port, type, server_start;
1400 dialog = gtk_file_chooser_dialog_new("Open File",
1401 GTK_WINDOW(ui.window),
1402 GTK_FILE_CHOOSER_ACTION_OPEN,
1403 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1404 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1406 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1408 filter = gtk_file_filter_new();
1409 gtk_file_filter_add_pattern(filter, "*.fio");
1410 gtk_file_filter_add_pattern(filter, "*.job");
1411 gtk_file_filter_add_mime_type(filter, "text/fio");
1412 gtk_file_filter_set_name(filter, "Fio job file");
1413 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1415 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1416 gtk_widget_destroy(dialog);
1420 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1422 gtk_widget_destroy(dialog);
1424 if (get_connection_details(&host, &port, &type, &server_start))
1427 filenames = fn_glist;
1428 while (filenames != NULL) {
1429 struct fio_client *client;
1431 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1432 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1435 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1439 error = g_error_new(g_quark_from_string("fio"), 1,
1440 "Failed to add client %s", host);
1441 report_error(error);
1442 g_error_free(error);
1444 gfio_client_added(&ui, client);
1446 g_free(filenames->data);
1447 filenames = g_slist_next(filenames);
1451 g_slist_free(fn_glist);
1454 static void file_save(GtkWidget *w, gpointer data)
1458 dialog = gtk_file_chooser_dialog_new("Save File",
1459 GTK_WINDOW(ui.window),
1460 GTK_FILE_CHOOSER_ACTION_SAVE,
1461 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1462 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1465 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1466 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1468 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1471 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1472 // save_job_file(filename);
1475 gtk_widget_destroy(dialog);
1478 static void view_log_destroy(GtkWidget *w, gpointer data)
1480 struct gui *ui = (struct gui *) data;
1482 gtk_widget_ref(ui->log_tree);
1483 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1484 gtk_widget_destroy(w);
1485 ui->log_view = NULL;
1488 static void view_log(GtkWidget *w, gpointer data)
1490 GtkWidget *win, *scroll, *vbox, *box;
1491 struct gui *ui = (struct gui *) data;
1496 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1497 gtk_window_set_title(GTK_WINDOW(win), "Log");
1498 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1500 scroll = gtk_scrolled_window_new(NULL, NULL);
1502 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1504 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1506 box = gtk_hbox_new(TRUE, 0);
1507 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1508 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1509 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1511 vbox = gtk_vbox_new(TRUE, 5);
1512 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1514 gtk_container_add(GTK_CONTAINER(win), vbox);
1515 gtk_widget_show_all(win);
1518 static void preferences(GtkWidget *w, gpointer data)
1520 GtkWidget *dialog, *frame, *box, **buttons;
1523 dialog = gtk_dialog_new_with_buttons("Preferences",
1524 GTK_WINDOW(ui.window),
1525 GTK_DIALOG_DESTROY_WITH_PARENT,
1526 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1527 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1530 frame = gtk_frame_new("Debug logging");
1531 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1532 box = gtk_hbox_new(FALSE, 6);
1533 gtk_container_add(GTK_CONTAINER(frame), box);
1535 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1537 for (i = 0; i < FD_DEBUG_MAX; i++) {
1538 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1539 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1540 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1543 gtk_widget_show_all(dialog);
1545 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1546 gtk_widget_destroy(dialog);
1550 for (i = 0; i < FD_DEBUG_MAX; i++) {
1553 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1555 fio_debug |= (1UL << i);
1558 gtk_widget_destroy(dialog);
1561 static void about_dialog(GtkWidget *w, gpointer data)
1563 gtk_show_about_dialog(NULL,
1564 "program-name", "gfio",
1565 "comments", "Gtk2 UI for fio",
1567 "version", fio_version_string,
1568 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1569 "logo-icon-name", "fio",
1575 static GtkActionEntry menu_items[] = {
1576 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1577 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1578 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1579 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1580 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1581 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1582 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1583 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1584 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1586 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1588 static const gchar *ui_string = " \
1590 <menubar name=\"MainMenu\"> \
1591 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1592 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1593 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1594 <separator name=\"Separator\"/> \
1595 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1596 <separator name=\"Separator2\"/> \
1597 <menuitem name=\"Quit\" action=\"Quit\" /> \
1599 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1600 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1602 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1603 <menuitem name=\"About\" action=\"About\" /> \
1609 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1612 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1615 action_group = gtk_action_group_new("Menu");
1616 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1618 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1619 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1621 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1622 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1625 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1626 GtkWidget *vbox, GtkUIManager *ui_manager)
1628 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1631 static void init_ui(int *argc, char **argv[], struct gui *ui)
1633 GtkSettings *settings;
1634 GtkUIManager *uimanager;
1635 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1637 memset(ui, 0, sizeof(*ui));
1639 /* Magical g*thread incantation, you just need this thread stuff.
1640 * Without it, the update that happens in gfio_update_thread_status
1641 * doesn't really happen in a timely fashion, you need expose events
1643 if (!g_thread_supported())
1644 g_thread_init(NULL);
1647 gtk_init(argc, argv);
1648 settings = gtk_settings_get_default();
1649 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1652 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1653 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1654 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1656 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1657 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1659 ui->vbox = gtk_vbox_new(FALSE, 0);
1660 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1662 uimanager = gtk_ui_manager_new();
1663 menu = get_menubar_menu(ui->window, uimanager, ui);
1664 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1667 * Set up alignments for widgets at the top of ui,
1668 * align top left, expand horizontally but not vertically
1670 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1671 ui->topvbox = gtk_vbox_new(FALSE, 3);
1672 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1673 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1675 probe = gtk_frame_new("Job");
1676 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1677 probe_frame = gtk_vbox_new(FALSE, 3);
1678 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1680 probe_box = gtk_hbox_new(FALSE, 3);
1681 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1682 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1683 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1684 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1685 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1687 probe_box = gtk_hbox_new(FALSE, 3);
1688 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1690 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1691 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1692 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1693 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1694 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1695 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1697 probe_box = gtk_hbox_new(FALSE, 3);
1698 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1699 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1700 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1701 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1702 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1705 * Only add this if we have a commit rate
1708 probe_box = gtk_hbox_new(FALSE, 3);
1709 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1711 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1712 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1714 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1715 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1719 * Add a text box for text op messages
1721 ui->textview = gtk_text_view_new();
1722 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1723 gtk_text_buffer_set_text(ui->text, "", -1);
1724 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1725 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1726 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1727 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1728 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1729 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1730 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1734 * Set up alignments for widgets at the bottom of ui,
1735 * align bottom left, expand horizontally but not vertically
1737 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1738 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1739 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1740 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1743 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1746 * Set up thread status progress bar
1748 ui->thread_status_pb = gtk_progress_bar_new();
1749 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1750 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1751 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1753 gfio_ui_setup_log(ui);
1755 gtk_widget_show_all(ui->window);
1758 int main(int argc, char *argv[], char *envp[])
1760 if (initialize_fio(envp))
1762 if (fio_init_options())
1765 init_ui(&argc, &argv, &ui);
1767 gdk_threads_enter();
1769 gdk_threads_leave();