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 void gfio_update_thread_status(char *status_message, double perc);
34 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
36 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
38 static void connect_clicked(GtkWidget *widget, gpointer data);
39 static void start_job_clicked(GtkWidget *widget, gpointer data);
41 static struct button_spec {
42 const char *buttontext;
44 const char *tooltiptext;
45 const int start_insensitive;
46 } buttonspeclist[] = {
47 #define CONNECT_BUTTON 0
48 #define START_JOB_BUTTON 1
49 { "Connect", connect_clicked, "Connect to host", 0 },
52 "Send current fio job to fio server to be executed", 1 },
74 GtkWidget *write_iops;
84 GtkWidget *bottomalign;
85 GtkWidget *thread_status_pb;
87 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
88 GtkWidget *scrolled_window;
90 GtkWidget *error_info_bar;
91 GtkWidget *error_label;
92 GtkWidget *results_notebook;
93 GtkWidget *results_window;
94 GtkListStore *log_model;
98 struct probe_widget probe;
99 struct eta_widget eta;
103 struct fio_client *client;
110 GtkWidget *results_widget;
111 GtkWidget *disk_util_frame;
114 static void clear_ui_info(struct gui *ui)
116 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
117 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
118 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
119 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
120 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
121 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
122 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
123 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
124 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
125 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
126 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
127 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
128 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
129 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
132 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
134 GtkWidget *entry, *frame;
136 frame = gtk_frame_new(label);
137 entry = gtk_entry_new();
138 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
139 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
140 gtk_container_add(GTK_CONTAINER(frame), entry);
145 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
147 GtkWidget *label_widget;
150 frame = gtk_frame_new(label);
151 label_widget = gtk_label_new(NULL);
152 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
153 gtk_container_add(GTK_CONTAINER(frame), label_widget);
158 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
160 GtkWidget *button, *box;
162 box = gtk_hbox_new(FALSE, 3);
163 gtk_container_add(GTK_CONTAINER(hbox), box);
165 button = gtk_spin_button_new_with_range(min, max, 1.0);
166 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
168 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
169 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
174 static void gfio_set_connected(struct gui *ui, int connected)
177 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
179 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
180 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
183 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
184 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
188 static void label_set_int_value(GtkWidget *entry, unsigned int val)
192 sprintf(tmp, "%u", val);
193 gtk_label_set_text(GTK_LABEL(entry), tmp);
196 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
200 sprintf(tmp, "%u", val);
201 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
205 #define ALIGN_RIGHT 2
209 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
211 GtkCellRenderer *renderer;
212 GtkTreeViewColumn *col;
213 double xalign = 0.0; /* left as default */
214 PangoAlignment align;
217 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
218 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
220 visible = !(flags & INVISIBLE);
222 renderer = gtk_cell_renderer_text_new();
223 col = gtk_tree_view_column_new();
225 gtk_tree_view_column_set_title(col, title);
226 if (!(flags & UNSORTABLE))
227 gtk_tree_view_column_set_sort_column_id(col, index);
228 gtk_tree_view_column_set_resizable(col, TRUE);
229 gtk_tree_view_column_pack_start(col, renderer, TRUE);
230 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
231 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
233 case PANGO_ALIGN_LEFT:
236 case PANGO_ALIGN_CENTER:
239 case PANGO_ALIGN_RIGHT:
243 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
244 gtk_tree_view_column_set_visible(col, visible);
245 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
249 static void gfio_ui_setup_log(struct gui *ui)
251 GtkTreeSelection *selection;
253 GtkWidget *tree_view;
255 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
257 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
258 gtk_widget_set_can_focus(tree_view, FALSE);
260 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
261 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
262 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
263 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
265 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
266 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
267 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
268 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
270 ui->log_model = model;
271 ui->log_tree = tree_view;
274 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
280 GType types[FIO_IO_U_LIST_MAX_LEN];
281 GtkWidget *tree_view;
282 GtkTreeSelection *selection;
287 for (i = 0; i < len; i++)
288 types[i] = G_TYPE_INT;
290 model = gtk_list_store_newv(len, types);
292 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
293 gtk_widget_set_can_focus(tree_view, FALSE);
295 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
296 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
298 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
299 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
301 for (i = 0; i < len; i++) {
304 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
305 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
308 gtk_list_store_append(model, &iter);
310 for (i = 0; i < len; i++) {
312 ovals[i] = (ovals[i] + 999) / 1000;
313 gtk_list_store_set(model, &iter, i, ovals[i], -1);
319 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
322 unsigned int *io_u_plat = ts->io_u_plat[ddir];
323 unsigned long nr = ts->clat_stat[ddir].samples;
324 fio_fp64_t *plist = ts->percentile_list;
325 unsigned int *ovals, len, minv, maxv, scale_down;
327 GtkWidget *tree_view, *frame, *hbox;
330 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
335 * We default to usecs, but if the value range is such that we
336 * should scale down to msecs, do that.
338 if (minv > 2000 && maxv > 99999) {
346 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
348 sprintf(tmp, "Completion percentiles (%s)", base);
349 frame = gtk_frame_new(tmp);
350 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
352 hbox = gtk_hbox_new(FALSE, 3);
353 gtk_container_add(GTK_CONTAINER(frame), hbox);
355 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
361 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
362 unsigned long max, double mean, double dev)
364 const char *base = "(usec)";
365 GtkWidget *hbox, *label, *frame;
369 if (!usec_to_msec(&min, &max, &mean, &dev))
372 minp = num2str(min, 6, 1, 0);
373 maxp = num2str(max, 6, 1, 0);
375 sprintf(tmp, "%s %s", name, base);
376 frame = gtk_frame_new(tmp);
377 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
379 hbox = gtk_hbox_new(FALSE, 3);
380 gtk_container_add(GTK_CONTAINER(frame), hbox);
382 label = new_info_label_in_frame(hbox, "Minimum");
383 gtk_label_set_text(GTK_LABEL(label), minp);
384 label = new_info_label_in_frame(hbox, "Maximum");
385 gtk_label_set_text(GTK_LABEL(label), maxp);
386 label = new_info_label_in_frame(hbox, "Average");
387 sprintf(tmp, "%5.02f", mean);
388 gtk_label_set_text(GTK_LABEL(label), tmp);
389 label = new_info_label_in_frame(hbox, "Standard deviation");
390 sprintf(tmp, "%5.02f", dev);
391 gtk_label_set_text(GTK_LABEL(label), tmp);
402 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
403 struct thread_stat *ts, int ddir)
405 const char *ddir_label[2] = { "Read", "Write" };
406 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
407 unsigned long min[3], max[3], runt;
408 unsigned long long bw, iops;
409 unsigned int flags = 0;
410 double mean[3], dev[3];
411 char *io_p, *bw_p, *iops_p;
414 if (!ts->runtime[ddir])
417 i2p = is_power_of_2(rs->kb_base);
418 runt = ts->runtime[ddir];
420 bw = (1000 * ts->io_bytes[ddir]) / runt;
421 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
422 bw_p = num2str(bw, 6, 1, i2p);
424 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
425 iops_p = num2str(iops, 6, 1, 0);
427 box = gtk_hbox_new(FALSE, 3);
428 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
430 frame = gtk_frame_new(ddir_label[ddir]);
431 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
433 main_vbox = gtk_vbox_new(FALSE, 3);
434 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
436 box = gtk_hbox_new(FALSE, 3);
437 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
439 label = new_info_label_in_frame(box, "IO");
440 gtk_label_set_text(GTK_LABEL(label), io_p);
441 label = new_info_label_in_frame(box, "Bandwidth");
442 gtk_label_set_text(GTK_LABEL(label), bw_p);
443 label = new_info_label_in_frame(box, "IOPS");
444 gtk_label_set_text(GTK_LABEL(label), iops_p);
445 label = new_info_label_in_frame(box, "Runtime (msec)");
446 label_set_int_value(label, ts->runtime[ddir]);
448 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
449 double p_of_agg = 100.0;
450 const char *bw_str = "KB";
454 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
455 if (p_of_agg > 100.0)
459 if (mean[0] > 999999.9) {
467 sprintf(tmp, "Bandwidth (%s)", bw_str);
468 frame = gtk_frame_new(tmp);
469 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
471 box = gtk_hbox_new(FALSE, 3);
472 gtk_container_add(GTK_CONTAINER(frame), box);
474 label = new_info_label_in_frame(box, "Minimum");
475 label_set_int_value(label, min[0]);
476 label = new_info_label_in_frame(box, "Maximum");
477 label_set_int_value(label, max[0]);
478 label = new_info_label_in_frame(box, "Percentage of jobs");
479 sprintf(tmp, "%3.2f%%", p_of_agg);
480 gtk_label_set_text(GTK_LABEL(label), tmp);
481 label = new_info_label_in_frame(box, "Average");
482 sprintf(tmp, "%5.02f", mean[0]);
483 gtk_label_set_text(GTK_LABEL(label), tmp);
484 label = new_info_label_in_frame(box, "Standard deviation");
485 sprintf(tmp, "%5.02f", dev[0]);
486 gtk_label_set_text(GTK_LABEL(label), tmp);
489 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
491 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
493 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
497 frame = gtk_frame_new("Latency");
498 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
500 vbox = gtk_vbox_new(FALSE, 3);
501 gtk_container_add(GTK_CONTAINER(frame), vbox);
503 if (flags & GFIO_SLAT)
504 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
505 if (flags & GFIO_CLAT)
506 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
507 if (flags & GFIO_LAT)
508 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
511 if (ts->clat_percentiles)
512 gfio_show_clat_percentiles(main_vbox, ts, ddir);
520 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
523 GtkWidget *tree_view;
524 GtkTreeSelection *selection;
531 * Check if all are empty, in which case don't bother
533 for (i = 0, skipped = 0; i < num; i++)
540 types = malloc(num * sizeof(GType));
542 for (i = 0; i < num; i++)
543 types[i] = G_TYPE_STRING;
545 model = gtk_list_store_newv(num, types);
549 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
550 gtk_widget_set_can_focus(tree_view, FALSE);
552 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
553 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
555 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
556 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
558 for (i = 0; i < num; i++)
559 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
561 gtk_list_store_append(model, &iter);
563 for (i = 0; i < num; i++) {
567 sprintf(fbuf, "0.00");
569 sprintf(fbuf, "%3.2f%%", lat[i]);
571 gtk_list_store_set(model, &iter, i, fbuf, -1);
577 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
579 GtkWidget *box, *frame, *tree_view;
580 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
581 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
582 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
583 "250", "500", "750", "1000", };
584 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
585 "250", "500", "750", "1000", "2000",
588 stat_calc_lat_u(ts, io_u_lat_u);
589 stat_calc_lat_m(ts, io_u_lat_m);
591 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
593 frame = gtk_frame_new("Latency buckets (usec)");
594 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
596 box = gtk_hbox_new(FALSE, 3);
597 gtk_container_add(GTK_CONTAINER(frame), box);
598 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
601 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
603 frame = gtk_frame_new("Latency buckets (msec)");
604 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
606 box = gtk_hbox_new(FALSE, 3);
607 gtk_container_add(GTK_CONTAINER(frame), box);
608 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
612 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
614 GtkWidget *box, *frame, *entry;
615 double usr_cpu, sys_cpu;
616 unsigned long runtime;
619 runtime = ts->total_run_time;
621 double runt = (double) runtime;
623 usr_cpu = (double) ts->usr_time * 100 / runt;
624 sys_cpu = (double) ts->sys_time * 100 / runt;
630 frame = gtk_frame_new("OS resources");
631 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
633 box = gtk_hbox_new(FALSE, 3);
634 gtk_container_add(GTK_CONTAINER(frame), box);
636 entry = new_info_entry_in_frame(box, "User CPU");
637 sprintf(tmp, "%3.2f%%", usr_cpu);
638 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
639 entry = new_info_entry_in_frame(box, "System CPU");
640 sprintf(tmp, "%3.2f%%", sys_cpu);
641 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
642 entry = new_info_entry_in_frame(box, "Context switches");
643 entry_set_int_value(entry, ts->ctx);
644 entry = new_info_entry_in_frame(box, "Major faults");
645 entry_set_int_value(entry, ts->majf);
646 entry = new_info_entry_in_frame(box, "Minor faults");
647 entry_set_int_value(entry, ts->minf);
649 static void gfio_add_sc_depths_tree(GtkListStore *model,
650 struct thread_stat *ts, unsigned int len,
653 double io_u_dist[FIO_IO_U_MAP_NR];
655 /* Bits 0, and 3-8 */
656 const int add_mask = 0x1f9;
660 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
662 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
664 gtk_list_store_append(model, &iter);
666 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
668 for (i = 1, j = 0; i < len; i++) {
671 if (!(add_mask & (1UL << (i - 1))))
672 sprintf(fbuf, "0.0%%");
674 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
678 gtk_list_store_set(model, &iter, i, fbuf, -1);
683 static void gfio_add_total_depths_tree(GtkListStore *model,
684 struct thread_stat *ts, unsigned int len)
686 double io_u_dist[FIO_IO_U_MAP_NR];
688 /* Bits 1-6, and 8 */
689 const int add_mask = 0x17e;
692 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
694 gtk_list_store_append(model, &iter);
696 gtk_list_store_set(model, &iter, 0, "Total", -1);
698 for (i = 1, j = 0; i < len; i++) {
701 if (!(add_mask & (1UL << (i - 1))))
702 sprintf(fbuf, "0.0%%");
704 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
708 gtk_list_store_set(model, &iter, i, fbuf, -1);
713 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
715 GtkWidget *frame, *box, *tree_view;
716 GtkTreeSelection *selection;
718 GType types[FIO_IO_U_MAP_NR + 1];
721 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
723 frame = gtk_frame_new("IO depths");
724 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
726 box = gtk_hbox_new(FALSE, 3);
727 gtk_container_add(GTK_CONTAINER(frame), box);
729 for (i = 0; i < NR_LABELS; i++)
730 types[i] = G_TYPE_STRING;
732 model = gtk_list_store_newv(NR_LABELS, types);
734 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
735 gtk_widget_set_can_focus(tree_view, FALSE);
737 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
738 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
740 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
741 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
743 for (i = 0; i < NR_LABELS; i++)
744 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
746 gfio_add_total_depths_tree(model, ts, NR_LABELS);
747 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
748 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
750 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
753 static gboolean results_window_delete(GtkWidget *w, gpointer data)
755 struct gui *ui = (struct gui *) data;
757 gtk_widget_destroy(w);
758 ui->results_window = NULL;
759 ui->results_notebook = NULL;
763 static GtkWidget *get_results_window(struct gui *ui)
765 GtkWidget *win, *notebook;
767 if (ui->results_window)
768 return ui->results_notebook;
770 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
771 gtk_window_set_title(GTK_WINDOW(win), "Results");
772 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
773 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
775 notebook = gtk_notebook_new();
776 gtk_container_add(GTK_CONTAINER(win), notebook);
778 ui->results_window = win;
779 ui->results_notebook = notebook;
780 return ui->results_notebook;
783 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
784 struct group_run_stats *rs)
786 GtkWidget *res_win, *box, *vbox, *entry;
787 struct gfio_client *gc = client->client_data;
791 res_win = get_results_window(gc->ui);
793 vbox = gtk_vbox_new(FALSE, 3);
795 box = gtk_hbox_new(TRUE, 3);
796 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
798 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
800 gc->results_widget = vbox;
802 entry = new_info_entry_in_frame(box, "Name");
803 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
804 if (strlen(ts->description)) {
805 entry = new_info_entry_in_frame(box, "Description");
806 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
808 entry = new_info_entry_in_frame(box, "Group ID");
809 entry_set_int_value(entry, ts->groupid);
810 entry = new_info_entry_in_frame(box, "Jobs");
811 entry_set_int_value(entry, ts->members);
812 entry = new_info_entry_in_frame(box, "Error");
813 entry_set_int_value(entry, ts->error);
814 entry = new_info_entry_in_frame(box, "PID");
815 entry_set_int_value(entry, ts->pid);
817 if (ts->io_bytes[DDIR_READ])
818 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
819 if (ts->io_bytes[DDIR_WRITE])
820 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
822 gfio_show_latency_buckets(vbox, ts);
823 gfio_show_cpu_usage(vbox, ts);
824 gfio_show_io_depths(vbox, ts);
826 gtk_widget_show_all(gc->ui->results_window);
830 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
832 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
833 struct gfio_client *gc = client->client_data;
837 char tmp[64], timebuf[80];
840 tm = localtime(&sec);
841 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
842 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
846 gtk_list_store_append(gc->ui->log_model, &iter);
847 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
848 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
849 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
850 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
855 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
857 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
858 struct gfio_client *gc = client->client_data;
859 GtkWidget *box, *frame, *entry, *vbox;
863 if (!gc->results_widget) {
864 printf("no results!\n");
868 if (!gc->disk_util_frame) {
869 gc->disk_util_frame = gtk_frame_new("Disk utilization");
870 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
873 vbox = gtk_vbox_new(FALSE, 3);
874 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
876 frame = gtk_frame_new((char *) p->dus.name);
877 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
879 box = gtk_vbox_new(FALSE, 3);
880 gtk_container_add(GTK_CONTAINER(frame), box);
882 frame = gtk_frame_new("Read");
883 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
884 vbox = gtk_hbox_new(TRUE, 3);
885 gtk_container_add(GTK_CONTAINER(frame), vbox);
886 entry = new_info_entry_in_frame(vbox, "IOs");
887 entry_set_int_value(entry, p->dus.ios[0]);
888 entry = new_info_entry_in_frame(vbox, "Merges");
889 entry_set_int_value(entry, p->dus.merges[0]);
890 entry = new_info_entry_in_frame(vbox, "Sectors");
891 entry_set_int_value(entry, p->dus.sectors[0]);
892 entry = new_info_entry_in_frame(vbox, "Ticks");
893 entry_set_int_value(entry, p->dus.ticks[0]);
895 frame = gtk_frame_new("Write");
896 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
897 vbox = gtk_hbox_new(TRUE, 3);
898 gtk_container_add(GTK_CONTAINER(frame), vbox);
899 entry = new_info_entry_in_frame(vbox, "IOs");
900 entry_set_int_value(entry, p->dus.ios[1]);
901 entry = new_info_entry_in_frame(vbox, "Merges");
902 entry_set_int_value(entry, p->dus.merges[1]);
903 entry = new_info_entry_in_frame(vbox, "Sectors");
904 entry_set_int_value(entry, p->dus.sectors[1]);
905 entry = new_info_entry_in_frame(vbox, "Ticks");
906 entry_set_int_value(entry, p->dus.ticks[1]);
908 frame = gtk_frame_new("Shared");
909 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
910 vbox = gtk_hbox_new(TRUE, 3);
911 gtk_container_add(GTK_CONTAINER(frame), vbox);
912 entry = new_info_entry_in_frame(vbox, "IO ticks");
913 entry_set_int_value(entry, p->dus.io_ticks);
914 entry = new_info_entry_in_frame(vbox, "Time in queue");
915 entry_set_int_value(entry, p->dus.time_in_queue);
917 gtk_widget_show_all(gc->results_widget);
922 extern int sum_stat_clients;
923 extern struct thread_stat client_ts;
924 extern struct group_run_stats client_gs;
926 static int sum_stat_nr;
928 static void gfio_thread_status_op(struct fio_client *client,
929 struct fio_net_cmd *cmd)
931 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
933 gfio_display_ts(client, &p->ts, &p->rs);
935 if (sum_stat_clients == 1)
938 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
939 sum_group_stats(&client_gs, &p->rs);
942 client_ts.groupid = p->ts.groupid;
944 if (++sum_stat_nr == sum_stat_clients) {
945 strcpy(client_ts.name, "All clients");
946 gfio_display_ts(client, &client_ts, &client_gs);
950 static void gfio_group_stats_op(struct fio_client *client,
951 struct fio_net_cmd *cmd)
954 printf("gfio_group_stats_op called\n");
955 fio_client_ops.group_stats(client, cmd);
959 static void gfio_update_eta(struct jobs_eta *je)
973 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
974 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
975 eta_to_str(eta_str, je->eta_sec);
978 sprintf(tmp, "%u", je->nr_running);
979 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
980 sprintf(tmp, "%u", je->files_open);
981 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
984 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
985 if (je->m_rate || je->t_rate) {
988 mr = num2str(je->m_rate, 4, 0, i2p);
989 tr = num2str(je->t_rate, 4, 0, i2p);
990 gtk_entry_set_text(GTK_ENTRY(ui.eta);
991 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
994 } else if (je->m_iops || je->t_iops)
995 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
997 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
998 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
999 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
1000 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
1003 if (je->eta_sec != INT_MAX && je->nr_running) {
1007 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1008 strcpy(output, "-.-% done");
1012 sprintf(output, "%3.1f%% done", perc);
1015 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1016 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1018 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1019 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1021 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1022 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1023 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1024 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1033 char *dst = output + strlen(output);
1035 sprintf(dst, " - %s", eta_str);
1038 gfio_update_thread_status(output, perc);
1039 gdk_threads_leave();
1042 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1044 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1045 struct gfio_client *gc = client->client_data;
1046 struct gui *ui = gc->ui;
1047 const char *os, *arch;
1050 os = fio_get_os_string(probe->os);
1054 arch = fio_get_arch_string(probe->arch);
1059 client->name = strdup((char *) probe->hostname);
1061 gdk_threads_enter();
1063 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1064 gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1065 gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1066 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1067 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1069 gfio_set_connected(ui, 1);
1071 gdk_threads_leave();
1074 static void gfio_update_thread_status(char *status_message, double perc)
1076 static char message[100];
1077 const char *m = message;
1079 strncpy(message, status_message, sizeof(message) - 1);
1080 gtk_progress_bar_set_text(
1081 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1082 gtk_progress_bar_set_fraction(
1083 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1084 gtk_widget_queue_draw(ui.window);
1087 static void gfio_quit_op(struct fio_client *client)
1089 struct gfio_client *gc = client->client_data;
1091 gdk_threads_enter();
1092 gfio_set_connected(gc->ui, 0);
1093 gdk_threads_leave();
1096 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1098 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1099 struct gfio_client *gc = client->client_data;
1100 struct gui *ui = gc->ui;
1104 p->iodepth = le32_to_cpu(p->iodepth);
1105 p->rw = le32_to_cpu(p->rw);
1107 for (i = 0; i < 2; i++) {
1108 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
1109 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
1112 p->numjobs = le32_to_cpu(p->numjobs);
1113 p->group_reporting = le32_to_cpu(p->group_reporting);
1115 gdk_threads_enter();
1117 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1118 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1119 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1121 sprintf(tmp, "%u", p->iodepth);
1122 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1124 gdk_threads_leave();
1127 static void gfio_client_timed_out(struct fio_client *client)
1129 struct gfio_client *gc = client->client_data;
1130 GtkWidget *dialog, *label, *content;
1133 gdk_threads_enter();
1135 gfio_set_connected(gc->ui, 0);
1136 clear_ui_info(gc->ui);
1138 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1140 dialog = gtk_dialog_new_with_buttons("Timed out!",
1141 GTK_WINDOW(gc->ui->window),
1142 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1143 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1145 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1146 label = gtk_label_new((const gchar *) buf);
1147 gtk_container_add(GTK_CONTAINER(content), label);
1148 gtk_widget_show_all(dialog);
1149 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1151 gtk_dialog_run(GTK_DIALOG(dialog));
1152 gtk_widget_destroy(dialog);
1154 gdk_threads_leave();
1157 struct client_ops gfio_client_ops = {
1158 .text_op = gfio_text_op,
1159 .disk_util = gfio_disk_util_op,
1160 .thread_status = gfio_thread_status_op,
1161 .group_stats = gfio_group_stats_op,
1162 .eta = gfio_update_eta,
1163 .probe = gfio_probe_op,
1164 .quit = gfio_quit_op,
1165 .add_job = gfio_add_job_op,
1166 .timed_out = gfio_client_timed_out,
1167 .stay_connected = 1,
1170 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1171 __attribute__((unused)) gpointer data)
1176 static void *job_thread(void *arg)
1178 fio_handle_clients(&gfio_client_ops);
1182 static int send_job_files(struct gui *ui)
1186 for (i = 0; i < ui->nr_job_files; i++) {
1187 ret = fio_clients_send_ini(ui->job_files[i]);
1191 free(ui->job_files[i]);
1192 ui->job_files[i] = NULL;
1194 while (i < ui->nr_job_files) {
1195 free(ui->job_files[i]);
1196 ui->job_files[i] = NULL;
1203 static void start_job_thread(struct gui *ui)
1205 if (send_job_files(ui)) {
1206 printf("Yeah, I didn't really like those options too much.\n");
1207 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1212 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1215 struct gui *ui = data;
1217 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1218 start_job_thread(ui);
1221 static void file_open(GtkWidget *w, gpointer data);
1223 static void connect_clicked(GtkWidget *widget, gpointer data)
1225 struct gui *ui = data;
1227 if (!ui->connected) {
1228 if (!ui->nr_job_files)
1229 file_open(widget, data);
1230 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1231 if (!fio_clients_connect()) {
1232 pthread_create(&ui->t, NULL, job_thread, NULL);
1233 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1236 fio_clients_terminate();
1237 gfio_set_connected(ui, 0);
1242 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1243 struct button_spec *buttonspec)
1245 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1246 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1247 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1248 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1249 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1252 static void add_buttons(struct gui *ui,
1253 struct button_spec *buttonlist,
1258 for (i = 0; i < nbuttons; i++)
1259 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1262 static void on_info_bar_response(GtkWidget *widget, gint response,
1265 if (response == GTK_RESPONSE_OK) {
1266 gtk_widget_destroy(widget);
1267 ui.error_info_bar = NULL;
1271 void report_error(GError *error)
1273 if (ui.error_info_bar == NULL) {
1274 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1277 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1278 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1281 ui.error_label = gtk_label_new(error->message);
1282 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1283 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1285 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1286 gtk_widget_show_all(ui.vbox);
1289 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1290 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1294 static int get_connection_details(char **host, int *port, int *type,
1297 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1301 dialog = gtk_dialog_new_with_buttons("Connection details",
1302 GTK_WINDOW(ui.window),
1303 GTK_DIALOG_DESTROY_WITH_PARENT,
1304 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1305 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1307 frame = gtk_frame_new("Hostname / socket name");
1308 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1309 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1311 box = gtk_vbox_new(FALSE, 6);
1312 gtk_container_add(GTK_CONTAINER(frame), box);
1314 hbox = gtk_hbox_new(TRUE, 10);
1315 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1316 hentry = gtk_entry_new();
1317 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1318 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1320 frame = gtk_frame_new("Port");
1321 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1322 box = gtk_vbox_new(FALSE, 10);
1323 gtk_container_add(GTK_CONTAINER(frame), box);
1325 hbox = gtk_hbox_new(TRUE, 4);
1326 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1327 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1329 frame = gtk_frame_new("Type");
1330 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1331 box = gtk_vbox_new(FALSE, 10);
1332 gtk_container_add(GTK_CONTAINER(frame), box);
1334 hbox = gtk_hbox_new(TRUE, 4);
1335 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1337 combo = gtk_combo_box_new_text();
1338 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1339 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1340 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
1341 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1343 gtk_container_add(GTK_CONTAINER(hbox), combo);
1345 frame = gtk_frame_new("Options");
1346 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1347 box = gtk_vbox_new(FALSE, 10);
1348 gtk_container_add(GTK_CONTAINER(frame), box);
1350 hbox = gtk_hbox_new(TRUE, 4);
1351 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1353 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1354 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1355 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.");
1356 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1358 gtk_widget_show_all(dialog);
1360 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1361 gtk_widget_destroy(dialog);
1365 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1366 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1368 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1369 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1370 *type = Fio_client_ipv4;
1371 else if (!strncmp(typeentry, "IPv6", 4))
1372 *type = Fio_client_ipv6;
1374 *type = Fio_client_socket;
1377 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1379 gtk_widget_destroy(dialog);
1383 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1385 struct gfio_client *gc;
1387 gc = malloc(sizeof(*gc));
1388 memset(gc, 0, sizeof(*gc));
1391 client->client_data = gc;
1394 static void file_open(GtkWidget *w, gpointer data)
1397 GSList *filenames, *fn_glist;
1398 GtkFileFilter *filter;
1400 int port, type, server_start;
1402 dialog = gtk_file_chooser_dialog_new("Open File",
1403 GTK_WINDOW(ui.window),
1404 GTK_FILE_CHOOSER_ACTION_OPEN,
1405 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1406 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1408 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1410 filter = gtk_file_filter_new();
1411 gtk_file_filter_add_pattern(filter, "*.fio");
1412 gtk_file_filter_add_pattern(filter, "*.job");
1413 gtk_file_filter_add_mime_type(filter, "text/fio");
1414 gtk_file_filter_set_name(filter, "Fio job file");
1415 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1417 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1418 gtk_widget_destroy(dialog);
1422 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1424 gtk_widget_destroy(dialog);
1426 if (get_connection_details(&host, &port, &type, &server_start))
1429 filenames = fn_glist;
1430 while (filenames != NULL) {
1431 struct fio_client *client;
1433 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1434 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1437 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1441 error = g_error_new(g_quark_from_string("fio"), 1,
1442 "Failed to add client %s", host);
1443 report_error(error);
1444 g_error_free(error);
1446 gfio_client_added(&ui, client);
1448 g_free(filenames->data);
1449 filenames = g_slist_next(filenames);
1453 g_slist_free(fn_glist);
1456 static void file_save(GtkWidget *w, gpointer data)
1460 dialog = gtk_file_chooser_dialog_new("Save File",
1461 GTK_WINDOW(ui.window),
1462 GTK_FILE_CHOOSER_ACTION_SAVE,
1463 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1464 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1467 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1468 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1470 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1473 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1474 // save_job_file(filename);
1477 gtk_widget_destroy(dialog);
1480 static void view_log_destroy(GtkWidget *w, gpointer data)
1482 struct gui *ui = (struct gui *) data;
1484 gtk_widget_ref(ui->log_tree);
1485 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1486 gtk_widget_destroy(w);
1487 ui->log_view = NULL;
1490 static void view_log(GtkWidget *w, gpointer data)
1492 GtkWidget *win, *scroll, *vbox, *box;
1493 struct gui *ui = (struct gui *) data;
1498 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1499 gtk_window_set_title(GTK_WINDOW(win), "Log");
1500 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1502 scroll = gtk_scrolled_window_new(NULL, NULL);
1504 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1506 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1508 box = gtk_hbox_new(TRUE, 0);
1509 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1510 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1511 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1513 vbox = gtk_vbox_new(TRUE, 5);
1514 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1516 gtk_container_add(GTK_CONTAINER(win), vbox);
1517 gtk_widget_show_all(win);
1520 static void preferences(GtkWidget *w, gpointer data)
1522 GtkWidget *dialog, *frame, *box, **buttons;
1525 dialog = gtk_dialog_new_with_buttons("Preferences",
1526 GTK_WINDOW(ui.window),
1527 GTK_DIALOG_DESTROY_WITH_PARENT,
1528 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1529 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1532 frame = gtk_frame_new("Debug logging");
1533 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1534 box = gtk_hbox_new(FALSE, 6);
1535 gtk_container_add(GTK_CONTAINER(frame), box);
1537 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1539 for (i = 0; i < FD_DEBUG_MAX; i++) {
1540 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1541 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1542 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1545 gtk_widget_show_all(dialog);
1547 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1548 gtk_widget_destroy(dialog);
1552 for (i = 0; i < FD_DEBUG_MAX; i++) {
1555 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1557 fio_debug |= (1UL << i);
1560 gtk_widget_destroy(dialog);
1563 static void about_dialog(GtkWidget *w, gpointer data)
1565 gtk_show_about_dialog(NULL,
1566 "program-name", "gfio",
1567 "comments", "Gtk2 UI for fio",
1569 "version", fio_version_string,
1570 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1571 "logo-icon-name", "fio",
1577 static GtkActionEntry menu_items[] = {
1578 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1579 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1580 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1581 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1582 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1583 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1584 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1585 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1586 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1588 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1590 static const gchar *ui_string = " \
1592 <menubar name=\"MainMenu\"> \
1593 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1594 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1595 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1596 <separator name=\"Separator\"/> \
1597 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1598 <separator name=\"Separator2\"/> \
1599 <menuitem name=\"Quit\" action=\"Quit\" /> \
1601 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1602 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1604 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1605 <menuitem name=\"About\" action=\"About\" /> \
1611 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1614 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1617 action_group = gtk_action_group_new("Menu");
1618 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1620 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1621 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1623 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1624 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1627 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1628 GtkWidget *vbox, GtkUIManager *ui_manager)
1630 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1633 static void init_ui(int *argc, char **argv[], struct gui *ui)
1635 GtkSettings *settings;
1636 GtkUIManager *uimanager;
1637 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1639 memset(ui, 0, sizeof(*ui));
1641 /* Magical g*thread incantation, you just need this thread stuff.
1642 * Without it, the update that happens in gfio_update_thread_status
1643 * doesn't really happen in a timely fashion, you need expose events
1645 if (!g_thread_supported())
1646 g_thread_init(NULL);
1649 gtk_init(argc, argv);
1650 settings = gtk_settings_get_default();
1651 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1654 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1655 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1656 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1658 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1659 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1661 ui->vbox = gtk_vbox_new(FALSE, 0);
1662 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1664 uimanager = gtk_ui_manager_new();
1665 menu = get_menubar_menu(ui->window, uimanager, ui);
1666 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1669 * Set up alignments for widgets at the top of ui,
1670 * align top left, expand horizontally but not vertically
1672 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1673 ui->topvbox = gtk_vbox_new(FALSE, 3);
1674 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1675 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1677 probe = gtk_frame_new("Job");
1678 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1679 probe_frame = gtk_vbox_new(FALSE, 3);
1680 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1682 probe_box = gtk_hbox_new(FALSE, 3);
1683 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1684 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1685 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1686 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1687 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1689 probe_box = gtk_hbox_new(FALSE, 3);
1690 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1692 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1693 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1694 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1695 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1696 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1697 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1699 probe_box = gtk_hbox_new(FALSE, 3);
1700 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1701 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1702 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1703 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1704 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1707 * Only add this if we have a commit rate
1710 probe_box = gtk_hbox_new(FALSE, 3);
1711 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1713 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1714 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1716 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1717 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1721 * Add a text box for text op messages
1723 ui->textview = gtk_text_view_new();
1724 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1725 gtk_text_buffer_set_text(ui->text, "", -1);
1726 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1727 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1728 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1729 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1730 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1731 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1732 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1736 * Set up alignments for widgets at the bottom of ui,
1737 * align bottom left, expand horizontally but not vertically
1739 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1740 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1741 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1742 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1745 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1748 * Set up thread status progress bar
1750 ui->thread_status_pb = gtk_progress_bar_new();
1751 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1752 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1753 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1755 gfio_ui_setup_log(ui);
1757 gtk_widget_show_all(ui->window);
1760 int main(int argc, char *argv[], char *envp[])
1762 if (initialize_fio(envp))
1764 if (fio_init_options())
1767 init_ui(&argc, &argv, &ui);
1769 gdk_threads_enter();
1771 gdk_threads_leave();