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;
127 unsigned int job_added;
128 struct thread_options o;
131 static void setup_iops_graph(struct gui *ui)
134 graph_free(ui->iops_graph);
135 ui->iops_graph = graph_new(DRAWING_AREA_XDIM / 2.0,
136 DRAWING_AREA_YDIM, gfio_graph_font);
137 graph_title(ui->iops_graph, "IOPS");
138 graph_x_title(ui->iops_graph, "Time (secs)");
139 graph_y_title(ui->iops_graph, "IOs / sec");
140 graph_add_label(ui->iops_graph, "Read IOPS");
141 graph_add_label(ui->iops_graph, "Write IOPS");
142 graph_set_color(ui->iops_graph, "Read IOPS", 0.13, 0.54, 0.13);
143 graph_set_color(ui->iops_graph, "Write IOPS", 1.0, 0.0, 0.0);
144 line_graph_set_data_count_limit(ui->iops_graph, 100);
147 static void setup_bandwidth_graph(struct gui *ui)
149 if (ui->bandwidth_graph)
150 graph_free(ui->bandwidth_graph);
151 ui->bandwidth_graph = graph_new(DRAWING_AREA_XDIM / 2.0,
152 DRAWING_AREA_YDIM, gfio_graph_font);
153 graph_title(ui->bandwidth_graph, "Bandwidth");
154 graph_x_title(ui->bandwidth_graph, "Time (secs)");
155 graph_y_title(ui->bandwidth_graph, "Kbytes / sec");
156 graph_add_label(ui->bandwidth_graph, "Read Bandwidth");
157 graph_add_label(ui->bandwidth_graph, "Write Bandwidth");
158 graph_set_color(ui->bandwidth_graph, "Read Bandwidth", 0.13, 0.54, 0.13);
159 graph_set_color(ui->bandwidth_graph, "Write Bandwidth", 1.0, 0.0, 0.0);
160 line_graph_set_data_count_limit(ui->bandwidth_graph, 100);
163 static void clear_ui_info(struct gui *ui)
165 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
166 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
167 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
168 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
169 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
170 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
171 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
172 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
173 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
174 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
175 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
176 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
177 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
178 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
181 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
183 GtkWidget *entry, *frame;
185 frame = gtk_frame_new(label);
186 entry = gtk_entry_new();
187 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
188 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
189 gtk_container_add(GTK_CONTAINER(frame), entry);
194 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
196 GtkWidget *label_widget;
199 frame = gtk_frame_new(label);
200 label_widget = gtk_label_new(NULL);
201 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
202 gtk_container_add(GTK_CONTAINER(frame), label_widget);
207 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
209 GtkWidget *button, *box;
211 box = gtk_hbox_new(FALSE, 3);
212 gtk_container_add(GTK_CONTAINER(hbox), box);
214 button = gtk_spin_button_new_with_range(min, max, 1.0);
215 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
217 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
218 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
223 static void gfio_set_connected(struct gui *ui, int connected)
226 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
228 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
229 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
232 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
233 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
234 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
238 static void label_set_int_value(GtkWidget *entry, unsigned int val)
242 sprintf(tmp, "%u", val);
243 gtk_label_set_text(GTK_LABEL(entry), tmp);
246 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
250 sprintf(tmp, "%u", val);
251 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
255 #define ALIGN_RIGHT 2
259 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
261 GtkCellRenderer *renderer;
262 GtkTreeViewColumn *col;
263 double xalign = 0.0; /* left as default */
264 PangoAlignment align;
267 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
268 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
270 visible = !(flags & INVISIBLE);
272 renderer = gtk_cell_renderer_text_new();
273 col = gtk_tree_view_column_new();
275 gtk_tree_view_column_set_title(col, title);
276 if (!(flags & UNSORTABLE))
277 gtk_tree_view_column_set_sort_column_id(col, index);
278 gtk_tree_view_column_set_resizable(col, TRUE);
279 gtk_tree_view_column_pack_start(col, renderer, TRUE);
280 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
281 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
283 case PANGO_ALIGN_LEFT:
286 case PANGO_ALIGN_CENTER:
289 case PANGO_ALIGN_RIGHT:
293 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
294 gtk_tree_view_column_set_visible(col, visible);
295 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
299 static void gfio_ui_setup_log(struct gui *ui)
301 GtkTreeSelection *selection;
303 GtkWidget *tree_view;
305 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
307 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
308 gtk_widget_set_can_focus(tree_view, FALSE);
310 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
311 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
312 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
313 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
315 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
316 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
317 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
318 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
320 ui->log_model = model;
321 ui->log_tree = tree_view;
324 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
330 GType types[FIO_IO_U_LIST_MAX_LEN];
331 GtkWidget *tree_view;
332 GtkTreeSelection *selection;
337 for (i = 0; i < len; i++)
338 types[i] = G_TYPE_INT;
340 model = gtk_list_store_newv(len, types);
342 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
343 gtk_widget_set_can_focus(tree_view, FALSE);
345 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
346 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
348 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
349 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
351 for (i = 0; i < len; i++) {
354 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
355 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
358 gtk_list_store_append(model, &iter);
360 for (i = 0; i < len; i++) {
362 ovals[i] = (ovals[i] + 999) / 1000;
363 gtk_list_store_set(model, &iter, i, ovals[i], -1);
369 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
372 unsigned int *io_u_plat = ts->io_u_plat[ddir];
373 unsigned long nr = ts->clat_stat[ddir].samples;
374 fio_fp64_t *plist = ts->percentile_list;
375 unsigned int *ovals, len, minv, maxv, scale_down;
377 GtkWidget *tree_view, *frame, *hbox;
380 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
385 * We default to usecs, but if the value range is such that we
386 * should scale down to msecs, do that.
388 if (minv > 2000 && maxv > 99999) {
396 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
398 sprintf(tmp, "Completion percentiles (%s)", base);
399 frame = gtk_frame_new(tmp);
400 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
402 hbox = gtk_hbox_new(FALSE, 3);
403 gtk_container_add(GTK_CONTAINER(frame), hbox);
405 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
411 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
412 unsigned long max, double mean, double dev)
414 const char *base = "(usec)";
415 GtkWidget *hbox, *label, *frame;
419 if (!usec_to_msec(&min, &max, &mean, &dev))
422 minp = num2str(min, 6, 1, 0);
423 maxp = num2str(max, 6, 1, 0);
425 sprintf(tmp, "%s %s", name, base);
426 frame = gtk_frame_new(tmp);
427 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
429 hbox = gtk_hbox_new(FALSE, 3);
430 gtk_container_add(GTK_CONTAINER(frame), hbox);
432 label = new_info_label_in_frame(hbox, "Minimum");
433 gtk_label_set_text(GTK_LABEL(label), minp);
434 label = new_info_label_in_frame(hbox, "Maximum");
435 gtk_label_set_text(GTK_LABEL(label), maxp);
436 label = new_info_label_in_frame(hbox, "Average");
437 sprintf(tmp, "%5.02f", mean);
438 gtk_label_set_text(GTK_LABEL(label), tmp);
439 label = new_info_label_in_frame(hbox, "Standard deviation");
440 sprintf(tmp, "%5.02f", dev);
441 gtk_label_set_text(GTK_LABEL(label), tmp);
452 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
453 struct thread_stat *ts, int ddir)
455 const char *ddir_label[2] = { "Read", "Write" };
456 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
457 unsigned long min[3], max[3], runt;
458 unsigned long long bw, iops;
459 unsigned int flags = 0;
460 double mean[3], dev[3];
461 char *io_p, *bw_p, *iops_p;
464 if (!ts->runtime[ddir])
467 i2p = is_power_of_2(rs->kb_base);
468 runt = ts->runtime[ddir];
470 bw = (1000 * ts->io_bytes[ddir]) / runt;
471 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
472 bw_p = num2str(bw, 6, 1, i2p);
474 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
475 iops_p = num2str(iops, 6, 1, 0);
477 box = gtk_hbox_new(FALSE, 3);
478 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
480 frame = gtk_frame_new(ddir_label[ddir]);
481 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
483 main_vbox = gtk_vbox_new(FALSE, 3);
484 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
486 box = gtk_hbox_new(FALSE, 3);
487 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
489 label = new_info_label_in_frame(box, "IO");
490 gtk_label_set_text(GTK_LABEL(label), io_p);
491 label = new_info_label_in_frame(box, "Bandwidth");
492 gtk_label_set_text(GTK_LABEL(label), bw_p);
493 label = new_info_label_in_frame(box, "IOPS");
494 gtk_label_set_text(GTK_LABEL(label), iops_p);
495 label = new_info_label_in_frame(box, "Runtime (msec)");
496 label_set_int_value(label, ts->runtime[ddir]);
498 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
499 double p_of_agg = 100.0;
500 const char *bw_str = "KB";
504 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
505 if (p_of_agg > 100.0)
509 if (mean[0] > 999999.9) {
517 sprintf(tmp, "Bandwidth (%s)", bw_str);
518 frame = gtk_frame_new(tmp);
519 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
521 box = gtk_hbox_new(FALSE, 3);
522 gtk_container_add(GTK_CONTAINER(frame), box);
524 label = new_info_label_in_frame(box, "Minimum");
525 label_set_int_value(label, min[0]);
526 label = new_info_label_in_frame(box, "Maximum");
527 label_set_int_value(label, max[0]);
528 label = new_info_label_in_frame(box, "Percentage of jobs");
529 sprintf(tmp, "%3.2f%%", p_of_agg);
530 gtk_label_set_text(GTK_LABEL(label), tmp);
531 label = new_info_label_in_frame(box, "Average");
532 sprintf(tmp, "%5.02f", mean[0]);
533 gtk_label_set_text(GTK_LABEL(label), tmp);
534 label = new_info_label_in_frame(box, "Standard deviation");
535 sprintf(tmp, "%5.02f", dev[0]);
536 gtk_label_set_text(GTK_LABEL(label), tmp);
539 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
541 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
543 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
547 frame = gtk_frame_new("Latency");
548 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
550 vbox = gtk_vbox_new(FALSE, 3);
551 gtk_container_add(GTK_CONTAINER(frame), vbox);
553 if (flags & GFIO_SLAT)
554 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
555 if (flags & GFIO_CLAT)
556 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
557 if (flags & GFIO_LAT)
558 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
561 if (ts->clat_percentiles)
562 gfio_show_clat_percentiles(main_vbox, ts, ddir);
570 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
573 GtkWidget *tree_view;
574 GtkTreeSelection *selection;
581 * Check if all are empty, in which case don't bother
583 for (i = 0, skipped = 0; i < num; i++)
590 types = malloc(num * sizeof(GType));
592 for (i = 0; i < num; i++)
593 types[i] = G_TYPE_STRING;
595 model = gtk_list_store_newv(num, types);
599 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
600 gtk_widget_set_can_focus(tree_view, FALSE);
602 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
603 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
605 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
606 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
608 for (i = 0; i < num; i++)
609 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
611 gtk_list_store_append(model, &iter);
613 for (i = 0; i < num; i++) {
617 sprintf(fbuf, "0.00");
619 sprintf(fbuf, "%3.2f%%", lat[i]);
621 gtk_list_store_set(model, &iter, i, fbuf, -1);
627 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
629 GtkWidget *box, *frame, *tree_view;
630 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
631 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
632 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
633 "250", "500", "750", "1000", };
634 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
635 "250", "500", "750", "1000", "2000",
638 stat_calc_lat_u(ts, io_u_lat_u);
639 stat_calc_lat_m(ts, io_u_lat_m);
641 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
643 frame = gtk_frame_new("Latency buckets (usec)");
644 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
646 box = gtk_hbox_new(FALSE, 3);
647 gtk_container_add(GTK_CONTAINER(frame), box);
648 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
651 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
653 frame = gtk_frame_new("Latency buckets (msec)");
654 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
656 box = gtk_hbox_new(FALSE, 3);
657 gtk_container_add(GTK_CONTAINER(frame), box);
658 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
662 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
664 GtkWidget *box, *frame, *entry;
665 double usr_cpu, sys_cpu;
666 unsigned long runtime;
669 runtime = ts->total_run_time;
671 double runt = (double) runtime;
673 usr_cpu = (double) ts->usr_time * 100 / runt;
674 sys_cpu = (double) ts->sys_time * 100 / runt;
680 frame = gtk_frame_new("OS resources");
681 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
683 box = gtk_hbox_new(FALSE, 3);
684 gtk_container_add(GTK_CONTAINER(frame), box);
686 entry = new_info_entry_in_frame(box, "User CPU");
687 sprintf(tmp, "%3.2f%%", usr_cpu);
688 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
689 entry = new_info_entry_in_frame(box, "System CPU");
690 sprintf(tmp, "%3.2f%%", sys_cpu);
691 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
692 entry = new_info_entry_in_frame(box, "Context switches");
693 entry_set_int_value(entry, ts->ctx);
694 entry = new_info_entry_in_frame(box, "Major faults");
695 entry_set_int_value(entry, ts->majf);
696 entry = new_info_entry_in_frame(box, "Minor faults");
697 entry_set_int_value(entry, ts->minf);
699 static void gfio_add_sc_depths_tree(GtkListStore *model,
700 struct thread_stat *ts, unsigned int len,
703 double io_u_dist[FIO_IO_U_MAP_NR];
705 /* Bits 0, and 3-8 */
706 const int add_mask = 0x1f9;
710 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
712 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
714 gtk_list_store_append(model, &iter);
716 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
718 for (i = 1, j = 0; i < len; i++) {
721 if (!(add_mask & (1UL << (i - 1))))
722 sprintf(fbuf, "0.0%%");
724 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
728 gtk_list_store_set(model, &iter, i, fbuf, -1);
733 static void gfio_add_total_depths_tree(GtkListStore *model,
734 struct thread_stat *ts, unsigned int len)
736 double io_u_dist[FIO_IO_U_MAP_NR];
738 /* Bits 1-6, and 8 */
739 const int add_mask = 0x17e;
742 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
744 gtk_list_store_append(model, &iter);
746 gtk_list_store_set(model, &iter, 0, "Total", -1);
748 for (i = 1, j = 0; i < len; i++) {
751 if (!(add_mask & (1UL << (i - 1))))
752 sprintf(fbuf, "0.0%%");
754 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
758 gtk_list_store_set(model, &iter, i, fbuf, -1);
763 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
765 GtkWidget *frame, *box, *tree_view;
766 GtkTreeSelection *selection;
768 GType types[FIO_IO_U_MAP_NR + 1];
771 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
773 frame = gtk_frame_new("IO depths");
774 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
776 box = gtk_hbox_new(FALSE, 3);
777 gtk_container_add(GTK_CONTAINER(frame), box);
779 for (i = 0; i < NR_LABELS; i++)
780 types[i] = G_TYPE_STRING;
782 model = gtk_list_store_newv(NR_LABELS, types);
784 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
785 gtk_widget_set_can_focus(tree_view, FALSE);
787 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
788 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
790 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
791 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
793 for (i = 0; i < NR_LABELS; i++)
794 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
796 gfio_add_total_depths_tree(model, ts, NR_LABELS);
797 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
798 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
800 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
803 static gboolean results_window_delete(GtkWidget *w, gpointer data)
805 struct gui *ui = (struct gui *) data;
807 gtk_widget_destroy(w);
808 ui->results_window = NULL;
809 ui->results_notebook = NULL;
813 static GtkWidget *get_results_window(struct gui *ui)
815 GtkWidget *win, *notebook;
817 if (ui->results_window)
818 return ui->results_notebook;
820 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
821 gtk_window_set_title(GTK_WINDOW(win), "Results");
822 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
823 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
824 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
826 notebook = gtk_notebook_new();
827 gtk_container_add(GTK_CONTAINER(win), notebook);
829 ui->results_window = win;
830 ui->results_notebook = notebook;
831 return ui->results_notebook;
834 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
835 struct group_run_stats *rs)
837 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
838 struct gfio_client *gc = client->client_data;
842 res_win = get_results_window(gc->ui);
844 scroll = gtk_scrolled_window_new(NULL, NULL);
845 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
846 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
848 vbox = gtk_vbox_new(FALSE, 3);
850 box = gtk_hbox_new(FALSE, 0);
851 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
853 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
855 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
857 gc->results_widget = vbox;
859 entry = new_info_entry_in_frame(box, "Name");
860 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
861 if (strlen(ts->description)) {
862 entry = new_info_entry_in_frame(box, "Description");
863 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
865 entry = new_info_entry_in_frame(box, "Group ID");
866 entry_set_int_value(entry, ts->groupid);
867 entry = new_info_entry_in_frame(box, "Jobs");
868 entry_set_int_value(entry, ts->members);
869 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
870 entry_set_int_value(entry, ts->error);
871 entry = new_info_entry_in_frame(box, "PID");
872 entry_set_int_value(entry, ts->pid);
874 if (ts->io_bytes[DDIR_READ])
875 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
876 if (ts->io_bytes[DDIR_WRITE])
877 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
879 gfio_show_latency_buckets(vbox, ts);
880 gfio_show_cpu_usage(vbox, ts);
881 gfio_show_io_depths(vbox, ts);
883 gtk_widget_show_all(gc->ui->results_window);
887 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
889 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
890 struct gfio_client *gc = client->client_data;
894 char tmp[64], timebuf[80];
897 tm = localtime(&sec);
898 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
899 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
903 gtk_list_store_append(gc->ui->log_model, &iter);
904 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
905 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
906 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
907 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
909 if (p->level == FIO_LOG_ERR)
910 view_log(NULL, (gpointer) gc->ui);
915 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
917 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
918 struct gfio_client *gc = client->client_data;
919 GtkWidget *box, *frame, *entry, *vbox;
925 if (!gc->results_widget)
928 if (!gc->disk_util_frame) {
929 gc->disk_util_frame = gtk_frame_new("Disk utilization");
930 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
933 vbox = gtk_vbox_new(FALSE, 3);
934 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
936 frame = gtk_frame_new((char *) p->dus.name);
937 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
939 box = gtk_vbox_new(FALSE, 3);
940 gtk_container_add(GTK_CONTAINER(frame), box);
942 frame = gtk_frame_new("Read");
943 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
944 vbox = gtk_hbox_new(TRUE, 3);
945 gtk_container_add(GTK_CONTAINER(frame), vbox);
946 entry = new_info_entry_in_frame(vbox, "IOs");
947 entry_set_int_value(entry, p->dus.ios[0]);
948 entry = new_info_entry_in_frame(vbox, "Merges");
949 entry_set_int_value(entry, p->dus.merges[0]);
950 entry = new_info_entry_in_frame(vbox, "Sectors");
951 entry_set_int_value(entry, p->dus.sectors[0]);
952 entry = new_info_entry_in_frame(vbox, "Ticks");
953 entry_set_int_value(entry, p->dus.ticks[0]);
955 frame = gtk_frame_new("Write");
956 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
957 vbox = gtk_hbox_new(TRUE, 3);
958 gtk_container_add(GTK_CONTAINER(frame), vbox);
959 entry = new_info_entry_in_frame(vbox, "IOs");
960 entry_set_int_value(entry, p->dus.ios[1]);
961 entry = new_info_entry_in_frame(vbox, "Merges");
962 entry_set_int_value(entry, p->dus.merges[1]);
963 entry = new_info_entry_in_frame(vbox, "Sectors");
964 entry_set_int_value(entry, p->dus.sectors[1]);
965 entry = new_info_entry_in_frame(vbox, "Ticks");
966 entry_set_int_value(entry, p->dus.ticks[1]);
968 frame = gtk_frame_new("Shared");
969 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
970 vbox = gtk_hbox_new(TRUE, 3);
971 gtk_container_add(GTK_CONTAINER(frame), vbox);
972 entry = new_info_entry_in_frame(vbox, "IO ticks");
973 entry_set_int_value(entry, p->dus.io_ticks);
974 entry = new_info_entry_in_frame(vbox, "Time in queue");
975 entry_set_int_value(entry, p->dus.time_in_queue);
979 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
983 sprintf(tmp, "%3.2f%%", util);
984 entry = new_info_entry_in_frame(vbox, "Disk utilization");
985 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
987 gtk_widget_show_all(gc->results_widget);
992 extern int sum_stat_clients;
993 extern struct thread_stat client_ts;
994 extern struct group_run_stats client_gs;
996 static int sum_stat_nr;
998 static void gfio_thread_status_op(struct fio_client *client,
999 struct fio_net_cmd *cmd)
1001 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1003 gfio_display_ts(client, &p->ts, &p->rs);
1005 if (sum_stat_clients == 1)
1008 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1009 sum_group_stats(&client_gs, &p->rs);
1011 client_ts.members++;
1012 client_ts.groupid = p->ts.groupid;
1014 if (++sum_stat_nr == sum_stat_clients) {
1015 strcpy(client_ts.name, "All clients");
1016 gfio_display_ts(client, &client_ts, &client_gs);
1020 static void gfio_group_stats_op(struct fio_client *client,
1021 struct fio_net_cmd *cmd)
1023 gdk_threads_enter();
1024 printf("gfio_group_stats_op called\n");
1025 fio_client_ops.group_stats(client, cmd);
1026 gdk_threads_leave();
1029 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event)
1031 ui.drawing_area_xdim = w->allocation.width;
1032 ui.drawing_area_ydim = w->allocation.height;
1036 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1038 struct gui *ui = (struct gui *) p;
1041 graph_set_size(ui->iops_graph, ui->drawing_area_xdim / 2.0,
1042 ui->drawing_area_ydim);
1043 graph_set_size(ui->bandwidth_graph, ui->drawing_area_xdim / 2.0,
1044 ui->drawing_area_ydim);
1045 cr = gdk_cairo_create(w->window);
1047 cairo_set_source_rgb(cr, 0, 0, 0);
1050 cairo_translate(cr, 0, 0);
1051 line_graph_draw(ui->bandwidth_graph, cr);
1056 cairo_translate(cr, ui->drawing_area_xdim / 2.0, 0);
1057 line_graph_draw(ui->iops_graph, cr);
1065 static void gfio_update_eta(struct jobs_eta *je)
1067 static int eta_good;
1074 gdk_threads_enter();
1079 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1080 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1081 eta_to_str(eta_str, je->eta_sec);
1084 sprintf(tmp, "%u", je->nr_running);
1085 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
1086 sprintf(tmp, "%u", je->files_open);
1087 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
1090 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1091 if (je->m_rate || je->t_rate) {
1094 mr = num2str(je->m_rate, 4, 0, i2p);
1095 tr = num2str(je->t_rate, 4, 0, i2p);
1096 gtk_entry_set_text(GTK_ENTRY(ui.eta);
1097 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1100 } else if (je->m_iops || je->t_iops)
1101 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1103 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
1104 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
1105 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
1106 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
1109 if (je->eta_sec != INT_MAX && je->nr_running) {
1113 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1114 strcpy(output, "-.-% done");
1118 sprintf(output, "%3.1f%% done", perc);
1121 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1122 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1124 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1125 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1127 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1128 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1129 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1130 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1132 graph_add_xy_data(ui.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1133 graph_add_xy_data(ui.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1134 graph_add_xy_data(ui.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1135 graph_add_xy_data(ui.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1144 char *dst = output + strlen(output);
1146 sprintf(dst, " - %s", eta_str);
1149 gfio_update_thread_status(output, perc);
1150 gdk_threads_leave();
1153 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1155 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1156 struct gfio_client *gc = client->client_data;
1157 struct gui *ui = gc->ui;
1158 const char *os, *arch;
1161 os = fio_get_os_string(probe->os);
1165 arch = fio_get_arch_string(probe->arch);
1170 client->name = strdup((char *) probe->hostname);
1172 gdk_threads_enter();
1174 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1175 gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1176 gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1177 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1178 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1180 gfio_set_connected(ui, 1);
1182 gdk_threads_leave();
1185 static void gfio_update_thread_status(char *status_message, double perc)
1187 static char message[100];
1188 const char *m = message;
1190 strncpy(message, status_message, sizeof(message) - 1);
1191 gtk_progress_bar_set_text(
1192 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1193 gtk_progress_bar_set_fraction(
1194 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1195 gtk_widget_queue_draw(ui.window);
1198 static void gfio_quit_op(struct fio_client *client)
1200 struct gfio_client *gc = client->client_data;
1202 gdk_threads_enter();
1203 gfio_set_connected(gc->ui, 0);
1204 gdk_threads_leave();
1207 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1209 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1210 struct gfio_client *gc = client->client_data;
1211 struct thread_options *o = &gc->o;
1212 struct gui *ui = gc->ui;
1215 convert_thread_options_to_cpu(o, &p->top);
1217 gdk_threads_enter();
1219 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) o->name);
1220 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(o->td_ddir));
1221 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) o->ioengine);
1223 sprintf(tmp, "%u", o->iodepth);
1224 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1228 gdk_threads_leave();
1231 static void gfio_client_timed_out(struct fio_client *client)
1233 struct gfio_client *gc = client->client_data;
1234 GtkWidget *dialog, *label, *content;
1237 gdk_threads_enter();
1239 gfio_set_connected(gc->ui, 0);
1240 clear_ui_info(gc->ui);
1242 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1244 dialog = gtk_dialog_new_with_buttons("Timed out!",
1245 GTK_WINDOW(gc->ui->window),
1246 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1247 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1249 /* gtk_dialog_get_content_area() is 2.14 and newer */
1250 content = GTK_DIALOG(dialog)->vbox;
1252 label = gtk_label_new((const gchar *) buf);
1253 gtk_container_add(GTK_CONTAINER(content), label);
1254 gtk_widget_show_all(dialog);
1255 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1257 gtk_dialog_run(GTK_DIALOG(dialog));
1258 gtk_widget_destroy(dialog);
1260 gdk_threads_leave();
1263 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1265 struct gfio_client *gc = client->client_data;
1267 gdk_threads_enter();
1269 gfio_set_connected(gc->ui, 0);
1272 entry_set_int_value(gc->err_entry, client->error);
1274 gdk_threads_leave();
1277 struct client_ops gfio_client_ops = {
1278 .text_op = gfio_text_op,
1279 .disk_util = gfio_disk_util_op,
1280 .thread_status = gfio_thread_status_op,
1281 .group_stats = gfio_group_stats_op,
1282 .eta = gfio_update_eta,
1283 .probe = gfio_probe_op,
1284 .quit = gfio_quit_op,
1285 .add_job = gfio_add_job_op,
1286 .timed_out = gfio_client_timed_out,
1287 .stop = gfio_client_stop,
1288 .stay_connected = 1,
1291 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1292 __attribute__((unused)) gpointer data)
1297 static void *job_thread(void *arg)
1299 fio_handle_clients(&gfio_client_ops);
1303 static int send_job_files(struct gui *ui)
1307 for (i = 0; i < ui->nr_job_files; i++) {
1308 ret = fio_clients_send_ini(ui->job_files[i]);
1312 free(ui->job_files[i]);
1313 ui->job_files[i] = NULL;
1315 while (i < ui->nr_job_files) {
1316 free(ui->job_files[i]);
1317 ui->job_files[i] = NULL;
1324 static void start_job_thread(struct gui *ui)
1326 if (send_job_files(ui)) {
1327 printf("Yeah, I didn't really like those options too much.\n");
1328 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1333 static void *server_thread(void *arg)
1336 gfio_server_running = 1;
1337 fio_start_server(NULL);
1338 gfio_server_running = 0;
1342 static void gfio_start_server(struct gui *ui)
1344 if (!gfio_server_running) {
1345 gfio_server_running = 1;
1346 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1347 pthread_detach(ui->server_t);
1351 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1354 struct gui *ui = data;
1356 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1357 start_job_thread(ui);
1360 static void file_open(GtkWidget *w, gpointer data);
1362 static void connect_clicked(GtkWidget *widget, gpointer data)
1364 struct gui *ui = data;
1366 if (!ui->connected) {
1367 if (!ui->nr_job_files)
1368 file_open(widget, data);
1369 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1370 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1371 if (!fio_clients_connect()) {
1372 pthread_create(&ui->t, NULL, job_thread, NULL);
1373 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1376 fio_clients_terminate();
1377 gfio_set_connected(ui, 0);
1382 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1383 struct button_spec *buttonspec)
1385 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1386 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1387 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1388 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1389 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1392 static void add_buttons(struct gui *ui,
1393 struct button_spec *buttonlist,
1398 for (i = 0; i < nbuttons; i++)
1399 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1402 static void on_info_bar_response(GtkWidget *widget, gint response,
1405 if (response == GTK_RESPONSE_OK) {
1406 gtk_widget_destroy(widget);
1407 ui.error_info_bar = NULL;
1411 void report_error(GError *error)
1413 if (ui.error_info_bar == NULL) {
1414 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1417 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1418 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1421 ui.error_label = gtk_label_new(error->message);
1422 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1423 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1425 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1426 gtk_widget_show_all(ui.vbox);
1429 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1430 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1434 struct connection_widgets
1441 static void hostname_cb(GtkEntry *entry, gpointer data)
1443 struct connection_widgets *cw = data;
1444 int uses_net = 0, is_localhost = 0;
1449 * Check whether to display the 'auto start backend' box
1450 * or not. Show it if we are a localhost and using network,
1451 * or using a socket.
1453 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1454 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1459 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1460 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1461 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1462 !strcmp(text, "ip6-loopback"))
1466 if (!uses_net || is_localhost) {
1467 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1468 gtk_widget_set_sensitive(cw->button, 1);
1470 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1471 gtk_widget_set_sensitive(cw->button, 0);
1475 static int get_connection_details(char **host, int *port, int *type,
1478 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1479 struct connection_widgets cw;
1482 dialog = gtk_dialog_new_with_buttons("Connection details",
1483 GTK_WINDOW(ui.window),
1484 GTK_DIALOG_DESTROY_WITH_PARENT,
1485 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1486 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1488 frame = gtk_frame_new("Hostname / socket name");
1489 /* gtk_dialog_get_content_area() is 2.14 and newer */
1490 vbox = GTK_DIALOG(dialog)->vbox;
1491 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1493 box = gtk_vbox_new(FALSE, 6);
1494 gtk_container_add(GTK_CONTAINER(frame), box);
1496 hbox = gtk_hbox_new(TRUE, 10);
1497 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1498 cw.hentry = gtk_entry_new();
1499 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1500 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1502 frame = gtk_frame_new("Port");
1503 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1504 box = gtk_vbox_new(FALSE, 10);
1505 gtk_container_add(GTK_CONTAINER(frame), box);
1507 hbox = gtk_hbox_new(TRUE, 4);
1508 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1509 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1511 frame = gtk_frame_new("Type");
1512 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1513 box = gtk_vbox_new(FALSE, 10);
1514 gtk_container_add(GTK_CONTAINER(frame), box);
1516 hbox = gtk_hbox_new(TRUE, 4);
1517 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1519 cw.combo = gtk_combo_box_new_text();
1520 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1521 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1522 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1523 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1525 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1527 frame = gtk_frame_new("Options");
1528 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1529 box = gtk_vbox_new(FALSE, 10);
1530 gtk_container_add(GTK_CONTAINER(frame), box);
1532 hbox = gtk_hbox_new(TRUE, 4);
1533 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1535 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1536 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1537 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.");
1538 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1541 * Connect edit signal, so we can show/not-show the auto start button
1543 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1544 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1546 gtk_widget_show_all(dialog);
1548 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1549 gtk_widget_destroy(dialog);
1553 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1554 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1556 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1557 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1558 *type = Fio_client_ipv4;
1559 else if (!strncmp(typeentry, "IPv6", 4))
1560 *type = Fio_client_ipv6;
1562 *type = Fio_client_socket;
1565 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1567 gtk_widget_destroy(dialog);
1571 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1573 struct gfio_client *gc;
1575 gc = malloc(sizeof(*gc));
1576 memset(gc, 0, sizeof(*gc));
1579 client->client_data = gc;
1582 static void file_open(GtkWidget *w, gpointer data)
1585 struct gui *ui = data;
1586 GSList *filenames, *fn_glist;
1587 GtkFileFilter *filter;
1589 int port, type, server_start;
1591 dialog = gtk_file_chooser_dialog_new("Open File",
1592 GTK_WINDOW(ui->window),
1593 GTK_FILE_CHOOSER_ACTION_OPEN,
1594 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1595 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1597 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1599 filter = gtk_file_filter_new();
1600 gtk_file_filter_add_pattern(filter, "*.fio");
1601 gtk_file_filter_add_pattern(filter, "*.job");
1602 gtk_file_filter_add_pattern(filter, "*.ini");
1603 gtk_file_filter_add_mime_type(filter, "text/fio");
1604 gtk_file_filter_set_name(filter, "Fio job file");
1605 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1607 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1608 gtk_widget_destroy(dialog);
1612 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1614 gtk_widget_destroy(dialog);
1616 if (get_connection_details(&host, &port, &type, &server_start))
1619 filenames = fn_glist;
1620 while (filenames != NULL) {
1621 struct fio_client *client;
1623 ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1624 ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1627 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1631 error = g_error_new(g_quark_from_string("fio"), 1,
1632 "Failed to add client %s", host);
1633 report_error(error);
1634 g_error_free(error);
1636 gfio_client_added(ui, client);
1638 g_free(filenames->data);
1639 filenames = g_slist_next(filenames);
1644 gfio_start_server(ui);
1646 g_slist_free(fn_glist);
1649 static void file_save(GtkWidget *w, gpointer data)
1651 struct gui *ui = data;
1654 dialog = gtk_file_chooser_dialog_new("Save File",
1655 GTK_WINDOW(ui->window),
1656 GTK_FILE_CHOOSER_ACTION_SAVE,
1657 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1658 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1661 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1662 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1664 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1667 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1668 // save_job_file(filename);
1671 gtk_widget_destroy(dialog);
1674 static void view_log_destroy(GtkWidget *w, gpointer data)
1676 struct gui *ui = (struct gui *) data;
1678 gtk_widget_ref(ui->log_tree);
1679 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1680 gtk_widget_destroy(w);
1681 ui->log_view = NULL;
1684 static void view_log(GtkWidget *w, gpointer data)
1686 GtkWidget *win, *scroll, *vbox, *box;
1687 struct gui *ui = (struct gui *) data;
1692 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1693 gtk_window_set_title(GTK_WINDOW(win), "Log");
1694 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1696 scroll = gtk_scrolled_window_new(NULL, NULL);
1698 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1700 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1702 box = gtk_hbox_new(TRUE, 0);
1703 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1704 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1705 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1707 vbox = gtk_vbox_new(TRUE, 5);
1708 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1710 gtk_container_add(GTK_CONTAINER(win), vbox);
1711 gtk_widget_show_all(win);
1714 static void preferences(GtkWidget *w, gpointer data)
1716 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1719 dialog = gtk_dialog_new_with_buttons("Preferences",
1720 GTK_WINDOW(ui.window),
1721 GTK_DIALOG_DESTROY_WITH_PARENT,
1722 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1723 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1726 frame = gtk_frame_new("Debug logging");
1727 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1729 vbox = gtk_vbox_new(FALSE, 6);
1730 gtk_container_add(GTK_CONTAINER(frame), vbox);
1732 box = gtk_hbox_new(FALSE, 6);
1733 gtk_container_add(GTK_CONTAINER(vbox), box);
1735 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1737 for (i = 0; i < FD_DEBUG_MAX; i++) {
1739 box = gtk_hbox_new(FALSE, 6);
1740 gtk_container_add(GTK_CONTAINER(vbox), box);
1744 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1745 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1746 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1749 frame = gtk_frame_new("Graph font");
1750 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1751 vbox = gtk_vbox_new(FALSE, 6);
1752 gtk_container_add(GTK_CONTAINER(frame), vbox);
1754 font = gtk_font_button_new();
1755 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
1757 gtk_widget_show_all(dialog);
1759 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1760 gtk_widget_destroy(dialog);
1764 for (i = 0; i < FD_DEBUG_MAX; i++) {
1767 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1769 fio_debug |= (1UL << i);
1772 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1773 gtk_widget_destroy(dialog);
1776 static void about_dialog(GtkWidget *w, gpointer data)
1778 const char *authors[] = {
1779 "Jens Axboe <axboe@kernel.dk>",
1780 "Stephen Carmeron <stephenmcameron@gmail.com>",
1783 const char *license[] = {
1784 "Fio is free software; you can redistribute it and/or modify "
1785 "it under the terms of the GNU General Public License as published by "
1786 "the Free Software Foundation; either version 2 of the License, or "
1787 "(at your option) any later version.\n",
1788 "Fio is distributed in the hope that it will be useful, "
1789 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
1790 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
1791 "GNU General Public License for more details.\n",
1792 "You should have received a copy of the GNU General Public License "
1793 "along with Fio; if not, write to the Free Software Foundation, Inc., "
1794 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
1796 char *license_trans;
1798 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
1799 license[2], "\n", NULL);
1801 gtk_show_about_dialog(NULL,
1802 "program-name", "gfio",
1803 "comments", "Gtk2 UI for fio",
1804 "license", license_trans,
1805 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
1807 "version", fio_version_string,
1808 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
1809 "logo-icon-name", "fio",
1811 "wrap-license", TRUE,
1814 g_free (license_trans);
1817 static GtkActionEntry menu_items[] = {
1818 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1819 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1820 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1821 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1822 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1823 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1824 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1825 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1826 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1828 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1830 static const gchar *ui_string = " \
1832 <menubar name=\"MainMenu\"> \
1833 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1834 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1835 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1836 <separator name=\"Separator\"/> \
1837 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1838 <separator name=\"Separator2\"/> \
1839 <menuitem name=\"Quit\" action=\"Quit\" /> \
1841 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1842 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1844 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1845 <menuitem name=\"About\" action=\"About\" /> \
1851 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1854 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1857 action_group = gtk_action_group_new("Menu");
1858 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1860 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1861 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1863 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1864 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1867 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1868 GtkWidget *vbox, GtkUIManager *ui_manager)
1870 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1873 static void init_ui(int *argc, char **argv[], struct gui *ui)
1875 GtkSettings *settings;
1876 GtkUIManager *uimanager;
1877 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1880 memset(ui, 0, sizeof(*ui));
1882 /* Magical g*thread incantation, you just need this thread stuff.
1883 * Without it, the update that happens in gfio_update_thread_status
1884 * doesn't really happen in a timely fashion, you need expose events
1886 if (!g_thread_supported())
1887 g_thread_init(NULL);
1890 gtk_init(argc, argv);
1891 settings = gtk_settings_get_default();
1892 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1895 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1896 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1897 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 700);
1899 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1900 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1902 ui->vbox = gtk_vbox_new(FALSE, 0);
1903 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1905 uimanager = gtk_ui_manager_new();
1906 menu = get_menubar_menu(ui->window, uimanager, ui);
1907 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1910 * Set up alignments for widgets at the top of ui,
1911 * align top left, expand horizontally but not vertically
1913 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1914 ui->topvbox = gtk_vbox_new(FALSE, 3);
1915 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1916 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1918 probe = gtk_frame_new("Job");
1919 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1920 probe_frame = gtk_vbox_new(FALSE, 3);
1921 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1923 probe_box = gtk_hbox_new(FALSE, 3);
1924 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1925 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1926 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1927 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1928 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1930 probe_box = gtk_hbox_new(FALSE, 3);
1931 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1933 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1934 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1935 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1936 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1937 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1938 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1940 probe_box = gtk_hbox_new(FALSE, 3);
1941 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1942 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1943 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1944 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1945 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1948 * Only add this if we have a commit rate
1951 probe_box = gtk_hbox_new(FALSE, 3);
1952 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1954 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1955 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1957 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1958 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1962 * Set up a drawing area and IOPS and bandwidth graphs
1964 gdk_color_parse("white", &white);
1965 ui->drawing_area = gtk_drawing_area_new();
1966 ui->drawing_area_xdim = DRAWING_AREA_XDIM;
1967 ui->drawing_area_ydim = DRAWING_AREA_YDIM;
1968 gtk_widget_set_size_request(GTK_WIDGET(ui->drawing_area),
1969 ui->drawing_area_xdim, ui->drawing_area_ydim);
1970 gtk_widget_modify_bg(ui->drawing_area, GTK_STATE_NORMAL, &white);
1971 g_signal_connect(G_OBJECT(ui->drawing_area), "expose_event",
1972 G_CALLBACK (on_expose_drawing_area), ui);
1973 g_signal_connect(G_OBJECT(ui->drawing_area), "configure_event",
1974 G_CALLBACK (on_config_drawing_area), ui);
1975 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1976 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1977 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1978 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1980 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1983 setup_iops_graph(ui);
1984 setup_bandwidth_graph(ui);
1987 * Set up alignments for widgets at the bottom of ui,
1988 * align bottom left, expand horizontally but not vertically
1990 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1991 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1992 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1993 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1996 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1999 * Set up thread status progress bar
2001 ui->thread_status_pb = gtk_progress_bar_new();
2002 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2003 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2004 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2006 gfio_ui_setup_log(ui);
2008 gtk_widget_show_all(ui->window);
2011 int main(int argc, char *argv[], char *envp[])
2013 if (initialize_fio(envp))
2015 if (fio_init_options())
2018 init_ui(&argc, &argv, &ui);
2020 gdk_threads_enter();
2022 gdk_threads_leave();