2 * gfio - gui front end for fio - the flexible io tester
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
5 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 static int gfio_server_running;
34 static void gfio_update_thread_status(char *status_message, double perc);
36 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
38 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
40 static void connect_clicked(GtkWidget *widget, gpointer data);
41 static void start_job_clicked(GtkWidget *widget, gpointer data);
43 static struct button_spec {
44 const char *buttontext;
46 const char *tooltiptext;
47 const int start_insensitive;
48 } buttonspeclist[] = {
49 #define CONNECT_BUTTON 0
50 #define START_JOB_BUTTON 1
51 { "Connect", connect_clicked, "Connect to host", 0 },
54 "Send current fio job to fio server to be executed", 1 },
76 GtkWidget *write_iops;
86 GtkWidget *bottomalign;
87 GtkWidget *thread_status_pb;
89 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
90 GtkWidget *scrolled_window;
92 GtkWidget *error_info_bar;
93 GtkWidget *error_label;
94 GtkWidget *results_notebook;
95 GtkWidget *results_window;
96 GtkListStore *log_model;
100 struct probe_widget probe;
101 struct eta_widget eta;
106 struct fio_client *client;
113 GtkWidget *results_widget;
114 GtkWidget *disk_util_frame;
117 static void clear_ui_info(struct gui *ui)
119 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
120 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
121 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
122 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
123 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
124 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
125 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
126 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
127 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
128 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
129 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
130 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
131 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
132 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
135 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
137 GtkWidget *entry, *frame;
139 frame = gtk_frame_new(label);
140 entry = gtk_entry_new();
141 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
142 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
143 gtk_container_add(GTK_CONTAINER(frame), entry);
148 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
150 GtkWidget *label_widget;
153 frame = gtk_frame_new(label);
154 label_widget = gtk_label_new(NULL);
155 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
156 gtk_container_add(GTK_CONTAINER(frame), label_widget);
161 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
163 GtkWidget *button, *box;
165 box = gtk_hbox_new(FALSE, 3);
166 gtk_container_add(GTK_CONTAINER(hbox), box);
168 button = gtk_spin_button_new_with_range(min, max, 1.0);
169 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
171 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
172 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
177 static void gfio_set_connected(struct gui *ui, int connected)
180 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
182 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
183 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
186 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
187 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
188 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
192 static void label_set_int_value(GtkWidget *entry, unsigned int val)
196 sprintf(tmp, "%u", val);
197 gtk_label_set_text(GTK_LABEL(entry), tmp);
200 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
204 sprintf(tmp, "%u", val);
205 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
209 #define ALIGN_RIGHT 2
213 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
215 GtkCellRenderer *renderer;
216 GtkTreeViewColumn *col;
217 double xalign = 0.0; /* left as default */
218 PangoAlignment align;
221 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
222 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
224 visible = !(flags & INVISIBLE);
226 renderer = gtk_cell_renderer_text_new();
227 col = gtk_tree_view_column_new();
229 gtk_tree_view_column_set_title(col, title);
230 if (!(flags & UNSORTABLE))
231 gtk_tree_view_column_set_sort_column_id(col, index);
232 gtk_tree_view_column_set_resizable(col, TRUE);
233 gtk_tree_view_column_pack_start(col, renderer, TRUE);
234 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
235 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
237 case PANGO_ALIGN_LEFT:
240 case PANGO_ALIGN_CENTER:
243 case PANGO_ALIGN_RIGHT:
247 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
248 gtk_tree_view_column_set_visible(col, visible);
249 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
253 static void gfio_ui_setup_log(struct gui *ui)
255 GtkTreeSelection *selection;
257 GtkWidget *tree_view;
259 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
261 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
262 gtk_widget_set_can_focus(tree_view, FALSE);
264 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
265 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
266 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
267 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
269 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
270 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
271 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
272 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
274 ui->log_model = model;
275 ui->log_tree = tree_view;
278 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
284 GType types[FIO_IO_U_LIST_MAX_LEN];
285 GtkWidget *tree_view;
286 GtkTreeSelection *selection;
291 for (i = 0; i < len; i++)
292 types[i] = G_TYPE_INT;
294 model = gtk_list_store_newv(len, types);
296 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
297 gtk_widget_set_can_focus(tree_view, FALSE);
299 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
300 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
302 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
303 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
305 for (i = 0; i < len; i++) {
308 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
309 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
312 gtk_list_store_append(model, &iter);
314 for (i = 0; i < len; i++) {
316 ovals[i] = (ovals[i] + 999) / 1000;
317 gtk_list_store_set(model, &iter, i, ovals[i], -1);
323 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
326 unsigned int *io_u_plat = ts->io_u_plat[ddir];
327 unsigned long nr = ts->clat_stat[ddir].samples;
328 fio_fp64_t *plist = ts->percentile_list;
329 unsigned int *ovals, len, minv, maxv, scale_down;
331 GtkWidget *tree_view, *frame, *hbox;
334 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
339 * We default to usecs, but if the value range is such that we
340 * should scale down to msecs, do that.
342 if (minv > 2000 && maxv > 99999) {
350 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
352 sprintf(tmp, "Completion percentiles (%s)", base);
353 frame = gtk_frame_new(tmp);
354 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
356 hbox = gtk_hbox_new(FALSE, 3);
357 gtk_container_add(GTK_CONTAINER(frame), hbox);
359 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
365 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
366 unsigned long max, double mean, double dev)
368 const char *base = "(usec)";
369 GtkWidget *hbox, *label, *frame;
373 if (!usec_to_msec(&min, &max, &mean, &dev))
376 minp = num2str(min, 6, 1, 0);
377 maxp = num2str(max, 6, 1, 0);
379 sprintf(tmp, "%s %s", name, base);
380 frame = gtk_frame_new(tmp);
381 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
383 hbox = gtk_hbox_new(FALSE, 3);
384 gtk_container_add(GTK_CONTAINER(frame), hbox);
386 label = new_info_label_in_frame(hbox, "Minimum");
387 gtk_label_set_text(GTK_LABEL(label), minp);
388 label = new_info_label_in_frame(hbox, "Maximum");
389 gtk_label_set_text(GTK_LABEL(label), maxp);
390 label = new_info_label_in_frame(hbox, "Average");
391 sprintf(tmp, "%5.02f", mean);
392 gtk_label_set_text(GTK_LABEL(label), tmp);
393 label = new_info_label_in_frame(hbox, "Standard deviation");
394 sprintf(tmp, "%5.02f", dev);
395 gtk_label_set_text(GTK_LABEL(label), tmp);
406 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
407 struct thread_stat *ts, int ddir)
409 const char *ddir_label[2] = { "Read", "Write" };
410 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
411 unsigned long min[3], max[3], runt;
412 unsigned long long bw, iops;
413 unsigned int flags = 0;
414 double mean[3], dev[3];
415 char *io_p, *bw_p, *iops_p;
418 if (!ts->runtime[ddir])
421 i2p = is_power_of_2(rs->kb_base);
422 runt = ts->runtime[ddir];
424 bw = (1000 * ts->io_bytes[ddir]) / runt;
425 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
426 bw_p = num2str(bw, 6, 1, i2p);
428 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
429 iops_p = num2str(iops, 6, 1, 0);
431 box = gtk_hbox_new(FALSE, 3);
432 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
434 frame = gtk_frame_new(ddir_label[ddir]);
435 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
437 main_vbox = gtk_vbox_new(FALSE, 3);
438 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
440 box = gtk_hbox_new(FALSE, 3);
441 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
443 label = new_info_label_in_frame(box, "IO");
444 gtk_label_set_text(GTK_LABEL(label), io_p);
445 label = new_info_label_in_frame(box, "Bandwidth");
446 gtk_label_set_text(GTK_LABEL(label), bw_p);
447 label = new_info_label_in_frame(box, "IOPS");
448 gtk_label_set_text(GTK_LABEL(label), iops_p);
449 label = new_info_label_in_frame(box, "Runtime (msec)");
450 label_set_int_value(label, ts->runtime[ddir]);
452 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
453 double p_of_agg = 100.0;
454 const char *bw_str = "KB";
458 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
459 if (p_of_agg > 100.0)
463 if (mean[0] > 999999.9) {
471 sprintf(tmp, "Bandwidth (%s)", bw_str);
472 frame = gtk_frame_new(tmp);
473 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
475 box = gtk_hbox_new(FALSE, 3);
476 gtk_container_add(GTK_CONTAINER(frame), box);
478 label = new_info_label_in_frame(box, "Minimum");
479 label_set_int_value(label, min[0]);
480 label = new_info_label_in_frame(box, "Maximum");
481 label_set_int_value(label, max[0]);
482 label = new_info_label_in_frame(box, "Percentage of jobs");
483 sprintf(tmp, "%3.2f%%", p_of_agg);
484 gtk_label_set_text(GTK_LABEL(label), tmp);
485 label = new_info_label_in_frame(box, "Average");
486 sprintf(tmp, "%5.02f", mean[0]);
487 gtk_label_set_text(GTK_LABEL(label), tmp);
488 label = new_info_label_in_frame(box, "Standard deviation");
489 sprintf(tmp, "%5.02f", dev[0]);
490 gtk_label_set_text(GTK_LABEL(label), tmp);
493 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
495 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
497 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
501 frame = gtk_frame_new("Latency");
502 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
504 vbox = gtk_vbox_new(FALSE, 3);
505 gtk_container_add(GTK_CONTAINER(frame), vbox);
507 if (flags & GFIO_SLAT)
508 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
509 if (flags & GFIO_CLAT)
510 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
511 if (flags & GFIO_LAT)
512 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
515 if (ts->clat_percentiles)
516 gfio_show_clat_percentiles(main_vbox, ts, ddir);
524 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
527 GtkWidget *tree_view;
528 GtkTreeSelection *selection;
535 * Check if all are empty, in which case don't bother
537 for (i = 0, skipped = 0; i < num; i++)
544 types = malloc(num * sizeof(GType));
546 for (i = 0; i < num; i++)
547 types[i] = G_TYPE_STRING;
549 model = gtk_list_store_newv(num, types);
553 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
554 gtk_widget_set_can_focus(tree_view, FALSE);
556 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
557 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
559 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
560 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
562 for (i = 0; i < num; i++)
563 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
565 gtk_list_store_append(model, &iter);
567 for (i = 0; i < num; i++) {
571 sprintf(fbuf, "0.00");
573 sprintf(fbuf, "%3.2f%%", lat[i]);
575 gtk_list_store_set(model, &iter, i, fbuf, -1);
581 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
583 GtkWidget *box, *frame, *tree_view;
584 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
585 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
586 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
587 "250", "500", "750", "1000", };
588 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
589 "250", "500", "750", "1000", "2000",
592 stat_calc_lat_u(ts, io_u_lat_u);
593 stat_calc_lat_m(ts, io_u_lat_m);
595 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
597 frame = gtk_frame_new("Latency buckets (usec)");
598 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
600 box = gtk_hbox_new(FALSE, 3);
601 gtk_container_add(GTK_CONTAINER(frame), box);
602 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
605 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
607 frame = gtk_frame_new("Latency buckets (msec)");
608 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
610 box = gtk_hbox_new(FALSE, 3);
611 gtk_container_add(GTK_CONTAINER(frame), box);
612 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
616 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
618 GtkWidget *box, *frame, *entry;
619 double usr_cpu, sys_cpu;
620 unsigned long runtime;
623 runtime = ts->total_run_time;
625 double runt = (double) runtime;
627 usr_cpu = (double) ts->usr_time * 100 / runt;
628 sys_cpu = (double) ts->sys_time * 100 / runt;
634 frame = gtk_frame_new("OS resources");
635 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
637 box = gtk_hbox_new(FALSE, 3);
638 gtk_container_add(GTK_CONTAINER(frame), box);
640 entry = new_info_entry_in_frame(box, "User CPU");
641 sprintf(tmp, "%3.2f%%", usr_cpu);
642 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
643 entry = new_info_entry_in_frame(box, "System CPU");
644 sprintf(tmp, "%3.2f%%", sys_cpu);
645 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
646 entry = new_info_entry_in_frame(box, "Context switches");
647 entry_set_int_value(entry, ts->ctx);
648 entry = new_info_entry_in_frame(box, "Major faults");
649 entry_set_int_value(entry, ts->majf);
650 entry = new_info_entry_in_frame(box, "Minor faults");
651 entry_set_int_value(entry, ts->minf);
653 static void gfio_add_sc_depths_tree(GtkListStore *model,
654 struct thread_stat *ts, unsigned int len,
657 double io_u_dist[FIO_IO_U_MAP_NR];
659 /* Bits 0, and 3-8 */
660 const int add_mask = 0x1f9;
664 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
666 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
668 gtk_list_store_append(model, &iter);
670 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
672 for (i = 1, j = 0; i < len; i++) {
675 if (!(add_mask & (1UL << (i - 1))))
676 sprintf(fbuf, "0.0%%");
678 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
682 gtk_list_store_set(model, &iter, i, fbuf, -1);
687 static void gfio_add_total_depths_tree(GtkListStore *model,
688 struct thread_stat *ts, unsigned int len)
690 double io_u_dist[FIO_IO_U_MAP_NR];
692 /* Bits 1-6, and 8 */
693 const int add_mask = 0x17e;
696 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
698 gtk_list_store_append(model, &iter);
700 gtk_list_store_set(model, &iter, 0, "Total", -1);
702 for (i = 1, j = 0; i < len; i++) {
705 if (!(add_mask & (1UL << (i - 1))))
706 sprintf(fbuf, "0.0%%");
708 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
712 gtk_list_store_set(model, &iter, i, fbuf, -1);
717 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
719 GtkWidget *frame, *box, *tree_view;
720 GtkTreeSelection *selection;
722 GType types[FIO_IO_U_MAP_NR + 1];
725 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
727 frame = gtk_frame_new("IO depths");
728 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
730 box = gtk_hbox_new(FALSE, 3);
731 gtk_container_add(GTK_CONTAINER(frame), box);
733 for (i = 0; i < NR_LABELS; i++)
734 types[i] = G_TYPE_STRING;
736 model = gtk_list_store_newv(NR_LABELS, types);
738 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
739 gtk_widget_set_can_focus(tree_view, FALSE);
741 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
742 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
744 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
745 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
747 for (i = 0; i < NR_LABELS; i++)
748 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
750 gfio_add_total_depths_tree(model, ts, NR_LABELS);
751 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
752 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
754 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
757 static gboolean results_window_delete(GtkWidget *w, gpointer data)
759 struct gui *ui = (struct gui *) data;
761 gtk_widget_destroy(w);
762 ui->results_window = NULL;
763 ui->results_notebook = NULL;
767 static GtkWidget *get_results_window(struct gui *ui)
769 GtkWidget *win, *notebook;
771 if (ui->results_window)
772 return ui->results_notebook;
774 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
775 gtk_window_set_title(GTK_WINDOW(win), "Results");
776 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
777 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
779 notebook = gtk_notebook_new();
780 gtk_container_add(GTK_CONTAINER(win), notebook);
782 ui->results_window = win;
783 ui->results_notebook = notebook;
784 return ui->results_notebook;
787 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
788 struct group_run_stats *rs)
790 GtkWidget *res_win, *box, *vbox, *entry;
791 struct gfio_client *gc = client->client_data;
795 res_win = get_results_window(gc->ui);
797 vbox = gtk_vbox_new(FALSE, 3);
799 box = gtk_hbox_new(TRUE, 3);
800 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
802 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
804 gc->results_widget = vbox;
806 entry = new_info_entry_in_frame(box, "Name");
807 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
808 if (strlen(ts->description)) {
809 entry = new_info_entry_in_frame(box, "Description");
810 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
812 entry = new_info_entry_in_frame(box, "Group ID");
813 entry_set_int_value(entry, ts->groupid);
814 entry = new_info_entry_in_frame(box, "Jobs");
815 entry_set_int_value(entry, ts->members);
816 entry = new_info_entry_in_frame(box, "Error");
817 entry_set_int_value(entry, ts->error);
818 entry = new_info_entry_in_frame(box, "PID");
819 entry_set_int_value(entry, ts->pid);
821 if (ts->io_bytes[DDIR_READ])
822 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
823 if (ts->io_bytes[DDIR_WRITE])
824 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
826 gfio_show_latency_buckets(vbox, ts);
827 gfio_show_cpu_usage(vbox, ts);
828 gfio_show_io_depths(vbox, ts);
830 gtk_widget_show_all(gc->ui->results_window);
834 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
836 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
837 struct gfio_client *gc = client->client_data;
841 char tmp[64], timebuf[80];
844 tm = localtime(&sec);
845 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
846 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
850 gtk_list_store_append(gc->ui->log_model, &iter);
851 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
852 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
853 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
854 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
859 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
861 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
862 struct gfio_client *gc = client->client_data;
863 GtkWidget *box, *frame, *entry, *vbox;
867 if (!gc->results_widget) {
868 printf("no results!\n");
872 if (!gc->disk_util_frame) {
873 gc->disk_util_frame = gtk_frame_new("Disk utilization");
874 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
877 vbox = gtk_vbox_new(FALSE, 3);
878 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
880 frame = gtk_frame_new((char *) p->dus.name);
881 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
883 box = gtk_vbox_new(FALSE, 3);
884 gtk_container_add(GTK_CONTAINER(frame), box);
886 frame = gtk_frame_new("Read");
887 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
888 vbox = gtk_hbox_new(TRUE, 3);
889 gtk_container_add(GTK_CONTAINER(frame), vbox);
890 entry = new_info_entry_in_frame(vbox, "IOs");
891 entry_set_int_value(entry, p->dus.ios[0]);
892 entry = new_info_entry_in_frame(vbox, "Merges");
893 entry_set_int_value(entry, p->dus.merges[0]);
894 entry = new_info_entry_in_frame(vbox, "Sectors");
895 entry_set_int_value(entry, p->dus.sectors[0]);
896 entry = new_info_entry_in_frame(vbox, "Ticks");
897 entry_set_int_value(entry, p->dus.ticks[0]);
899 frame = gtk_frame_new("Write");
900 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
901 vbox = gtk_hbox_new(TRUE, 3);
902 gtk_container_add(GTK_CONTAINER(frame), vbox);
903 entry = new_info_entry_in_frame(vbox, "IOs");
904 entry_set_int_value(entry, p->dus.ios[1]);
905 entry = new_info_entry_in_frame(vbox, "Merges");
906 entry_set_int_value(entry, p->dus.merges[1]);
907 entry = new_info_entry_in_frame(vbox, "Sectors");
908 entry_set_int_value(entry, p->dus.sectors[1]);
909 entry = new_info_entry_in_frame(vbox, "Ticks");
910 entry_set_int_value(entry, p->dus.ticks[1]);
912 frame = gtk_frame_new("Shared");
913 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
914 vbox = gtk_hbox_new(TRUE, 3);
915 gtk_container_add(GTK_CONTAINER(frame), vbox);
916 entry = new_info_entry_in_frame(vbox, "IO ticks");
917 entry_set_int_value(entry, p->dus.io_ticks);
918 entry = new_info_entry_in_frame(vbox, "Time in queue");
919 entry_set_int_value(entry, p->dus.time_in_queue);
921 gtk_widget_show_all(gc->results_widget);
926 extern int sum_stat_clients;
927 extern struct thread_stat client_ts;
928 extern struct group_run_stats client_gs;
930 static int sum_stat_nr;
932 static void gfio_thread_status_op(struct fio_client *client,
933 struct fio_net_cmd *cmd)
935 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
937 gfio_display_ts(client, &p->ts, &p->rs);
939 if (sum_stat_clients == 1)
942 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
943 sum_group_stats(&client_gs, &p->rs);
946 client_ts.groupid = p->ts.groupid;
948 if (++sum_stat_nr == sum_stat_clients) {
949 strcpy(client_ts.name, "All clients");
950 gfio_display_ts(client, &client_ts, &client_gs);
954 static void gfio_group_stats_op(struct fio_client *client,
955 struct fio_net_cmd *cmd)
958 printf("gfio_group_stats_op called\n");
959 fio_client_ops.group_stats(client, cmd);
963 static void gfio_update_eta(struct jobs_eta *je)
977 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
978 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
979 eta_to_str(eta_str, je->eta_sec);
982 sprintf(tmp, "%u", je->nr_running);
983 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
984 sprintf(tmp, "%u", je->files_open);
985 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
988 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
989 if (je->m_rate || je->t_rate) {
992 mr = num2str(je->m_rate, 4, 0, i2p);
993 tr = num2str(je->t_rate, 4, 0, i2p);
994 gtk_entry_set_text(GTK_ENTRY(ui.eta);
995 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
998 } else if (je->m_iops || je->t_iops)
999 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1001 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
1002 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
1003 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
1004 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
1007 if (je->eta_sec != INT_MAX && je->nr_running) {
1011 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1012 strcpy(output, "-.-% done");
1016 sprintf(output, "%3.1f%% done", perc);
1019 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1020 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1022 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1023 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1025 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1026 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1027 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1028 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1037 char *dst = output + strlen(output);
1039 sprintf(dst, " - %s", eta_str);
1042 gfio_update_thread_status(output, perc);
1043 gdk_threads_leave();
1046 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1048 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1049 struct gfio_client *gc = client->client_data;
1050 struct gui *ui = gc->ui;
1051 const char *os, *arch;
1054 os = fio_get_os_string(probe->os);
1058 arch = fio_get_arch_string(probe->arch);
1063 client->name = strdup((char *) probe->hostname);
1065 gdk_threads_enter();
1067 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1068 gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1069 gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1070 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1071 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1073 gfio_set_connected(ui, 1);
1075 gdk_threads_leave();
1078 static void gfio_update_thread_status(char *status_message, double perc)
1080 static char message[100];
1081 const char *m = message;
1083 strncpy(message, status_message, sizeof(message) - 1);
1084 gtk_progress_bar_set_text(
1085 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1086 gtk_progress_bar_set_fraction(
1087 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1088 gtk_widget_queue_draw(ui.window);
1091 static void gfio_quit_op(struct fio_client *client)
1093 struct gfio_client *gc = client->client_data;
1095 gdk_threads_enter();
1096 gfio_set_connected(gc->ui, 0);
1097 gdk_threads_leave();
1100 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1102 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1103 struct gfio_client *gc = client->client_data;
1104 struct gui *ui = gc->ui;
1108 p->iodepth = le32_to_cpu(p->iodepth);
1109 p->rw = le32_to_cpu(p->rw);
1111 for (i = 0; i < 2; i++) {
1112 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
1113 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
1116 p->numjobs = le32_to_cpu(p->numjobs);
1117 p->group_reporting = le32_to_cpu(p->group_reporting);
1119 gdk_threads_enter();
1121 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1122 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1123 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1125 sprintf(tmp, "%u", p->iodepth);
1126 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1128 gdk_threads_leave();
1131 static void gfio_client_timed_out(struct fio_client *client)
1133 struct gfio_client *gc = client->client_data;
1134 GtkWidget *dialog, *label, *content;
1137 gdk_threads_enter();
1139 gfio_set_connected(gc->ui, 0);
1140 clear_ui_info(gc->ui);
1142 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1144 dialog = gtk_dialog_new_with_buttons("Timed out!",
1145 GTK_WINDOW(gc->ui->window),
1146 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1147 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1149 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1150 label = gtk_label_new((const gchar *) buf);
1151 gtk_container_add(GTK_CONTAINER(content), label);
1152 gtk_widget_show_all(dialog);
1153 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1155 gtk_dialog_run(GTK_DIALOG(dialog));
1156 gtk_widget_destroy(dialog);
1158 gdk_threads_leave();
1161 struct client_ops gfio_client_ops = {
1162 .text_op = gfio_text_op,
1163 .disk_util = gfio_disk_util_op,
1164 .thread_status = gfio_thread_status_op,
1165 .group_stats = gfio_group_stats_op,
1166 .eta = gfio_update_eta,
1167 .probe = gfio_probe_op,
1168 .quit = gfio_quit_op,
1169 .add_job = gfio_add_job_op,
1170 .timed_out = gfio_client_timed_out,
1171 .stay_connected = 1,
1174 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1175 __attribute__((unused)) gpointer data)
1180 static void *job_thread(void *arg)
1182 fio_handle_clients(&gfio_client_ops);
1186 static int send_job_files(struct gui *ui)
1190 for (i = 0; i < ui->nr_job_files; i++) {
1191 ret = fio_clients_send_ini(ui->job_files[i]);
1195 free(ui->job_files[i]);
1196 ui->job_files[i] = NULL;
1198 while (i < ui->nr_job_files) {
1199 free(ui->job_files[i]);
1200 ui->job_files[i] = NULL;
1207 static void start_job_thread(struct gui *ui)
1209 if (send_job_files(ui)) {
1210 printf("Yeah, I didn't really like those options too much.\n");
1211 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1216 static void *server_thread(void *arg)
1219 gfio_server_running = 1;
1220 fio_start_server(NULL);
1221 gfio_server_running = 0;
1225 static void gfio_start_server(struct gui *ui)
1227 if (!gfio_server_running) {
1228 gfio_server_running = 1;
1229 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1230 pthread_detach(ui->server_t);
1234 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1237 struct gui *ui = data;
1239 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1240 start_job_thread(ui);
1243 static void file_open(GtkWidget *w, gpointer data);
1245 static void connect_clicked(GtkWidget *widget, gpointer data)
1247 struct gui *ui = data;
1249 if (!ui->connected) {
1250 if (!ui->nr_job_files)
1251 file_open(widget, data);
1252 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1253 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1254 if (!fio_clients_connect()) {
1255 pthread_create(&ui->t, NULL, job_thread, NULL);
1256 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1259 fio_clients_terminate();
1260 gfio_set_connected(ui, 0);
1265 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1266 struct button_spec *buttonspec)
1268 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1269 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1270 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1271 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1272 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1275 static void add_buttons(struct gui *ui,
1276 struct button_spec *buttonlist,
1281 for (i = 0; i < nbuttons; i++)
1282 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1285 static void on_info_bar_response(GtkWidget *widget, gint response,
1288 if (response == GTK_RESPONSE_OK) {
1289 gtk_widget_destroy(widget);
1290 ui.error_info_bar = NULL;
1294 void report_error(GError *error)
1296 if (ui.error_info_bar == NULL) {
1297 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1300 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1301 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1304 ui.error_label = gtk_label_new(error->message);
1305 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1306 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1308 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1309 gtk_widget_show_all(ui.vbox);
1312 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1313 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1317 static int get_connection_details(char **host, int *port, int *type,
1320 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1324 dialog = gtk_dialog_new_with_buttons("Connection details",
1325 GTK_WINDOW(ui.window),
1326 GTK_DIALOG_DESTROY_WITH_PARENT,
1327 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1328 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1330 frame = gtk_frame_new("Hostname / socket name");
1331 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1332 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1334 box = gtk_vbox_new(FALSE, 6);
1335 gtk_container_add(GTK_CONTAINER(frame), box);
1337 hbox = gtk_hbox_new(TRUE, 10);
1338 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1339 hentry = gtk_entry_new();
1340 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1341 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1343 frame = gtk_frame_new("Port");
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);
1350 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1352 frame = gtk_frame_new("Type");
1353 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1354 box = gtk_vbox_new(FALSE, 10);
1355 gtk_container_add(GTK_CONTAINER(frame), box);
1357 hbox = gtk_hbox_new(TRUE, 4);
1358 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1360 combo = gtk_combo_box_new_text();
1361 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1362 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1363 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
1364 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1366 gtk_container_add(GTK_CONTAINER(hbox), combo);
1368 frame = gtk_frame_new("Options");
1369 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1370 box = gtk_vbox_new(FALSE, 10);
1371 gtk_container_add(GTK_CONTAINER(frame), box);
1373 hbox = gtk_hbox_new(TRUE, 4);
1374 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1376 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1377 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1378 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.");
1379 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1381 gtk_widget_show_all(dialog);
1383 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1384 gtk_widget_destroy(dialog);
1388 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1389 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1391 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1392 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1393 *type = Fio_client_ipv4;
1394 else if (!strncmp(typeentry, "IPv6", 4))
1395 *type = Fio_client_ipv6;
1397 *type = Fio_client_socket;
1400 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1402 gtk_widget_destroy(dialog);
1406 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1408 struct gfio_client *gc;
1410 gc = malloc(sizeof(*gc));
1411 memset(gc, 0, sizeof(*gc));
1414 client->client_data = gc;
1417 static void file_open(GtkWidget *w, gpointer data)
1420 struct gui *ui = data;
1421 GSList *filenames, *fn_glist;
1422 GtkFileFilter *filter;
1424 int port, type, server_start;
1426 dialog = gtk_file_chooser_dialog_new("Open File",
1427 GTK_WINDOW(ui->window),
1428 GTK_FILE_CHOOSER_ACTION_OPEN,
1429 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1430 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1432 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1434 filter = gtk_file_filter_new();
1435 gtk_file_filter_add_pattern(filter, "*.fio");
1436 gtk_file_filter_add_pattern(filter, "*.job");
1437 gtk_file_filter_add_mime_type(filter, "text/fio");
1438 gtk_file_filter_set_name(filter, "Fio job file");
1439 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1441 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1442 gtk_widget_destroy(dialog);
1446 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1448 gtk_widget_destroy(dialog);
1450 if (get_connection_details(&host, &port, &type, &server_start))
1453 filenames = fn_glist;
1454 while (filenames != NULL) {
1455 struct fio_client *client;
1457 ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1458 ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1461 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1465 error = g_error_new(g_quark_from_string("fio"), 1,
1466 "Failed to add client %s", host);
1467 report_error(error);
1468 g_error_free(error);
1470 gfio_client_added(ui, client);
1472 g_free(filenames->data);
1473 filenames = g_slist_next(filenames);
1478 gfio_start_server(ui);
1480 g_slist_free(fn_glist);
1483 static void file_save(GtkWidget *w, gpointer data)
1485 struct gui *ui = data;
1488 dialog = gtk_file_chooser_dialog_new("Save File",
1489 GTK_WINDOW(ui->window),
1490 GTK_FILE_CHOOSER_ACTION_SAVE,
1491 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1492 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1495 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1496 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1498 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1501 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1502 // save_job_file(filename);
1505 gtk_widget_destroy(dialog);
1508 static void view_log_destroy(GtkWidget *w, gpointer data)
1510 struct gui *ui = (struct gui *) data;
1512 gtk_widget_ref(ui->log_tree);
1513 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1514 gtk_widget_destroy(w);
1515 ui->log_view = NULL;
1518 static void view_log(GtkWidget *w, gpointer data)
1520 GtkWidget *win, *scroll, *vbox, *box;
1521 struct gui *ui = (struct gui *) data;
1526 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1527 gtk_window_set_title(GTK_WINDOW(win), "Log");
1528 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1530 scroll = gtk_scrolled_window_new(NULL, NULL);
1532 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1534 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1536 box = gtk_hbox_new(TRUE, 0);
1537 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1538 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1539 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1541 vbox = gtk_vbox_new(TRUE, 5);
1542 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1544 gtk_container_add(GTK_CONTAINER(win), vbox);
1545 gtk_widget_show_all(win);
1548 static void preferences(GtkWidget *w, gpointer data)
1550 GtkWidget *dialog, *frame, *box, **buttons;
1553 dialog = gtk_dialog_new_with_buttons("Preferences",
1554 GTK_WINDOW(ui.window),
1555 GTK_DIALOG_DESTROY_WITH_PARENT,
1556 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1557 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1560 frame = gtk_frame_new("Debug logging");
1561 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1562 box = gtk_hbox_new(FALSE, 6);
1563 gtk_container_add(GTK_CONTAINER(frame), box);
1565 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1567 for (i = 0; i < FD_DEBUG_MAX; i++) {
1568 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1569 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1570 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1573 gtk_widget_show_all(dialog);
1575 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1576 gtk_widget_destroy(dialog);
1580 for (i = 0; i < FD_DEBUG_MAX; i++) {
1583 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1585 fio_debug |= (1UL << i);
1588 gtk_widget_destroy(dialog);
1591 static void about_dialog(GtkWidget *w, gpointer data)
1593 gtk_show_about_dialog(NULL,
1594 "program-name", "gfio",
1595 "comments", "Gtk2 UI for fio",
1597 "version", fio_version_string,
1598 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1599 "logo-icon-name", "fio",
1605 static GtkActionEntry menu_items[] = {
1606 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1607 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1608 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1609 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1610 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1611 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1612 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1613 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1614 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1616 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1618 static const gchar *ui_string = " \
1620 <menubar name=\"MainMenu\"> \
1621 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1622 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1623 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1624 <separator name=\"Separator\"/> \
1625 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1626 <separator name=\"Separator2\"/> \
1627 <menuitem name=\"Quit\" action=\"Quit\" /> \
1629 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1630 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1632 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1633 <menuitem name=\"About\" action=\"About\" /> \
1639 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1642 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1645 action_group = gtk_action_group_new("Menu");
1646 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1648 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1649 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1651 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1652 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1655 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1656 GtkWidget *vbox, GtkUIManager *ui_manager)
1658 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1661 static void init_ui(int *argc, char **argv[], struct gui *ui)
1663 GtkSettings *settings;
1664 GtkUIManager *uimanager;
1665 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1667 memset(ui, 0, sizeof(*ui));
1669 /* Magical g*thread incantation, you just need this thread stuff.
1670 * Without it, the update that happens in gfio_update_thread_status
1671 * doesn't really happen in a timely fashion, you need expose events
1673 if (!g_thread_supported())
1674 g_thread_init(NULL);
1677 gtk_init(argc, argv);
1678 settings = gtk_settings_get_default();
1679 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1682 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1683 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1684 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1686 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1687 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1689 ui->vbox = gtk_vbox_new(FALSE, 0);
1690 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1692 uimanager = gtk_ui_manager_new();
1693 menu = get_menubar_menu(ui->window, uimanager, ui);
1694 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1697 * Set up alignments for widgets at the top of ui,
1698 * align top left, expand horizontally but not vertically
1700 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1701 ui->topvbox = gtk_vbox_new(FALSE, 3);
1702 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1703 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1705 probe = gtk_frame_new("Job");
1706 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1707 probe_frame = gtk_vbox_new(FALSE, 3);
1708 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1710 probe_box = gtk_hbox_new(FALSE, 3);
1711 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1712 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1713 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1714 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1715 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1717 probe_box = gtk_hbox_new(FALSE, 3);
1718 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1720 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1721 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1722 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1723 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1724 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1725 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1727 probe_box = gtk_hbox_new(FALSE, 3);
1728 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1729 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1730 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1731 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1732 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1735 * Only add this if we have a commit rate
1738 probe_box = gtk_hbox_new(FALSE, 3);
1739 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1741 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1742 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1744 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1745 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1749 * Add a text box for text op messages
1751 ui->textview = gtk_text_view_new();
1752 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1753 gtk_text_buffer_set_text(ui->text, "", -1);
1754 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1755 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1756 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1757 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1758 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1759 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1760 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1764 * Set up alignments for widgets at the bottom of ui,
1765 * align bottom left, expand horizontally but not vertically
1767 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1768 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1769 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1770 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1773 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1776 * Set up thread status progress bar
1778 ui->thread_status_pb = gtk_progress_bar_new();
1779 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1780 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1781 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1783 gfio_ui_setup_log(ui);
1785 gtk_widget_show_all(ui->window);
1788 int main(int argc, char *argv[], char *envp[])
1790 if (initialize_fio(envp))
1792 if (fio_init_options())
1795 init_ui(&argc, &argv, &ui);
1797 gdk_threads_enter();
1799 gdk_threads_leave();