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);
1233 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1236 struct gui *ui = data;
1238 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1239 start_job_thread(ui);
1242 static void file_open(GtkWidget *w, gpointer data);
1244 static void connect_clicked(GtkWidget *widget, gpointer data)
1246 struct gui *ui = data;
1248 if (!ui->connected) {
1249 if (!ui->nr_job_files)
1250 file_open(widget, data);
1251 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1252 if (!fio_clients_connect()) {
1253 pthread_create(&ui->t, NULL, job_thread, NULL);
1254 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1257 fio_clients_terminate();
1258 gfio_set_connected(ui, 0);
1263 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1264 struct button_spec *buttonspec)
1266 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1267 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1268 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1269 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1270 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1273 static void add_buttons(struct gui *ui,
1274 struct button_spec *buttonlist,
1279 for (i = 0; i < nbuttons; i++)
1280 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1283 static void on_info_bar_response(GtkWidget *widget, gint response,
1286 if (response == GTK_RESPONSE_OK) {
1287 gtk_widget_destroy(widget);
1288 ui.error_info_bar = NULL;
1292 void report_error(GError *error)
1294 if (ui.error_info_bar == NULL) {
1295 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1298 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1299 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1302 ui.error_label = gtk_label_new(error->message);
1303 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1304 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1306 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1307 gtk_widget_show_all(ui.vbox);
1310 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1311 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1315 static int get_connection_details(char **host, int *port, int *type,
1318 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1322 dialog = gtk_dialog_new_with_buttons("Connection details",
1323 GTK_WINDOW(ui.window),
1324 GTK_DIALOG_DESTROY_WITH_PARENT,
1325 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1326 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1328 frame = gtk_frame_new("Hostname / socket name");
1329 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1330 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1332 box = gtk_vbox_new(FALSE, 6);
1333 gtk_container_add(GTK_CONTAINER(frame), box);
1335 hbox = gtk_hbox_new(TRUE, 10);
1336 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1337 hentry = gtk_entry_new();
1338 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1339 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1341 frame = gtk_frame_new("Port");
1342 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1343 box = gtk_vbox_new(FALSE, 10);
1344 gtk_container_add(GTK_CONTAINER(frame), box);
1346 hbox = gtk_hbox_new(TRUE, 4);
1347 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1348 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1350 frame = gtk_frame_new("Type");
1351 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1352 box = gtk_vbox_new(FALSE, 10);
1353 gtk_container_add(GTK_CONTAINER(frame), box);
1355 hbox = gtk_hbox_new(TRUE, 4);
1356 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1358 combo = gtk_combo_box_new_text();
1359 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1360 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1361 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
1362 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1364 gtk_container_add(GTK_CONTAINER(hbox), combo);
1366 frame = gtk_frame_new("Options");
1367 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1368 box = gtk_vbox_new(FALSE, 10);
1369 gtk_container_add(GTK_CONTAINER(frame), box);
1371 hbox = gtk_hbox_new(TRUE, 4);
1372 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1374 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1375 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1376 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.");
1377 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1379 gtk_widget_show_all(dialog);
1381 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1382 gtk_widget_destroy(dialog);
1386 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1387 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1389 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1390 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1391 *type = Fio_client_ipv4;
1392 else if (!strncmp(typeentry, "IPv6", 4))
1393 *type = Fio_client_ipv6;
1395 *type = Fio_client_socket;
1398 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1400 gtk_widget_destroy(dialog);
1404 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1406 struct gfio_client *gc;
1408 gc = malloc(sizeof(*gc));
1409 memset(gc, 0, sizeof(*gc));
1412 client->client_data = gc;
1415 static void file_open(GtkWidget *w, gpointer data)
1418 struct gui *ui = data;
1419 GSList *filenames, *fn_glist;
1420 GtkFileFilter *filter;
1422 int port, type, server_start;
1424 dialog = gtk_file_chooser_dialog_new("Open File",
1425 GTK_WINDOW(ui->window),
1426 GTK_FILE_CHOOSER_ACTION_OPEN,
1427 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1428 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1430 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1432 filter = gtk_file_filter_new();
1433 gtk_file_filter_add_pattern(filter, "*.fio");
1434 gtk_file_filter_add_pattern(filter, "*.job");
1435 gtk_file_filter_add_mime_type(filter, "text/fio");
1436 gtk_file_filter_set_name(filter, "Fio job file");
1437 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1439 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1440 gtk_widget_destroy(dialog);
1444 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1446 gtk_widget_destroy(dialog);
1448 if (get_connection_details(&host, &port, &type, &server_start))
1451 filenames = fn_glist;
1452 while (filenames != NULL) {
1453 struct fio_client *client;
1455 ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1456 ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1459 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1463 error = g_error_new(g_quark_from_string("fio"), 1,
1464 "Failed to add client %s", host);
1465 report_error(error);
1466 g_error_free(error);
1468 gfio_client_added(ui, client);
1470 g_free(filenames->data);
1471 filenames = g_slist_next(filenames);
1476 gfio_start_server(ui);
1478 g_slist_free(fn_glist);
1481 static void file_save(GtkWidget *w, gpointer data)
1483 struct gui *ui = data;
1486 dialog = gtk_file_chooser_dialog_new("Save File",
1487 GTK_WINDOW(ui->window),
1488 GTK_FILE_CHOOSER_ACTION_SAVE,
1489 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1490 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1493 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1494 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1496 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1499 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1500 // save_job_file(filename);
1503 gtk_widget_destroy(dialog);
1506 static void view_log_destroy(GtkWidget *w, gpointer data)
1508 struct gui *ui = (struct gui *) data;
1510 gtk_widget_ref(ui->log_tree);
1511 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1512 gtk_widget_destroy(w);
1513 ui->log_view = NULL;
1516 static void view_log(GtkWidget *w, gpointer data)
1518 GtkWidget *win, *scroll, *vbox, *box;
1519 struct gui *ui = (struct gui *) data;
1524 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1525 gtk_window_set_title(GTK_WINDOW(win), "Log");
1526 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1528 scroll = gtk_scrolled_window_new(NULL, NULL);
1530 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1532 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1534 box = gtk_hbox_new(TRUE, 0);
1535 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1536 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1537 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1539 vbox = gtk_vbox_new(TRUE, 5);
1540 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1542 gtk_container_add(GTK_CONTAINER(win), vbox);
1543 gtk_widget_show_all(win);
1546 static void preferences(GtkWidget *w, gpointer data)
1548 GtkWidget *dialog, *frame, *box, **buttons;
1551 dialog = gtk_dialog_new_with_buttons("Preferences",
1552 GTK_WINDOW(ui.window),
1553 GTK_DIALOG_DESTROY_WITH_PARENT,
1554 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1555 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1558 frame = gtk_frame_new("Debug logging");
1559 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1560 box = gtk_hbox_new(FALSE, 6);
1561 gtk_container_add(GTK_CONTAINER(frame), box);
1563 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1565 for (i = 0; i < FD_DEBUG_MAX; i++) {
1566 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1567 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1568 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1571 gtk_widget_show_all(dialog);
1573 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1574 gtk_widget_destroy(dialog);
1578 for (i = 0; i < FD_DEBUG_MAX; i++) {
1581 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1583 fio_debug |= (1UL << i);
1586 gtk_widget_destroy(dialog);
1589 static void about_dialog(GtkWidget *w, gpointer data)
1591 gtk_show_about_dialog(NULL,
1592 "program-name", "gfio",
1593 "comments", "Gtk2 UI for fio",
1595 "version", fio_version_string,
1596 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1597 "logo-icon-name", "fio",
1603 static GtkActionEntry menu_items[] = {
1604 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1605 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1606 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1607 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1608 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1609 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1610 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1611 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1612 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1614 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1616 static const gchar *ui_string = " \
1618 <menubar name=\"MainMenu\"> \
1619 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1620 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1621 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1622 <separator name=\"Separator\"/> \
1623 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1624 <separator name=\"Separator2\"/> \
1625 <menuitem name=\"Quit\" action=\"Quit\" /> \
1627 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1628 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1630 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1631 <menuitem name=\"About\" action=\"About\" /> \
1637 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1640 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1643 action_group = gtk_action_group_new("Menu");
1644 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1646 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1647 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1649 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1650 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1653 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1654 GtkWidget *vbox, GtkUIManager *ui_manager)
1656 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1659 static void init_ui(int *argc, char **argv[], struct gui *ui)
1661 GtkSettings *settings;
1662 GtkUIManager *uimanager;
1663 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1665 memset(ui, 0, sizeof(*ui));
1667 /* Magical g*thread incantation, you just need this thread stuff.
1668 * Without it, the update that happens in gfio_update_thread_status
1669 * doesn't really happen in a timely fashion, you need expose events
1671 if (!g_thread_supported())
1672 g_thread_init(NULL);
1675 gtk_init(argc, argv);
1676 settings = gtk_settings_get_default();
1677 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1680 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1681 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1682 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1684 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1685 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1687 ui->vbox = gtk_vbox_new(FALSE, 0);
1688 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1690 uimanager = gtk_ui_manager_new();
1691 menu = get_menubar_menu(ui->window, uimanager, ui);
1692 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1695 * Set up alignments for widgets at the top of ui,
1696 * align top left, expand horizontally but not vertically
1698 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1699 ui->topvbox = gtk_vbox_new(FALSE, 3);
1700 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1701 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1703 probe = gtk_frame_new("Job");
1704 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1705 probe_frame = gtk_vbox_new(FALSE, 3);
1706 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1708 probe_box = gtk_hbox_new(FALSE, 3);
1709 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1710 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1711 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1712 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1713 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1715 probe_box = gtk_hbox_new(FALSE, 3);
1716 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1718 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1719 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1720 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1721 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1722 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1723 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1725 probe_box = gtk_hbox_new(FALSE, 3);
1726 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1727 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1728 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1729 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1730 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1733 * Only add this if we have a commit rate
1736 probe_box = gtk_hbox_new(FALSE, 3);
1737 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1739 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1740 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1742 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1743 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1747 * Add a text box for text op messages
1749 ui->textview = gtk_text_view_new();
1750 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1751 gtk_text_buffer_set_text(ui->text, "", -1);
1752 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1753 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1754 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1755 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1756 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1757 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1758 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1762 * Set up alignments for widgets at the bottom of ui,
1763 * align bottom left, expand horizontally but not vertically
1765 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1766 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1767 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1768 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1771 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1774 * Set up thread status progress bar
1776 ui->thread_status_pb = gtk_progress_bar_new();
1777 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1778 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1779 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1781 gfio_ui_setup_log(ui);
1783 gtk_widget_show_all(ui->window);
1786 int main(int argc, char *argv[], char *envp[])
1788 if (initialize_fio(envp))
1790 if (fio_init_options())
1793 init_ui(&argc, &argv, &ui);
1795 gdk_threads_enter();
1797 gdk_threads_leave();