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
35 static int gfio_server_running;
36 static const char *gfio_graph_font;
38 static void gfio_update_thread_status(char *status_message, double perc);
39 static void view_log(GtkWidget *w, gpointer data);
41 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
43 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
45 static void connect_clicked(GtkWidget *widget, gpointer data);
46 static void start_job_clicked(GtkWidget *widget, gpointer data);
48 static struct button_spec {
49 const char *buttontext;
51 const char *tooltiptext;
52 const int start_insensitive;
53 } buttonspeclist[] = {
54 #define CONNECT_BUTTON 0
55 #define START_JOB_BUTTON 1
56 { "Connect", connect_clicked, "Connect to host", 0 },
59 "Send current fio job to fio server to be executed", 1 },
81 GtkWidget *write_iops;
91 GtkWidget *bottomalign;
92 GtkWidget *thread_status_pb;
94 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
95 GtkWidget *scrolled_window;
96 #define DRAWING_AREA_XDIM 1000
97 #define DRAWING_AREA_YDIM 400
98 GtkWidget *drawing_area;
99 int drawing_area_xdim;
100 int drawing_area_ydim;
101 GtkWidget *error_info_bar;
102 GtkWidget *error_label;
103 GtkWidget *results_notebook;
104 GtkWidget *results_window;
105 GtkListStore *log_model;
109 struct probe_widget probe;
110 struct eta_widget eta;
115 struct graph *iops_graph;
116 struct graph *bandwidth_graph;
117 struct fio_client *client;
124 GtkWidget *results_widget;
125 GtkWidget *disk_util_frame;
126 GtkWidget *err_entry;
129 static void setup_iops_graph(struct gui *ui)
132 graph_free(ui->iops_graph);
133 ui->iops_graph = graph_new(DRAWING_AREA_XDIM / 2.0,
134 DRAWING_AREA_YDIM, gfio_graph_font);
135 graph_title(ui->iops_graph, "IOPS");
136 graph_x_title(ui->iops_graph, "Time (secs)");
137 graph_y_title(ui->iops_graph, "IOs / sec");
138 graph_add_label(ui->iops_graph, "Read IOPS");
139 graph_add_label(ui->iops_graph, "Write IOPS");
140 graph_set_color(ui->iops_graph, "Read IOPS", 0.13, 0.54, 0.13);
141 graph_set_color(ui->iops_graph, "Write IOPS", 1.0, 0.0, 0.0);
142 line_graph_set_data_count_limit(ui->iops_graph, 100);
145 static void setup_bandwidth_graph(struct gui *ui)
147 if (ui->bandwidth_graph)
148 graph_free(ui->bandwidth_graph);
149 ui->bandwidth_graph = graph_new(DRAWING_AREA_XDIM / 2.0,
150 DRAWING_AREA_YDIM, gfio_graph_font);
151 graph_title(ui->bandwidth_graph, "Bandwidth");
152 graph_x_title(ui->bandwidth_graph, "Time (secs)");
153 graph_y_title(ui->bandwidth_graph, "Kbytes / sec");
154 graph_add_label(ui->bandwidth_graph, "Read Bandwidth");
155 graph_add_label(ui->bandwidth_graph, "Write Bandwidth");
156 graph_set_color(ui->bandwidth_graph, "Read Bandwidth", 0.13, 0.54, 0.13);
157 graph_set_color(ui->bandwidth_graph, "Write Bandwidth", 1.0, 0.0, 0.0);
158 line_graph_set_data_count_limit(ui->bandwidth_graph, 100);
161 static void clear_ui_info(struct gui *ui)
163 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
164 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
165 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
166 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
167 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
168 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
169 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
170 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
171 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
172 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
173 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
174 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
175 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
176 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
179 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
181 GtkWidget *entry, *frame;
183 frame = gtk_frame_new(label);
184 entry = gtk_entry_new();
185 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
186 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
187 gtk_container_add(GTK_CONTAINER(frame), entry);
192 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
194 GtkWidget *label_widget;
197 frame = gtk_frame_new(label);
198 label_widget = gtk_label_new(NULL);
199 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
200 gtk_container_add(GTK_CONTAINER(frame), label_widget);
205 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
207 GtkWidget *button, *box;
209 box = gtk_hbox_new(FALSE, 3);
210 gtk_container_add(GTK_CONTAINER(hbox), box);
212 button = gtk_spin_button_new_with_range(min, max, 1.0);
213 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
215 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
216 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
221 static void gfio_set_connected(struct gui *ui, int connected)
224 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
226 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
227 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
230 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
231 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
232 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
236 static void label_set_int_value(GtkWidget *entry, unsigned int val)
240 sprintf(tmp, "%u", val);
241 gtk_label_set_text(GTK_LABEL(entry), tmp);
244 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
248 sprintf(tmp, "%u", val);
249 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
253 #define ALIGN_RIGHT 2
257 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
259 GtkCellRenderer *renderer;
260 GtkTreeViewColumn *col;
261 double xalign = 0.0; /* left as default */
262 PangoAlignment align;
265 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
266 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
268 visible = !(flags & INVISIBLE);
270 renderer = gtk_cell_renderer_text_new();
271 col = gtk_tree_view_column_new();
273 gtk_tree_view_column_set_title(col, title);
274 if (!(flags & UNSORTABLE))
275 gtk_tree_view_column_set_sort_column_id(col, index);
276 gtk_tree_view_column_set_resizable(col, TRUE);
277 gtk_tree_view_column_pack_start(col, renderer, TRUE);
278 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
279 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
281 case PANGO_ALIGN_LEFT:
284 case PANGO_ALIGN_CENTER:
287 case PANGO_ALIGN_RIGHT:
291 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
292 gtk_tree_view_column_set_visible(col, visible);
293 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
297 static void gfio_ui_setup_log(struct gui *ui)
299 GtkTreeSelection *selection;
301 GtkWidget *tree_view;
303 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
305 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
306 gtk_widget_set_can_focus(tree_view, FALSE);
308 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
309 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
310 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
311 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
313 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
314 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
315 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
316 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
318 ui->log_model = model;
319 ui->log_tree = tree_view;
322 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
328 GType types[FIO_IO_U_LIST_MAX_LEN];
329 GtkWidget *tree_view;
330 GtkTreeSelection *selection;
335 for (i = 0; i < len; i++)
336 types[i] = G_TYPE_INT;
338 model = gtk_list_store_newv(len, types);
340 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
341 gtk_widget_set_can_focus(tree_view, FALSE);
343 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
344 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
346 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
347 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
349 for (i = 0; i < len; i++) {
352 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
353 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
356 gtk_list_store_append(model, &iter);
358 for (i = 0; i < len; i++) {
360 ovals[i] = (ovals[i] + 999) / 1000;
361 gtk_list_store_set(model, &iter, i, ovals[i], -1);
367 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
370 unsigned int *io_u_plat = ts->io_u_plat[ddir];
371 unsigned long nr = ts->clat_stat[ddir].samples;
372 fio_fp64_t *plist = ts->percentile_list;
373 unsigned int *ovals, len, minv, maxv, scale_down;
375 GtkWidget *tree_view, *frame, *hbox;
378 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
383 * We default to usecs, but if the value range is such that we
384 * should scale down to msecs, do that.
386 if (minv > 2000 && maxv > 99999) {
394 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
396 sprintf(tmp, "Completion percentiles (%s)", base);
397 frame = gtk_frame_new(tmp);
398 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
400 hbox = gtk_hbox_new(FALSE, 3);
401 gtk_container_add(GTK_CONTAINER(frame), hbox);
403 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
409 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
410 unsigned long max, double mean, double dev)
412 const char *base = "(usec)";
413 GtkWidget *hbox, *label, *frame;
417 if (!usec_to_msec(&min, &max, &mean, &dev))
420 minp = num2str(min, 6, 1, 0);
421 maxp = num2str(max, 6, 1, 0);
423 sprintf(tmp, "%s %s", name, base);
424 frame = gtk_frame_new(tmp);
425 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
427 hbox = gtk_hbox_new(FALSE, 3);
428 gtk_container_add(GTK_CONTAINER(frame), hbox);
430 label = new_info_label_in_frame(hbox, "Minimum");
431 gtk_label_set_text(GTK_LABEL(label), minp);
432 label = new_info_label_in_frame(hbox, "Maximum");
433 gtk_label_set_text(GTK_LABEL(label), maxp);
434 label = new_info_label_in_frame(hbox, "Average");
435 sprintf(tmp, "%5.02f", mean);
436 gtk_label_set_text(GTK_LABEL(label), tmp);
437 label = new_info_label_in_frame(hbox, "Standard deviation");
438 sprintf(tmp, "%5.02f", dev);
439 gtk_label_set_text(GTK_LABEL(label), tmp);
450 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
451 struct thread_stat *ts, int ddir)
453 const char *ddir_label[2] = { "Read", "Write" };
454 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
455 unsigned long min[3], max[3], runt;
456 unsigned long long bw, iops;
457 unsigned int flags = 0;
458 double mean[3], dev[3];
459 char *io_p, *bw_p, *iops_p;
462 if (!ts->runtime[ddir])
465 i2p = is_power_of_2(rs->kb_base);
466 runt = ts->runtime[ddir];
468 bw = (1000 * ts->io_bytes[ddir]) / runt;
469 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
470 bw_p = num2str(bw, 6, 1, i2p);
472 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
473 iops_p = num2str(iops, 6, 1, 0);
475 box = gtk_hbox_new(FALSE, 3);
476 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
478 frame = gtk_frame_new(ddir_label[ddir]);
479 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
481 main_vbox = gtk_vbox_new(FALSE, 3);
482 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
484 box = gtk_hbox_new(FALSE, 3);
485 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
487 label = new_info_label_in_frame(box, "IO");
488 gtk_label_set_text(GTK_LABEL(label), io_p);
489 label = new_info_label_in_frame(box, "Bandwidth");
490 gtk_label_set_text(GTK_LABEL(label), bw_p);
491 label = new_info_label_in_frame(box, "IOPS");
492 gtk_label_set_text(GTK_LABEL(label), iops_p);
493 label = new_info_label_in_frame(box, "Runtime (msec)");
494 label_set_int_value(label, ts->runtime[ddir]);
496 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
497 double p_of_agg = 100.0;
498 const char *bw_str = "KB";
502 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
503 if (p_of_agg > 100.0)
507 if (mean[0] > 999999.9) {
515 sprintf(tmp, "Bandwidth (%s)", bw_str);
516 frame = gtk_frame_new(tmp);
517 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
519 box = gtk_hbox_new(FALSE, 3);
520 gtk_container_add(GTK_CONTAINER(frame), box);
522 label = new_info_label_in_frame(box, "Minimum");
523 label_set_int_value(label, min[0]);
524 label = new_info_label_in_frame(box, "Maximum");
525 label_set_int_value(label, max[0]);
526 label = new_info_label_in_frame(box, "Percentage of jobs");
527 sprintf(tmp, "%3.2f%%", p_of_agg);
528 gtk_label_set_text(GTK_LABEL(label), tmp);
529 label = new_info_label_in_frame(box, "Average");
530 sprintf(tmp, "%5.02f", mean[0]);
531 gtk_label_set_text(GTK_LABEL(label), tmp);
532 label = new_info_label_in_frame(box, "Standard deviation");
533 sprintf(tmp, "%5.02f", dev[0]);
534 gtk_label_set_text(GTK_LABEL(label), tmp);
537 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
539 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
541 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
545 frame = gtk_frame_new("Latency");
546 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
548 vbox = gtk_vbox_new(FALSE, 3);
549 gtk_container_add(GTK_CONTAINER(frame), vbox);
551 if (flags & GFIO_SLAT)
552 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
553 if (flags & GFIO_CLAT)
554 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
555 if (flags & GFIO_LAT)
556 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
559 if (ts->clat_percentiles)
560 gfio_show_clat_percentiles(main_vbox, ts, ddir);
568 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
571 GtkWidget *tree_view;
572 GtkTreeSelection *selection;
579 * Check if all are empty, in which case don't bother
581 for (i = 0, skipped = 0; i < num; i++)
588 types = malloc(num * sizeof(GType));
590 for (i = 0; i < num; i++)
591 types[i] = G_TYPE_STRING;
593 model = gtk_list_store_newv(num, types);
597 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
598 gtk_widget_set_can_focus(tree_view, FALSE);
600 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
601 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
603 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
604 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
606 for (i = 0; i < num; i++)
607 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
609 gtk_list_store_append(model, &iter);
611 for (i = 0; i < num; i++) {
615 sprintf(fbuf, "0.00");
617 sprintf(fbuf, "%3.2f%%", lat[i]);
619 gtk_list_store_set(model, &iter, i, fbuf, -1);
625 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
627 GtkWidget *box, *frame, *tree_view;
628 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
629 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
630 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
631 "250", "500", "750", "1000", };
632 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
633 "250", "500", "750", "1000", "2000",
636 stat_calc_lat_u(ts, io_u_lat_u);
637 stat_calc_lat_m(ts, io_u_lat_m);
639 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
641 frame = gtk_frame_new("Latency buckets (usec)");
642 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
644 box = gtk_hbox_new(FALSE, 3);
645 gtk_container_add(GTK_CONTAINER(frame), box);
646 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
649 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
651 frame = gtk_frame_new("Latency buckets (msec)");
652 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
654 box = gtk_hbox_new(FALSE, 3);
655 gtk_container_add(GTK_CONTAINER(frame), box);
656 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
660 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
662 GtkWidget *box, *frame, *entry;
663 double usr_cpu, sys_cpu;
664 unsigned long runtime;
667 runtime = ts->total_run_time;
669 double runt = (double) runtime;
671 usr_cpu = (double) ts->usr_time * 100 / runt;
672 sys_cpu = (double) ts->sys_time * 100 / runt;
678 frame = gtk_frame_new("OS resources");
679 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
681 box = gtk_hbox_new(FALSE, 3);
682 gtk_container_add(GTK_CONTAINER(frame), box);
684 entry = new_info_entry_in_frame(box, "User CPU");
685 sprintf(tmp, "%3.2f%%", usr_cpu);
686 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
687 entry = new_info_entry_in_frame(box, "System CPU");
688 sprintf(tmp, "%3.2f%%", sys_cpu);
689 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
690 entry = new_info_entry_in_frame(box, "Context switches");
691 entry_set_int_value(entry, ts->ctx);
692 entry = new_info_entry_in_frame(box, "Major faults");
693 entry_set_int_value(entry, ts->majf);
694 entry = new_info_entry_in_frame(box, "Minor faults");
695 entry_set_int_value(entry, ts->minf);
697 static void gfio_add_sc_depths_tree(GtkListStore *model,
698 struct thread_stat *ts, unsigned int len,
701 double io_u_dist[FIO_IO_U_MAP_NR];
703 /* Bits 0, and 3-8 */
704 const int add_mask = 0x1f9;
708 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
710 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
712 gtk_list_store_append(model, &iter);
714 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
716 for (i = 1, j = 0; i < len; i++) {
719 if (!(add_mask & (1UL << (i - 1))))
720 sprintf(fbuf, "0.0%%");
722 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
726 gtk_list_store_set(model, &iter, i, fbuf, -1);
731 static void gfio_add_total_depths_tree(GtkListStore *model,
732 struct thread_stat *ts, unsigned int len)
734 double io_u_dist[FIO_IO_U_MAP_NR];
736 /* Bits 1-6, and 8 */
737 const int add_mask = 0x17e;
740 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
742 gtk_list_store_append(model, &iter);
744 gtk_list_store_set(model, &iter, 0, "Total", -1);
746 for (i = 1, j = 0; i < len; i++) {
749 if (!(add_mask & (1UL << (i - 1))))
750 sprintf(fbuf, "0.0%%");
752 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
756 gtk_list_store_set(model, &iter, i, fbuf, -1);
761 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
763 GtkWidget *frame, *box, *tree_view;
764 GtkTreeSelection *selection;
766 GType types[FIO_IO_U_MAP_NR + 1];
769 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
771 frame = gtk_frame_new("IO depths");
772 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
774 box = gtk_hbox_new(FALSE, 3);
775 gtk_container_add(GTK_CONTAINER(frame), box);
777 for (i = 0; i < NR_LABELS; i++)
778 types[i] = G_TYPE_STRING;
780 model = gtk_list_store_newv(NR_LABELS, types);
782 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
783 gtk_widget_set_can_focus(tree_view, FALSE);
785 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
786 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
788 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
789 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
791 for (i = 0; i < NR_LABELS; i++)
792 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
794 gfio_add_total_depths_tree(model, ts, NR_LABELS);
795 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
796 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
798 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
801 static gboolean results_window_delete(GtkWidget *w, gpointer data)
803 struct gui *ui = (struct gui *) data;
805 gtk_widget_destroy(w);
806 ui->results_window = NULL;
807 ui->results_notebook = NULL;
811 static GtkWidget *get_results_window(struct gui *ui)
813 GtkWidget *win, *notebook;
815 if (ui->results_window)
816 return ui->results_notebook;
818 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
819 gtk_window_set_title(GTK_WINDOW(win), "Results");
820 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
821 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
822 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
824 notebook = gtk_notebook_new();
825 gtk_container_add(GTK_CONTAINER(win), notebook);
827 ui->results_window = win;
828 ui->results_notebook = notebook;
829 return ui->results_notebook;
832 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
833 struct group_run_stats *rs)
835 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
836 struct gfio_client *gc = client->client_data;
840 res_win = get_results_window(gc->ui);
842 scroll = gtk_scrolled_window_new(NULL, NULL);
843 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
844 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
846 vbox = gtk_vbox_new(FALSE, 3);
848 box = gtk_hbox_new(FALSE, 0);
849 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
851 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
853 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
855 gc->results_widget = vbox;
857 entry = new_info_entry_in_frame(box, "Name");
858 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
859 if (strlen(ts->description)) {
860 entry = new_info_entry_in_frame(box, "Description");
861 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
863 entry = new_info_entry_in_frame(box, "Group ID");
864 entry_set_int_value(entry, ts->groupid);
865 entry = new_info_entry_in_frame(box, "Jobs");
866 entry_set_int_value(entry, ts->members);
867 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
868 entry_set_int_value(entry, ts->error);
869 entry = new_info_entry_in_frame(box, "PID");
870 entry_set_int_value(entry, ts->pid);
872 if (ts->io_bytes[DDIR_READ])
873 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
874 if (ts->io_bytes[DDIR_WRITE])
875 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
877 gfio_show_latency_buckets(vbox, ts);
878 gfio_show_cpu_usage(vbox, ts);
879 gfio_show_io_depths(vbox, ts);
881 gtk_widget_show_all(gc->ui->results_window);
885 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
887 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
888 struct gfio_client *gc = client->client_data;
892 char tmp[64], timebuf[80];
895 tm = localtime(&sec);
896 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
897 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
901 gtk_list_store_append(gc->ui->log_model, &iter);
902 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
903 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
904 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
905 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
907 if (p->level == FIO_LOG_ERR)
908 view_log(NULL, (gpointer) gc->ui);
913 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
915 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
916 struct gfio_client *gc = client->client_data;
917 GtkWidget *box, *frame, *entry, *vbox;
923 if (!gc->results_widget)
926 if (!gc->disk_util_frame) {
927 gc->disk_util_frame = gtk_frame_new("Disk utilization");
928 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
931 vbox = gtk_vbox_new(FALSE, 3);
932 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
934 frame = gtk_frame_new((char *) p->dus.name);
935 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
937 box = gtk_vbox_new(FALSE, 3);
938 gtk_container_add(GTK_CONTAINER(frame), box);
940 frame = gtk_frame_new("Read");
941 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
942 vbox = gtk_hbox_new(TRUE, 3);
943 gtk_container_add(GTK_CONTAINER(frame), vbox);
944 entry = new_info_entry_in_frame(vbox, "IOs");
945 entry_set_int_value(entry, p->dus.ios[0]);
946 entry = new_info_entry_in_frame(vbox, "Merges");
947 entry_set_int_value(entry, p->dus.merges[0]);
948 entry = new_info_entry_in_frame(vbox, "Sectors");
949 entry_set_int_value(entry, p->dus.sectors[0]);
950 entry = new_info_entry_in_frame(vbox, "Ticks");
951 entry_set_int_value(entry, p->dus.ticks[0]);
953 frame = gtk_frame_new("Write");
954 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
955 vbox = gtk_hbox_new(TRUE, 3);
956 gtk_container_add(GTK_CONTAINER(frame), vbox);
957 entry = new_info_entry_in_frame(vbox, "IOs");
958 entry_set_int_value(entry, p->dus.ios[1]);
959 entry = new_info_entry_in_frame(vbox, "Merges");
960 entry_set_int_value(entry, p->dus.merges[1]);
961 entry = new_info_entry_in_frame(vbox, "Sectors");
962 entry_set_int_value(entry, p->dus.sectors[1]);
963 entry = new_info_entry_in_frame(vbox, "Ticks");
964 entry_set_int_value(entry, p->dus.ticks[1]);
966 frame = gtk_frame_new("Shared");
967 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
968 vbox = gtk_hbox_new(TRUE, 3);
969 gtk_container_add(GTK_CONTAINER(frame), vbox);
970 entry = new_info_entry_in_frame(vbox, "IO ticks");
971 entry_set_int_value(entry, p->dus.io_ticks);
972 entry = new_info_entry_in_frame(vbox, "Time in queue");
973 entry_set_int_value(entry, p->dus.time_in_queue);
977 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
981 sprintf(tmp, "%3.2f%%", util);
982 entry = new_info_entry_in_frame(vbox, "Disk utilization");
983 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
985 gtk_widget_show_all(gc->results_widget);
990 extern int sum_stat_clients;
991 extern struct thread_stat client_ts;
992 extern struct group_run_stats client_gs;
994 static int sum_stat_nr;
996 static void gfio_thread_status_op(struct fio_client *client,
997 struct fio_net_cmd *cmd)
999 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1001 gfio_display_ts(client, &p->ts, &p->rs);
1003 if (sum_stat_clients == 1)
1006 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1007 sum_group_stats(&client_gs, &p->rs);
1009 client_ts.members++;
1010 client_ts.groupid = p->ts.groupid;
1012 if (++sum_stat_nr == sum_stat_clients) {
1013 strcpy(client_ts.name, "All clients");
1014 gfio_display_ts(client, &client_ts, &client_gs);
1018 static void gfio_group_stats_op(struct fio_client *client,
1019 struct fio_net_cmd *cmd)
1021 gdk_threads_enter();
1022 printf("gfio_group_stats_op called\n");
1023 fio_client_ops.group_stats(client, cmd);
1024 gdk_threads_leave();
1027 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event)
1029 ui.drawing_area_xdim = w->allocation.width;
1030 ui.drawing_area_ydim = w->allocation.height;
1034 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1036 struct gui *ui = (struct gui *) p;
1039 graph_set_size(ui->iops_graph, ui->drawing_area_xdim / 2.0,
1040 ui->drawing_area_ydim);
1041 graph_set_size(ui->bandwidth_graph, ui->drawing_area_xdim / 2.0,
1042 ui->drawing_area_ydim);
1043 cr = gdk_cairo_create(w->window);
1045 cairo_set_source_rgb(cr, 0, 0, 0);
1048 cairo_translate(cr, 0, 0);
1049 line_graph_draw(ui->bandwidth_graph, cr);
1054 cairo_translate(cr, ui->drawing_area_xdim / 2.0, 0);
1055 line_graph_draw(ui->iops_graph, cr);
1063 static void gfio_update_eta(struct jobs_eta *je)
1065 static int eta_good;
1072 gdk_threads_enter();
1077 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1078 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1079 eta_to_str(eta_str, je->eta_sec);
1082 sprintf(tmp, "%u", je->nr_running);
1083 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
1084 sprintf(tmp, "%u", je->files_open);
1085 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
1088 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1089 if (je->m_rate || je->t_rate) {
1092 mr = num2str(je->m_rate, 4, 0, i2p);
1093 tr = num2str(je->t_rate, 4, 0, i2p);
1094 gtk_entry_set_text(GTK_ENTRY(ui.eta);
1095 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1098 } else if (je->m_iops || je->t_iops)
1099 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1101 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
1102 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
1103 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
1104 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
1107 if (je->eta_sec != INT_MAX && je->nr_running) {
1111 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1112 strcpy(output, "-.-% done");
1116 sprintf(output, "%3.1f%% done", perc);
1119 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1120 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1122 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1123 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1125 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1126 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1127 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1128 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1130 graph_add_xy_data(ui.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1131 graph_add_xy_data(ui.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1132 graph_add_xy_data(ui.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1133 graph_add_xy_data(ui.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1142 char *dst = output + strlen(output);
1144 sprintf(dst, " - %s", eta_str);
1147 gfio_update_thread_status(output, perc);
1148 gdk_threads_leave();
1151 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1153 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1154 struct gfio_client *gc = client->client_data;
1155 struct gui *ui = gc->ui;
1156 const char *os, *arch;
1159 os = fio_get_os_string(probe->os);
1163 arch = fio_get_arch_string(probe->arch);
1168 client->name = strdup((char *) probe->hostname);
1170 gdk_threads_enter();
1172 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1173 gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1174 gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1175 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1176 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1178 gfio_set_connected(ui, 1);
1180 gdk_threads_leave();
1183 static void gfio_update_thread_status(char *status_message, double perc)
1185 static char message[100];
1186 const char *m = message;
1188 strncpy(message, status_message, sizeof(message) - 1);
1189 gtk_progress_bar_set_text(
1190 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1191 gtk_progress_bar_set_fraction(
1192 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1193 gtk_widget_queue_draw(ui.window);
1196 static void gfio_quit_op(struct fio_client *client)
1198 struct gfio_client *gc = client->client_data;
1200 gdk_threads_enter();
1201 gfio_set_connected(gc->ui, 0);
1202 gdk_threads_leave();
1205 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1207 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1208 struct gfio_client *gc = client->client_data;
1209 struct gui *ui = gc->ui;
1213 p->iodepth = le32_to_cpu(p->iodepth);
1214 p->rw = le32_to_cpu(p->rw);
1216 for (i = 0; i < 2; i++) {
1217 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
1218 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
1221 p->numjobs = le32_to_cpu(p->numjobs);
1222 p->group_reporting = le32_to_cpu(p->group_reporting);
1224 gdk_threads_enter();
1226 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1227 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1228 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1230 sprintf(tmp, "%u", p->iodepth);
1231 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1233 gdk_threads_leave();
1236 static void gfio_client_timed_out(struct fio_client *client)
1238 struct gfio_client *gc = client->client_data;
1239 GtkWidget *dialog, *label, *content;
1242 gdk_threads_enter();
1244 gfio_set_connected(gc->ui, 0);
1245 clear_ui_info(gc->ui);
1247 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1249 dialog = gtk_dialog_new_with_buttons("Timed out!",
1250 GTK_WINDOW(gc->ui->window),
1251 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1252 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1254 /* gtk_dialog_get_content_area() is 2.14 and newer */
1255 content = GTK_DIALOG(dialog)->vbox;
1257 label = gtk_label_new((const gchar *) buf);
1258 gtk_container_add(GTK_CONTAINER(content), label);
1259 gtk_widget_show_all(dialog);
1260 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1262 gtk_dialog_run(GTK_DIALOG(dialog));
1263 gtk_widget_destroy(dialog);
1265 gdk_threads_leave();
1268 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1270 struct gfio_client *gc = client->client_data;
1272 gdk_threads_enter();
1274 gfio_set_connected(gc->ui, 0);
1277 entry_set_int_value(gc->err_entry, client->error);
1279 gdk_threads_leave();
1282 struct client_ops gfio_client_ops = {
1283 .text_op = gfio_text_op,
1284 .disk_util = gfio_disk_util_op,
1285 .thread_status = gfio_thread_status_op,
1286 .group_stats = gfio_group_stats_op,
1287 .eta = gfio_update_eta,
1288 .probe = gfio_probe_op,
1289 .quit = gfio_quit_op,
1290 .add_job = gfio_add_job_op,
1291 .timed_out = gfio_client_timed_out,
1292 .stop = gfio_client_stop,
1293 .stay_connected = 1,
1296 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1297 __attribute__((unused)) gpointer data)
1302 static void *job_thread(void *arg)
1304 fio_handle_clients(&gfio_client_ops);
1308 static int send_job_files(struct gui *ui)
1312 for (i = 0; i < ui->nr_job_files; i++) {
1313 ret = fio_clients_send_ini(ui->job_files[i]);
1317 free(ui->job_files[i]);
1318 ui->job_files[i] = NULL;
1320 while (i < ui->nr_job_files) {
1321 free(ui->job_files[i]);
1322 ui->job_files[i] = NULL;
1329 static void start_job_thread(struct gui *ui)
1331 if (send_job_files(ui)) {
1332 printf("Yeah, I didn't really like those options too much.\n");
1333 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1338 static void *server_thread(void *arg)
1341 gfio_server_running = 1;
1342 fio_start_server(NULL);
1343 gfio_server_running = 0;
1347 static void gfio_start_server(struct gui *ui)
1349 if (!gfio_server_running) {
1350 gfio_server_running = 1;
1351 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1352 pthread_detach(ui->server_t);
1356 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1359 struct gui *ui = data;
1361 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1362 start_job_thread(ui);
1365 static void file_open(GtkWidget *w, gpointer data);
1367 static void connect_clicked(GtkWidget *widget, gpointer data)
1369 struct gui *ui = data;
1371 if (!ui->connected) {
1372 if (!ui->nr_job_files)
1373 file_open(widget, data);
1374 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1375 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1376 if (!fio_clients_connect()) {
1377 pthread_create(&ui->t, NULL, job_thread, NULL);
1378 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1381 fio_clients_terminate();
1382 gfio_set_connected(ui, 0);
1387 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1388 struct button_spec *buttonspec)
1390 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1391 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1392 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1393 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1394 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1397 static void add_buttons(struct gui *ui,
1398 struct button_spec *buttonlist,
1403 for (i = 0; i < nbuttons; i++)
1404 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1407 static void on_info_bar_response(GtkWidget *widget, gint response,
1410 if (response == GTK_RESPONSE_OK) {
1411 gtk_widget_destroy(widget);
1412 ui.error_info_bar = NULL;
1416 void report_error(GError *error)
1418 if (ui.error_info_bar == NULL) {
1419 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1422 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1423 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1426 ui.error_label = gtk_label_new(error->message);
1427 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1428 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1430 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1431 gtk_widget_show_all(ui.vbox);
1434 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1435 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1439 struct connection_widgets
1446 static void hostname_cb(GtkEntry *entry, gpointer data)
1448 struct connection_widgets *cw = data;
1449 int uses_net = 0, is_localhost = 0;
1454 * Check whether to display the 'auto start backend' box
1455 * or not. Show it if we are a localhost and using network,
1456 * or using a socket.
1458 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1459 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1464 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1465 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1466 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1467 !strcmp(text, "ip6-loopback"))
1471 if (!uses_net || is_localhost) {
1472 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1473 gtk_widget_set_sensitive(cw->button, 1);
1475 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1476 gtk_widget_set_sensitive(cw->button, 0);
1480 static int get_connection_details(char **host, int *port, int *type,
1483 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1484 struct connection_widgets cw;
1487 dialog = gtk_dialog_new_with_buttons("Connection details",
1488 GTK_WINDOW(ui.window),
1489 GTK_DIALOG_DESTROY_WITH_PARENT,
1490 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1491 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1493 frame = gtk_frame_new("Hostname / socket name");
1494 /* gtk_dialog_get_content_area() is 2.14 and newer */
1495 vbox = GTK_DIALOG(dialog)->vbox;
1496 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1498 box = gtk_vbox_new(FALSE, 6);
1499 gtk_container_add(GTK_CONTAINER(frame), box);
1501 hbox = gtk_hbox_new(TRUE, 10);
1502 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1503 cw.hentry = gtk_entry_new();
1504 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1505 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1507 frame = gtk_frame_new("Port");
1508 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1509 box = gtk_vbox_new(FALSE, 10);
1510 gtk_container_add(GTK_CONTAINER(frame), box);
1512 hbox = gtk_hbox_new(TRUE, 4);
1513 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1514 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1516 frame = gtk_frame_new("Type");
1517 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1518 box = gtk_vbox_new(FALSE, 10);
1519 gtk_container_add(GTK_CONTAINER(frame), box);
1521 hbox = gtk_hbox_new(TRUE, 4);
1522 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1524 cw.combo = gtk_combo_box_new_text();
1525 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1526 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1527 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1528 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1530 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1532 frame = gtk_frame_new("Options");
1533 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1534 box = gtk_vbox_new(FALSE, 10);
1535 gtk_container_add(GTK_CONTAINER(frame), box);
1537 hbox = gtk_hbox_new(TRUE, 4);
1538 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1540 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1541 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1542 gtk_widget_set_tooltip_text(cw.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.");
1543 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1546 * Connect edit signal, so we can show/not-show the auto start button
1548 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1549 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1551 gtk_widget_show_all(dialog);
1553 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1554 gtk_widget_destroy(dialog);
1558 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1559 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1561 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1562 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1563 *type = Fio_client_ipv4;
1564 else if (!strncmp(typeentry, "IPv6", 4))
1565 *type = Fio_client_ipv6;
1567 *type = Fio_client_socket;
1570 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1572 gtk_widget_destroy(dialog);
1576 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1578 struct gfio_client *gc;
1580 gc = malloc(sizeof(*gc));
1581 memset(gc, 0, sizeof(*gc));
1584 client->client_data = gc;
1587 static void file_open(GtkWidget *w, gpointer data)
1590 struct gui *ui = data;
1591 GSList *filenames, *fn_glist;
1592 GtkFileFilter *filter;
1594 int port, type, server_start;
1596 dialog = gtk_file_chooser_dialog_new("Open File",
1597 GTK_WINDOW(ui->window),
1598 GTK_FILE_CHOOSER_ACTION_OPEN,
1599 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1600 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1602 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1604 filter = gtk_file_filter_new();
1605 gtk_file_filter_add_pattern(filter, "*.fio");
1606 gtk_file_filter_add_pattern(filter, "*.job");
1607 gtk_file_filter_add_pattern(filter, "*.ini");
1608 gtk_file_filter_add_mime_type(filter, "text/fio");
1609 gtk_file_filter_set_name(filter, "Fio job file");
1610 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1612 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1613 gtk_widget_destroy(dialog);
1617 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1619 gtk_widget_destroy(dialog);
1621 if (get_connection_details(&host, &port, &type, &server_start))
1624 filenames = fn_glist;
1625 while (filenames != NULL) {
1626 struct fio_client *client;
1628 ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1629 ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1632 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1636 error = g_error_new(g_quark_from_string("fio"), 1,
1637 "Failed to add client %s", host);
1638 report_error(error);
1639 g_error_free(error);
1641 gfio_client_added(ui, client);
1643 g_free(filenames->data);
1644 filenames = g_slist_next(filenames);
1649 gfio_start_server(ui);
1651 g_slist_free(fn_glist);
1654 static void file_save(GtkWidget *w, gpointer data)
1656 struct gui *ui = data;
1659 dialog = gtk_file_chooser_dialog_new("Save File",
1660 GTK_WINDOW(ui->window),
1661 GTK_FILE_CHOOSER_ACTION_SAVE,
1662 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1663 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1666 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1667 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1669 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1672 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1673 // save_job_file(filename);
1676 gtk_widget_destroy(dialog);
1679 static void view_log_destroy(GtkWidget *w, gpointer data)
1681 struct gui *ui = (struct gui *) data;
1683 gtk_widget_ref(ui->log_tree);
1684 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1685 gtk_widget_destroy(w);
1686 ui->log_view = NULL;
1689 static void view_log(GtkWidget *w, gpointer data)
1691 GtkWidget *win, *scroll, *vbox, *box;
1692 struct gui *ui = (struct gui *) data;
1697 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1698 gtk_window_set_title(GTK_WINDOW(win), "Log");
1699 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1701 scroll = gtk_scrolled_window_new(NULL, NULL);
1703 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1705 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1707 box = gtk_hbox_new(TRUE, 0);
1708 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1709 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1710 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1712 vbox = gtk_vbox_new(TRUE, 5);
1713 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1715 gtk_container_add(GTK_CONTAINER(win), vbox);
1716 gtk_widget_show_all(win);
1719 static void preferences(GtkWidget *w, gpointer data)
1721 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1724 dialog = gtk_dialog_new_with_buttons("Preferences",
1725 GTK_WINDOW(ui.window),
1726 GTK_DIALOG_DESTROY_WITH_PARENT,
1727 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1728 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1731 frame = gtk_frame_new("Debug logging");
1732 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1734 vbox = gtk_vbox_new(FALSE, 6);
1735 gtk_container_add(GTK_CONTAINER(frame), vbox);
1737 box = gtk_hbox_new(FALSE, 6);
1738 gtk_container_add(GTK_CONTAINER(vbox), box);
1740 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1742 for (i = 0; i < FD_DEBUG_MAX; i++) {
1744 box = gtk_hbox_new(FALSE, 6);
1745 gtk_container_add(GTK_CONTAINER(vbox), box);
1749 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1750 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1751 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1754 frame = gtk_frame_new("Graph font");
1755 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1756 vbox = gtk_vbox_new(FALSE, 6);
1757 gtk_container_add(GTK_CONTAINER(frame), vbox);
1759 font = gtk_font_button_new();
1760 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
1762 gtk_widget_show_all(dialog);
1764 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1765 gtk_widget_destroy(dialog);
1769 for (i = 0; i < FD_DEBUG_MAX; i++) {
1772 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1774 fio_debug |= (1UL << i);
1777 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1778 gtk_widget_destroy(dialog);
1781 static void about_dialog(GtkWidget *w, gpointer data)
1783 const char *authors[] = {
1784 "Jens Axboe <axboe@kernel.dk>",
1785 "Stephen Carmeron <stephenmcameron@gmail.com>",
1788 const char *license[] = {
1789 "Fio is free software; you can redistribute it and/or modify "
1790 "it under the terms of the GNU General Public License as published by "
1791 "the Free Software Foundation; either version 2 of the License, or "
1792 "(at your option) any later version.\n",
1793 "Fio is distributed in the hope that it will be useful, "
1794 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1795 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1796 "GNU General Public License for more details.\n",
1797 "You should have received a copy of the GNU General Public License "
1798 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1799 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1801 char *license_trans;
1803 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1804 license[2], "\n", NULL);
1806 gtk_show_about_dialog(NULL,
1807 "program-name", "gfio",
1808 "comments", "Gtk2 UI for fio",
1809 "license", license_trans,
1810 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1812 "version", fio_version_string,
1813 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1814 "logo-icon-name", "fio",
1816 "wrap-license", TRUE,
1819 g_free (license_trans);
1822 static GtkActionEntry menu_items[] = {
1823 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1824 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1825 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1826 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1827 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1828 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1829 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1830 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1831 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1833 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1835 static const gchar *ui_string = " \
1837 <menubar name=\"MainMenu\"> \
1838 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1839 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1840 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1841 <separator name=\"Separator\"/> \
1842 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1843 <separator name=\"Separator2\"/> \
1844 <menuitem name=\"Quit\" action=\"Quit\" /> \
1846 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1847 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1849 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1850 <menuitem name=\"About\" action=\"About\" /> \
1856 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1859 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1862 action_group = gtk_action_group_new("Menu");
1863 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1865 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1866 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1868 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1869 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1872 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1873 GtkWidget *vbox, GtkUIManager *ui_manager)
1875 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1878 static void init_ui(int *argc, char **argv[], struct gui *ui)
1880 GtkSettings *settings;
1881 GtkUIManager *uimanager;
1882 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1885 memset(ui, 0, sizeof(*ui));
1887 /* Magical g*thread incantation, you just need this thread stuff.
1888 * Without it, the update that happens in gfio_update_thread_status
1889 * doesn't really happen in a timely fashion, you need expose events
1891 if (!g_thread_supported())
1892 g_thread_init(NULL);
1895 gtk_init(argc, argv);
1896 settings = gtk_settings_get_default();
1897 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1900 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1901 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1902 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 700);
1904 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1905 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1907 ui->vbox = gtk_vbox_new(FALSE, 0);
1908 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1910 uimanager = gtk_ui_manager_new();
1911 menu = get_menubar_menu(ui->window, uimanager, ui);
1912 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1915 * Set up alignments for widgets at the top of ui,
1916 * align top left, expand horizontally but not vertically
1918 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1919 ui->topvbox = gtk_vbox_new(FALSE, 3);
1920 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1921 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1923 probe = gtk_frame_new("Job");
1924 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1925 probe_frame = gtk_vbox_new(FALSE, 3);
1926 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1928 probe_box = gtk_hbox_new(FALSE, 3);
1929 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1930 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1931 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1932 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1933 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1935 probe_box = gtk_hbox_new(FALSE, 3);
1936 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1938 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1939 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1940 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1941 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1942 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1943 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1945 probe_box = gtk_hbox_new(FALSE, 3);
1946 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1947 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1948 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1949 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1950 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1953 * Only add this if we have a commit rate
1956 probe_box = gtk_hbox_new(FALSE, 3);
1957 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1959 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1960 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1962 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1963 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1967 * Set up a drawing area and IOPS and bandwidth graphs
1969 gdk_color_parse("white", &white);
1970 ui->drawing_area = gtk_drawing_area_new();
1971 ui->drawing_area_xdim = DRAWING_AREA_XDIM;
1972 ui->drawing_area_ydim = DRAWING_AREA_YDIM;
1973 gtk_widget_set_size_request(GTK_WIDGET(ui->drawing_area),
1974 ui->drawing_area_xdim, ui->drawing_area_ydim);
1975 gtk_widget_modify_bg(ui->drawing_area, GTK_STATE_NORMAL, &white);
1976 g_signal_connect(G_OBJECT(ui->drawing_area), "expose_event",
1977 G_CALLBACK (on_expose_drawing_area), ui);
1978 g_signal_connect(G_OBJECT(ui->drawing_area), "configure_event",
1979 G_CALLBACK (on_config_drawing_area), ui);
1980 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1981 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1982 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1983 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1985 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1988 setup_iops_graph(ui);
1989 setup_bandwidth_graph(ui);
1992 * Set up alignments for widgets at the bottom of ui,
1993 * align bottom left, expand horizontally but not vertically
1995 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1996 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1997 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1998 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
2001 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
2004 * Set up thread status progress bar
2006 ui->thread_status_pb = gtk_progress_bar_new();
2007 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2008 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2009 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2011 gfio_ui_setup_log(ui);
2013 gtk_widget_show_all(ui->window);
2016 int main(int argc, char *argv[], char *envp[])
2018 if (initialize_fio(envp))
2020 if (fio_init_options())
2023 init_ui(&argc, &argv, &ui);
2025 gdk_threads_enter();
2027 gdk_threads_leave();