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
34 static int gfio_server_running;
36 static void gfio_update_thread_status(char *status_message, double perc);
38 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
40 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
42 static void connect_clicked(GtkWidget *widget, gpointer data);
43 static void start_job_clicked(GtkWidget *widget, gpointer data);
45 static struct button_spec {
46 const char *buttontext;
48 const char *tooltiptext;
49 const int start_insensitive;
50 } buttonspeclist[] = {
51 #define CONNECT_BUTTON 0
52 #define START_JOB_BUTTON 1
53 { "Connect", connect_clicked, "Connect to host", 0 },
56 "Send current fio job to fio server to be executed", 1 },
78 GtkWidget *write_iops;
88 GtkWidget *bottomalign;
89 GtkWidget *thread_status_pb;
91 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
92 GtkWidget *scrolled_window;
93 #define DRAWING_AREA_XDIM 1000
94 #define DRAWING_AREA_YDIM 400
95 GtkWidget *drawing_area;
96 GtkWidget *error_info_bar;
97 GtkWidget *error_label;
98 GtkWidget *results_notebook;
99 GtkWidget *results_window;
100 GtkListStore *log_model;
104 struct probe_widget probe;
105 struct eta_widget eta;
110 struct graph *iops_graph;
111 struct graph *bandwidth_graph;
112 struct fio_client *client;
119 GtkWidget *results_widget;
120 GtkWidget *disk_util_frame;
123 static void setup_iops_graph(struct gui *ui)
126 graph_free(ui->iops_graph);
127 ui->iops_graph = graph_new((int) DRAWING_AREA_XDIM / 2.0,
128 (int) DRAWING_AREA_YDIM);
129 graph_title(ui->iops_graph, "IOPS");
130 graph_x_title(ui->iops_graph, "Time");
131 graph_y_title(ui->iops_graph, "IOPS");
132 graph_add_label(ui->iops_graph, "Read IOPS");
133 graph_add_label(ui->iops_graph, "Write IOPS");
134 graph_set_color(ui->iops_graph, "Read IOPS", 0.7, 0.0, 0.0);
135 graph_set_color(ui->iops_graph, "Write IOPS", 0.0, 0.0, 0.7);
138 static void setup_bandwidth_graph(struct gui *ui)
140 if (ui->bandwidth_graph)
141 graph_free(ui->bandwidth_graph);
142 ui->bandwidth_graph = graph_new((int) DRAWING_AREA_XDIM / 2.0,
143 (int) DRAWING_AREA_YDIM);
144 graph_title(ui->bandwidth_graph, "Bandwidth");
145 graph_x_title(ui->bandwidth_graph, "Time");
146 graph_y_title(ui->bandwidth_graph, "Bandwidth");
147 graph_add_label(ui->bandwidth_graph, "Read Bandwidth");
148 graph_add_label(ui->bandwidth_graph, "Write Bandwidth");
149 graph_set_color(ui->bandwidth_graph, "Read Bandwidth", 0.7, 0.0, 0.0);
150 graph_set_color(ui->bandwidth_graph, "Write Bandwidth", 0.0, 0.0, 0.7);
153 static void clear_ui_info(struct gui *ui)
155 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
156 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
157 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
158 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
159 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
160 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
161 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
162 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
163 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
164 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
165 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
166 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
167 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
168 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
171 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
173 GtkWidget *entry, *frame;
175 frame = gtk_frame_new(label);
176 entry = gtk_entry_new();
177 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
178 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
179 gtk_container_add(GTK_CONTAINER(frame), entry);
184 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
186 GtkWidget *label_widget;
189 frame = gtk_frame_new(label);
190 label_widget = gtk_label_new(NULL);
191 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
192 gtk_container_add(GTK_CONTAINER(frame), label_widget);
197 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
199 GtkWidget *button, *box;
201 box = gtk_hbox_new(FALSE, 3);
202 gtk_container_add(GTK_CONTAINER(hbox), box);
204 button = gtk_spin_button_new_with_range(min, max, 1.0);
205 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
207 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
208 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
213 static void gfio_set_connected(struct gui *ui, int connected)
216 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
218 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
219 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
222 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
223 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
224 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
228 static void label_set_int_value(GtkWidget *entry, unsigned int val)
232 sprintf(tmp, "%u", val);
233 gtk_label_set_text(GTK_LABEL(entry), tmp);
236 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
240 sprintf(tmp, "%u", val);
241 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
245 #define ALIGN_RIGHT 2
249 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
251 GtkCellRenderer *renderer;
252 GtkTreeViewColumn *col;
253 double xalign = 0.0; /* left as default */
254 PangoAlignment align;
257 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
258 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
260 visible = !(flags & INVISIBLE);
262 renderer = gtk_cell_renderer_text_new();
263 col = gtk_tree_view_column_new();
265 gtk_tree_view_column_set_title(col, title);
266 if (!(flags & UNSORTABLE))
267 gtk_tree_view_column_set_sort_column_id(col, index);
268 gtk_tree_view_column_set_resizable(col, TRUE);
269 gtk_tree_view_column_pack_start(col, renderer, TRUE);
270 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
271 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
273 case PANGO_ALIGN_LEFT:
276 case PANGO_ALIGN_CENTER:
279 case PANGO_ALIGN_RIGHT:
283 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
284 gtk_tree_view_column_set_visible(col, visible);
285 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
289 static void gfio_ui_setup_log(struct gui *ui)
291 GtkTreeSelection *selection;
293 GtkWidget *tree_view;
295 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
297 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
298 gtk_widget_set_can_focus(tree_view, FALSE);
300 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
301 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
302 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
303 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
305 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
306 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
307 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
308 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
310 ui->log_model = model;
311 ui->log_tree = tree_view;
314 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
320 GType types[FIO_IO_U_LIST_MAX_LEN];
321 GtkWidget *tree_view;
322 GtkTreeSelection *selection;
327 for (i = 0; i < len; i++)
328 types[i] = G_TYPE_INT;
330 model = gtk_list_store_newv(len, types);
332 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
333 gtk_widget_set_can_focus(tree_view, FALSE);
335 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
336 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
338 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
339 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
341 for (i = 0; i < len; i++) {
344 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
345 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
348 gtk_list_store_append(model, &iter);
350 for (i = 0; i < len; i++) {
352 ovals[i] = (ovals[i] + 999) / 1000;
353 gtk_list_store_set(model, &iter, i, ovals[i], -1);
359 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
362 unsigned int *io_u_plat = ts->io_u_plat[ddir];
363 unsigned long nr = ts->clat_stat[ddir].samples;
364 fio_fp64_t *plist = ts->percentile_list;
365 unsigned int *ovals, len, minv, maxv, scale_down;
367 GtkWidget *tree_view, *frame, *hbox;
370 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
375 * We default to usecs, but if the value range is such that we
376 * should scale down to msecs, do that.
378 if (minv > 2000 && maxv > 99999) {
386 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
388 sprintf(tmp, "Completion percentiles (%s)", base);
389 frame = gtk_frame_new(tmp);
390 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
392 hbox = gtk_hbox_new(FALSE, 3);
393 gtk_container_add(GTK_CONTAINER(frame), hbox);
395 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
401 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
402 unsigned long max, double mean, double dev)
404 const char *base = "(usec)";
405 GtkWidget *hbox, *label, *frame;
409 if (!usec_to_msec(&min, &max, &mean, &dev))
412 minp = num2str(min, 6, 1, 0);
413 maxp = num2str(max, 6, 1, 0);
415 sprintf(tmp, "%s %s", name, base);
416 frame = gtk_frame_new(tmp);
417 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
419 hbox = gtk_hbox_new(FALSE, 3);
420 gtk_container_add(GTK_CONTAINER(frame), hbox);
422 label = new_info_label_in_frame(hbox, "Minimum");
423 gtk_label_set_text(GTK_LABEL(label), minp);
424 label = new_info_label_in_frame(hbox, "Maximum");
425 gtk_label_set_text(GTK_LABEL(label), maxp);
426 label = new_info_label_in_frame(hbox, "Average");
427 sprintf(tmp, "%5.02f", mean);
428 gtk_label_set_text(GTK_LABEL(label), tmp);
429 label = new_info_label_in_frame(hbox, "Standard deviation");
430 sprintf(tmp, "%5.02f", dev);
431 gtk_label_set_text(GTK_LABEL(label), tmp);
442 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
443 struct thread_stat *ts, int ddir)
445 const char *ddir_label[2] = { "Read", "Write" };
446 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
447 unsigned long min[3], max[3], runt;
448 unsigned long long bw, iops;
449 unsigned int flags = 0;
450 double mean[3], dev[3];
451 char *io_p, *bw_p, *iops_p;
454 if (!ts->runtime[ddir])
457 i2p = is_power_of_2(rs->kb_base);
458 runt = ts->runtime[ddir];
460 bw = (1000 * ts->io_bytes[ddir]) / runt;
461 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
462 bw_p = num2str(bw, 6, 1, i2p);
464 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
465 iops_p = num2str(iops, 6, 1, 0);
467 box = gtk_hbox_new(FALSE, 3);
468 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
470 frame = gtk_frame_new(ddir_label[ddir]);
471 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
473 main_vbox = gtk_vbox_new(FALSE, 3);
474 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
476 box = gtk_hbox_new(FALSE, 3);
477 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
479 label = new_info_label_in_frame(box, "IO");
480 gtk_label_set_text(GTK_LABEL(label), io_p);
481 label = new_info_label_in_frame(box, "Bandwidth");
482 gtk_label_set_text(GTK_LABEL(label), bw_p);
483 label = new_info_label_in_frame(box, "IOPS");
484 gtk_label_set_text(GTK_LABEL(label), iops_p);
485 label = new_info_label_in_frame(box, "Runtime (msec)");
486 label_set_int_value(label, ts->runtime[ddir]);
488 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
489 double p_of_agg = 100.0;
490 const char *bw_str = "KB";
494 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
495 if (p_of_agg > 100.0)
499 if (mean[0] > 999999.9) {
507 sprintf(tmp, "Bandwidth (%s)", bw_str);
508 frame = gtk_frame_new(tmp);
509 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
511 box = gtk_hbox_new(FALSE, 3);
512 gtk_container_add(GTK_CONTAINER(frame), box);
514 label = new_info_label_in_frame(box, "Minimum");
515 label_set_int_value(label, min[0]);
516 label = new_info_label_in_frame(box, "Maximum");
517 label_set_int_value(label, max[0]);
518 label = new_info_label_in_frame(box, "Percentage of jobs");
519 sprintf(tmp, "%3.2f%%", p_of_agg);
520 gtk_label_set_text(GTK_LABEL(label), tmp);
521 label = new_info_label_in_frame(box, "Average");
522 sprintf(tmp, "%5.02f", mean[0]);
523 gtk_label_set_text(GTK_LABEL(label), tmp);
524 label = new_info_label_in_frame(box, "Standard deviation");
525 sprintf(tmp, "%5.02f", dev[0]);
526 gtk_label_set_text(GTK_LABEL(label), tmp);
529 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
531 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
533 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
537 frame = gtk_frame_new("Latency");
538 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
540 vbox = gtk_vbox_new(FALSE, 3);
541 gtk_container_add(GTK_CONTAINER(frame), vbox);
543 if (flags & GFIO_SLAT)
544 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
545 if (flags & GFIO_CLAT)
546 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
547 if (flags & GFIO_LAT)
548 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
551 if (ts->clat_percentiles)
552 gfio_show_clat_percentiles(main_vbox, ts, ddir);
560 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
563 GtkWidget *tree_view;
564 GtkTreeSelection *selection;
571 * Check if all are empty, in which case don't bother
573 for (i = 0, skipped = 0; i < num; i++)
580 types = malloc(num * sizeof(GType));
582 for (i = 0; i < num; i++)
583 types[i] = G_TYPE_STRING;
585 model = gtk_list_store_newv(num, types);
589 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
590 gtk_widget_set_can_focus(tree_view, FALSE);
592 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
593 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
595 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
596 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
598 for (i = 0; i < num; i++)
599 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
601 gtk_list_store_append(model, &iter);
603 for (i = 0; i < num; i++) {
607 sprintf(fbuf, "0.00");
609 sprintf(fbuf, "%3.2f%%", lat[i]);
611 gtk_list_store_set(model, &iter, i, fbuf, -1);
617 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
619 GtkWidget *box, *frame, *tree_view;
620 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
621 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
622 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
623 "250", "500", "750", "1000", };
624 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
625 "250", "500", "750", "1000", "2000",
628 stat_calc_lat_u(ts, io_u_lat_u);
629 stat_calc_lat_m(ts, io_u_lat_m);
631 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
633 frame = gtk_frame_new("Latency buckets (usec)");
634 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
636 box = gtk_hbox_new(FALSE, 3);
637 gtk_container_add(GTK_CONTAINER(frame), box);
638 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
641 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
643 frame = gtk_frame_new("Latency buckets (msec)");
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);
652 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
654 GtkWidget *box, *frame, *entry;
655 double usr_cpu, sys_cpu;
656 unsigned long runtime;
659 runtime = ts->total_run_time;
661 double runt = (double) runtime;
663 usr_cpu = (double) ts->usr_time * 100 / runt;
664 sys_cpu = (double) ts->sys_time * 100 / runt;
670 frame = gtk_frame_new("OS resources");
671 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
673 box = gtk_hbox_new(FALSE, 3);
674 gtk_container_add(GTK_CONTAINER(frame), box);
676 entry = new_info_entry_in_frame(box, "User CPU");
677 sprintf(tmp, "%3.2f%%", usr_cpu);
678 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
679 entry = new_info_entry_in_frame(box, "System CPU");
680 sprintf(tmp, "%3.2f%%", sys_cpu);
681 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
682 entry = new_info_entry_in_frame(box, "Context switches");
683 entry_set_int_value(entry, ts->ctx);
684 entry = new_info_entry_in_frame(box, "Major faults");
685 entry_set_int_value(entry, ts->majf);
686 entry = new_info_entry_in_frame(box, "Minor faults");
687 entry_set_int_value(entry, ts->minf);
689 static void gfio_add_sc_depths_tree(GtkListStore *model,
690 struct thread_stat *ts, unsigned int len,
693 double io_u_dist[FIO_IO_U_MAP_NR];
695 /* Bits 0, and 3-8 */
696 const int add_mask = 0x1f9;
700 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
702 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
704 gtk_list_store_append(model, &iter);
706 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
708 for (i = 1, j = 0; i < len; i++) {
711 if (!(add_mask & (1UL << (i - 1))))
712 sprintf(fbuf, "0.0%%");
714 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
718 gtk_list_store_set(model, &iter, i, fbuf, -1);
723 static void gfio_add_total_depths_tree(GtkListStore *model,
724 struct thread_stat *ts, unsigned int len)
726 double io_u_dist[FIO_IO_U_MAP_NR];
728 /* Bits 1-6, and 8 */
729 const int add_mask = 0x17e;
732 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
734 gtk_list_store_append(model, &iter);
736 gtk_list_store_set(model, &iter, 0, "Total", -1);
738 for (i = 1, j = 0; i < len; i++) {
741 if (!(add_mask & (1UL << (i - 1))))
742 sprintf(fbuf, "0.0%%");
744 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
748 gtk_list_store_set(model, &iter, i, fbuf, -1);
753 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
755 GtkWidget *frame, *box, *tree_view;
756 GtkTreeSelection *selection;
758 GType types[FIO_IO_U_MAP_NR + 1];
761 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
763 frame = gtk_frame_new("IO depths");
764 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
766 box = gtk_hbox_new(FALSE, 3);
767 gtk_container_add(GTK_CONTAINER(frame), box);
769 for (i = 0; i < NR_LABELS; i++)
770 types[i] = G_TYPE_STRING;
772 model = gtk_list_store_newv(NR_LABELS, types);
774 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
775 gtk_widget_set_can_focus(tree_view, FALSE);
777 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
778 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
780 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
781 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
783 for (i = 0; i < NR_LABELS; i++)
784 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
786 gfio_add_total_depths_tree(model, ts, NR_LABELS);
787 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
788 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
790 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
793 static gboolean results_window_delete(GtkWidget *w, gpointer data)
795 struct gui *ui = (struct gui *) data;
797 gtk_widget_destroy(w);
798 ui->results_window = NULL;
799 ui->results_notebook = NULL;
803 static GtkWidget *get_results_window(struct gui *ui)
805 GtkWidget *win, *notebook;
807 if (ui->results_window)
808 return ui->results_notebook;
810 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
811 gtk_window_set_title(GTK_WINDOW(win), "Results");
812 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
813 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
815 notebook = gtk_notebook_new();
816 gtk_container_add(GTK_CONTAINER(win), notebook);
818 ui->results_window = win;
819 ui->results_notebook = notebook;
820 return ui->results_notebook;
823 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
824 struct group_run_stats *rs)
826 GtkWidget *res_win, *box, *vbox, *entry;
827 struct gfio_client *gc = client->client_data;
831 res_win = get_results_window(gc->ui);
833 vbox = gtk_vbox_new(FALSE, 3);
835 box = gtk_hbox_new(TRUE, 3);
836 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
838 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
840 gc->results_widget = vbox;
842 entry = new_info_entry_in_frame(box, "Name");
843 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
844 if (strlen(ts->description)) {
845 entry = new_info_entry_in_frame(box, "Description");
846 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
848 entry = new_info_entry_in_frame(box, "Group ID");
849 entry_set_int_value(entry, ts->groupid);
850 entry = new_info_entry_in_frame(box, "Jobs");
851 entry_set_int_value(entry, ts->members);
852 entry = new_info_entry_in_frame(box, "Error");
853 entry_set_int_value(entry, ts->error);
854 entry = new_info_entry_in_frame(box, "PID");
855 entry_set_int_value(entry, ts->pid);
857 if (ts->io_bytes[DDIR_READ])
858 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
859 if (ts->io_bytes[DDIR_WRITE])
860 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
862 gfio_show_latency_buckets(vbox, ts);
863 gfio_show_cpu_usage(vbox, ts);
864 gfio_show_io_depths(vbox, ts);
866 gtk_widget_show_all(gc->ui->results_window);
870 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
872 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
873 struct gfio_client *gc = client->client_data;
877 char tmp[64], timebuf[80];
880 tm = localtime(&sec);
881 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
882 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
886 gtk_list_store_append(gc->ui->log_model, &iter);
887 gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
888 gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
889 gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
890 gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
895 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
897 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
898 struct gfio_client *gc = client->client_data;
899 GtkWidget *box, *frame, *entry, *vbox;
903 if (!gc->results_widget) {
904 printf("no results!\n");
908 if (!gc->disk_util_frame) {
909 gc->disk_util_frame = gtk_frame_new("Disk utilization");
910 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
913 vbox = gtk_vbox_new(FALSE, 3);
914 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
916 frame = gtk_frame_new((char *) p->dus.name);
917 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
919 box = gtk_vbox_new(FALSE, 3);
920 gtk_container_add(GTK_CONTAINER(frame), box);
922 frame = gtk_frame_new("Read");
923 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
924 vbox = gtk_hbox_new(TRUE, 3);
925 gtk_container_add(GTK_CONTAINER(frame), vbox);
926 entry = new_info_entry_in_frame(vbox, "IOs");
927 entry_set_int_value(entry, p->dus.ios[0]);
928 entry = new_info_entry_in_frame(vbox, "Merges");
929 entry_set_int_value(entry, p->dus.merges[0]);
930 entry = new_info_entry_in_frame(vbox, "Sectors");
931 entry_set_int_value(entry, p->dus.sectors[0]);
932 entry = new_info_entry_in_frame(vbox, "Ticks");
933 entry_set_int_value(entry, p->dus.ticks[0]);
935 frame = gtk_frame_new("Write");
936 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
937 vbox = gtk_hbox_new(TRUE, 3);
938 gtk_container_add(GTK_CONTAINER(frame), vbox);
939 entry = new_info_entry_in_frame(vbox, "IOs");
940 entry_set_int_value(entry, p->dus.ios[1]);
941 entry = new_info_entry_in_frame(vbox, "Merges");
942 entry_set_int_value(entry, p->dus.merges[1]);
943 entry = new_info_entry_in_frame(vbox, "Sectors");
944 entry_set_int_value(entry, p->dus.sectors[1]);
945 entry = new_info_entry_in_frame(vbox, "Ticks");
946 entry_set_int_value(entry, p->dus.ticks[1]);
948 frame = gtk_frame_new("Shared");
949 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
950 vbox = gtk_hbox_new(TRUE, 3);
951 gtk_container_add(GTK_CONTAINER(frame), vbox);
952 entry = new_info_entry_in_frame(vbox, "IO ticks");
953 entry_set_int_value(entry, p->dus.io_ticks);
954 entry = new_info_entry_in_frame(vbox, "Time in queue");
955 entry_set_int_value(entry, p->dus.time_in_queue);
957 gtk_widget_show_all(gc->results_widget);
962 extern int sum_stat_clients;
963 extern struct thread_stat client_ts;
964 extern struct group_run_stats client_gs;
966 static int sum_stat_nr;
968 static void gfio_thread_status_op(struct fio_client *client,
969 struct fio_net_cmd *cmd)
971 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
973 gfio_display_ts(client, &p->ts, &p->rs);
975 if (sum_stat_clients == 1)
978 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
979 sum_group_stats(&client_gs, &p->rs);
982 client_ts.groupid = p->ts.groupid;
984 if (++sum_stat_nr == sum_stat_clients) {
985 strcpy(client_ts.name, "All clients");
986 gfio_display_ts(client, &client_ts, &client_gs);
990 static void gfio_group_stats_op(struct fio_client *client,
991 struct fio_net_cmd *cmd)
994 printf("gfio_group_stats_op called\n");
995 fio_client_ops.group_stats(client, cmd);
999 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1001 struct gui *ui = (struct gui *) p;
1004 cr = gdk_cairo_create(w->window);
1006 cairo_set_source_rgb(cr, 0, 0, 0);
1009 cairo_translate(cr, 0, 0);
1010 line_graph_draw(ui->bandwidth_graph, cr);
1015 cairo_translate(cr, DRAWING_AREA_XDIM / 2.0, 0);
1016 // DRAWING_AREA_YDIM * 0.05);
1017 line_graph_draw(ui->iops_graph, cr);
1025 static void gfio_update_eta(struct jobs_eta *je)
1027 static int eta_good;
1034 gdk_threads_enter();
1039 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1040 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1041 eta_to_str(eta_str, je->eta_sec);
1044 sprintf(tmp, "%u", je->nr_running);
1045 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
1046 sprintf(tmp, "%u", je->files_open);
1047 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
1050 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1051 if (je->m_rate || je->t_rate) {
1054 mr = num2str(je->m_rate, 4, 0, i2p);
1055 tr = num2str(je->t_rate, 4, 0, i2p);
1056 gtk_entry_set_text(GTK_ENTRY(ui.eta);
1057 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1060 } else if (je->m_iops || je->t_iops)
1061 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1063 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
1064 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
1065 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
1066 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
1069 if (je->eta_sec != INT_MAX && je->nr_running) {
1073 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1074 strcpy(output, "-.-% done");
1078 sprintf(output, "%3.1f%% done", perc);
1081 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1082 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1084 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1085 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1087 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1088 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1089 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1090 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1092 graph_add_xy_data(ui.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1093 graph_add_xy_data(ui.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1094 graph_add_xy_data(ui.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1095 graph_add_xy_data(ui.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1104 char *dst = output + strlen(output);
1106 sprintf(dst, " - %s", eta_str);
1109 gfio_update_thread_status(output, perc);
1110 gdk_threads_leave();
1113 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1115 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1116 struct gfio_client *gc = client->client_data;
1117 struct gui *ui = gc->ui;
1118 const char *os, *arch;
1121 os = fio_get_os_string(probe->os);
1125 arch = fio_get_arch_string(probe->arch);
1130 client->name = strdup((char *) probe->hostname);
1132 gdk_threads_enter();
1134 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1135 gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1136 gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1137 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1138 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1140 gfio_set_connected(ui, 1);
1142 gdk_threads_leave();
1145 static void gfio_update_thread_status(char *status_message, double perc)
1147 static char message[100];
1148 const char *m = message;
1150 strncpy(message, status_message, sizeof(message) - 1);
1151 gtk_progress_bar_set_text(
1152 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1153 gtk_progress_bar_set_fraction(
1154 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1155 gtk_widget_queue_draw(ui.window);
1158 static void gfio_quit_op(struct fio_client *client)
1160 struct gfio_client *gc = client->client_data;
1162 gdk_threads_enter();
1163 gfio_set_connected(gc->ui, 0);
1164 gdk_threads_leave();
1167 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1169 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1170 struct gfio_client *gc = client->client_data;
1171 struct gui *ui = gc->ui;
1175 p->iodepth = le32_to_cpu(p->iodepth);
1176 p->rw = le32_to_cpu(p->rw);
1178 for (i = 0; i < 2; i++) {
1179 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
1180 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
1183 p->numjobs = le32_to_cpu(p->numjobs);
1184 p->group_reporting = le32_to_cpu(p->group_reporting);
1186 gdk_threads_enter();
1188 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1189 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1190 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1192 sprintf(tmp, "%u", p->iodepth);
1193 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1195 gdk_threads_leave();
1198 static void gfio_client_timed_out(struct fio_client *client)
1200 struct gfio_client *gc = client->client_data;
1201 GtkWidget *dialog, *label, *content;
1204 gdk_threads_enter();
1206 gfio_set_connected(gc->ui, 0);
1207 clear_ui_info(gc->ui);
1209 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1211 dialog = gtk_dialog_new_with_buttons("Timed out!",
1212 GTK_WINDOW(gc->ui->window),
1213 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1214 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1216 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1217 label = gtk_label_new((const gchar *) buf);
1218 gtk_container_add(GTK_CONTAINER(content), label);
1219 gtk_widget_show_all(dialog);
1220 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1222 gtk_dialog_run(GTK_DIALOG(dialog));
1223 gtk_widget_destroy(dialog);
1225 gdk_threads_leave();
1228 struct client_ops gfio_client_ops = {
1229 .text_op = gfio_text_op,
1230 .disk_util = gfio_disk_util_op,
1231 .thread_status = gfio_thread_status_op,
1232 .group_stats = gfio_group_stats_op,
1233 .eta = gfio_update_eta,
1234 .probe = gfio_probe_op,
1235 .quit = gfio_quit_op,
1236 .add_job = gfio_add_job_op,
1237 .timed_out = gfio_client_timed_out,
1238 .stay_connected = 1,
1241 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1242 __attribute__((unused)) gpointer data)
1247 static void *job_thread(void *arg)
1249 fio_handle_clients(&gfio_client_ops);
1253 static int send_job_files(struct gui *ui)
1257 for (i = 0; i < ui->nr_job_files; i++) {
1258 ret = fio_clients_send_ini(ui->job_files[i]);
1262 free(ui->job_files[i]);
1263 ui->job_files[i] = NULL;
1265 while (i < ui->nr_job_files) {
1266 free(ui->job_files[i]);
1267 ui->job_files[i] = NULL;
1274 static void start_job_thread(struct gui *ui)
1276 if (send_job_files(ui)) {
1277 printf("Yeah, I didn't really like those options too much.\n");
1278 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1283 static void *server_thread(void *arg)
1286 gfio_server_running = 1;
1287 fio_start_server(NULL);
1288 gfio_server_running = 0;
1292 static void gfio_start_server(struct gui *ui)
1294 if (!gfio_server_running) {
1295 gfio_server_running = 1;
1296 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1297 pthread_detach(ui->server_t);
1301 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1304 struct gui *ui = data;
1306 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1307 start_job_thread(ui);
1310 static void file_open(GtkWidget *w, gpointer data);
1312 static void connect_clicked(GtkWidget *widget, gpointer data)
1314 struct gui *ui = data;
1316 if (!ui->connected) {
1317 if (!ui->nr_job_files)
1318 file_open(widget, data);
1319 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1320 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1321 if (!fio_clients_connect()) {
1322 pthread_create(&ui->t, NULL, job_thread, NULL);
1323 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1326 fio_clients_terminate();
1327 gfio_set_connected(ui, 0);
1332 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1333 struct button_spec *buttonspec)
1335 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1336 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1337 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1338 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1339 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1342 static void add_buttons(struct gui *ui,
1343 struct button_spec *buttonlist,
1348 for (i = 0; i < nbuttons; i++)
1349 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1352 static void on_info_bar_response(GtkWidget *widget, gint response,
1355 if (response == GTK_RESPONSE_OK) {
1356 gtk_widget_destroy(widget);
1357 ui.error_info_bar = NULL;
1361 void report_error(GError *error)
1363 if (ui.error_info_bar == NULL) {
1364 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1367 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1368 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1371 ui.error_label = gtk_label_new(error->message);
1372 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1373 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1375 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1376 gtk_widget_show_all(ui.vbox);
1379 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1380 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1384 static int get_connection_details(char **host, int *port, int *type,
1387 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1391 dialog = gtk_dialog_new_with_buttons("Connection details",
1392 GTK_WINDOW(ui.window),
1393 GTK_DIALOG_DESTROY_WITH_PARENT,
1394 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1395 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1397 frame = gtk_frame_new("Hostname / socket name");
1398 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1399 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1401 box = gtk_vbox_new(FALSE, 6);
1402 gtk_container_add(GTK_CONTAINER(frame), box);
1404 hbox = gtk_hbox_new(TRUE, 10);
1405 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1406 hentry = gtk_entry_new();
1407 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1408 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1410 frame = gtk_frame_new("Port");
1411 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1412 box = gtk_vbox_new(FALSE, 10);
1413 gtk_container_add(GTK_CONTAINER(frame), box);
1415 hbox = gtk_hbox_new(TRUE, 4);
1416 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1417 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1419 frame = gtk_frame_new("Type");
1420 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1421 box = gtk_vbox_new(FALSE, 10);
1422 gtk_container_add(GTK_CONTAINER(frame), box);
1424 hbox = gtk_hbox_new(TRUE, 4);
1425 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1427 combo = gtk_combo_box_new_text();
1428 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1429 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1430 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
1431 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1433 gtk_container_add(GTK_CONTAINER(hbox), combo);
1435 frame = gtk_frame_new("Options");
1436 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1437 box = gtk_vbox_new(FALSE, 10);
1438 gtk_container_add(GTK_CONTAINER(frame), box);
1440 hbox = gtk_hbox_new(TRUE, 4);
1441 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1443 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1444 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1445 gtk_widget_set_tooltip_text(button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
1446 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1448 gtk_widget_show_all(dialog);
1450 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1451 gtk_widget_destroy(dialog);
1455 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1456 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1458 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1459 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1460 *type = Fio_client_ipv4;
1461 else if (!strncmp(typeentry, "IPv6", 4))
1462 *type = Fio_client_ipv6;
1464 *type = Fio_client_socket;
1467 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1469 gtk_widget_destroy(dialog);
1473 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1475 struct gfio_client *gc;
1477 gc = malloc(sizeof(*gc));
1478 memset(gc, 0, sizeof(*gc));
1481 client->client_data = gc;
1484 static void file_open(GtkWidget *w, gpointer data)
1487 struct gui *ui = data;
1488 GSList *filenames, *fn_glist;
1489 GtkFileFilter *filter;
1491 int port, type, server_start;
1493 dialog = gtk_file_chooser_dialog_new("Open File",
1494 GTK_WINDOW(ui->window),
1495 GTK_FILE_CHOOSER_ACTION_OPEN,
1496 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1497 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1499 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1501 filter = gtk_file_filter_new();
1502 gtk_file_filter_add_pattern(filter, "*.fio");
1503 gtk_file_filter_add_pattern(filter, "*.job");
1504 gtk_file_filter_add_pattern(filter, "*.ini");
1505 gtk_file_filter_add_mime_type(filter, "text/fio");
1506 gtk_file_filter_set_name(filter, "Fio job file");
1507 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1509 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1510 gtk_widget_destroy(dialog);
1514 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1516 gtk_widget_destroy(dialog);
1518 if (get_connection_details(&host, &port, &type, &server_start))
1521 filenames = fn_glist;
1522 while (filenames != NULL) {
1523 struct fio_client *client;
1525 ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1526 ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1529 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1533 error = g_error_new(g_quark_from_string("fio"), 1,
1534 "Failed to add client %s", host);
1535 report_error(error);
1536 g_error_free(error);
1538 gfio_client_added(ui, client);
1540 g_free(filenames->data);
1541 filenames = g_slist_next(filenames);
1546 gfio_start_server(ui);
1548 g_slist_free(fn_glist);
1551 static void file_save(GtkWidget *w, gpointer data)
1553 struct gui *ui = data;
1556 dialog = gtk_file_chooser_dialog_new("Save File",
1557 GTK_WINDOW(ui->window),
1558 GTK_FILE_CHOOSER_ACTION_SAVE,
1559 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1560 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1563 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1564 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1566 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1569 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1570 // save_job_file(filename);
1573 gtk_widget_destroy(dialog);
1576 static void view_log_destroy(GtkWidget *w, gpointer data)
1578 struct gui *ui = (struct gui *) data;
1580 gtk_widget_ref(ui->log_tree);
1581 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1582 gtk_widget_destroy(w);
1583 ui->log_view = NULL;
1586 static void view_log(GtkWidget *w, gpointer data)
1588 GtkWidget *win, *scroll, *vbox, *box;
1589 struct gui *ui = (struct gui *) data;
1594 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1595 gtk_window_set_title(GTK_WINDOW(win), "Log");
1596 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1598 scroll = gtk_scrolled_window_new(NULL, NULL);
1600 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1602 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1604 box = gtk_hbox_new(TRUE, 0);
1605 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1606 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1607 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1609 vbox = gtk_vbox_new(TRUE, 5);
1610 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1612 gtk_container_add(GTK_CONTAINER(win), vbox);
1613 gtk_widget_show_all(win);
1616 static void preferences(GtkWidget *w, gpointer data)
1618 GtkWidget *dialog, *frame, *box, **buttons;
1621 dialog = gtk_dialog_new_with_buttons("Preferences",
1622 GTK_WINDOW(ui.window),
1623 GTK_DIALOG_DESTROY_WITH_PARENT,
1624 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1625 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1628 frame = gtk_frame_new("Debug logging");
1629 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1630 box = gtk_hbox_new(FALSE, 6);
1631 gtk_container_add(GTK_CONTAINER(frame), box);
1633 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1635 for (i = 0; i < FD_DEBUG_MAX; i++) {
1636 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1637 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1638 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1641 gtk_widget_show_all(dialog);
1643 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1644 gtk_widget_destroy(dialog);
1648 for (i = 0; i < FD_DEBUG_MAX; i++) {
1651 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1653 fio_debug |= (1UL << i);
1656 gtk_widget_destroy(dialog);
1659 static void about_dialog(GtkWidget *w, gpointer data)
1661 gtk_show_about_dialog(NULL,
1662 "program-name", "gfio",
1663 "comments", "Gtk2 UI for fio",
1665 "version", fio_version_string,
1666 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1667 "logo-icon-name", "fio",
1673 static GtkActionEntry menu_items[] = {
1674 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1675 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1676 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1677 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1678 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1679 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1680 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1681 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1682 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1684 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1686 static const gchar *ui_string = " \
1688 <menubar name=\"MainMenu\"> \
1689 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1690 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1691 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1692 <separator name=\"Separator\"/> \
1693 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1694 <separator name=\"Separator2\"/> \
1695 <menuitem name=\"Quit\" action=\"Quit\" /> \
1697 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1698 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1700 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1701 <menuitem name=\"About\" action=\"About\" /> \
1707 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1710 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1713 action_group = gtk_action_group_new("Menu");
1714 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1716 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1717 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1719 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1720 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1723 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1724 GtkWidget *vbox, GtkUIManager *ui_manager)
1726 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1729 static void init_ui(int *argc, char **argv[], struct gui *ui)
1731 GtkSettings *settings;
1732 GtkUIManager *uimanager;
1733 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1735 memset(ui, 0, sizeof(*ui));
1737 /* Magical g*thread incantation, you just need this thread stuff.
1738 * Without it, the update that happens in gfio_update_thread_status
1739 * doesn't really happen in a timely fashion, you need expose events
1741 if (!g_thread_supported())
1742 g_thread_init(NULL);
1745 gtk_init(argc, argv);
1746 settings = gtk_settings_get_default();
1747 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1750 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1751 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1752 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1754 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1755 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1757 ui->vbox = gtk_vbox_new(FALSE, 0);
1758 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1760 uimanager = gtk_ui_manager_new();
1761 menu = get_menubar_menu(ui->window, uimanager, ui);
1762 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1765 * Set up alignments for widgets at the top of ui,
1766 * align top left, expand horizontally but not vertically
1768 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1769 ui->topvbox = gtk_vbox_new(FALSE, 3);
1770 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1771 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1773 probe = gtk_frame_new("Job");
1774 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1775 probe_frame = gtk_vbox_new(FALSE, 3);
1776 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1778 probe_box = gtk_hbox_new(FALSE, 3);
1779 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1780 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1781 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1782 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1783 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1785 probe_box = gtk_hbox_new(FALSE, 3);
1786 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1788 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1789 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1790 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1791 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1792 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1793 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1795 probe_box = gtk_hbox_new(FALSE, 3);
1796 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1797 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1798 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1799 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1800 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1803 * Only add this if we have a commit rate
1806 probe_box = gtk_hbox_new(FALSE, 3);
1807 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1809 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1810 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1812 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1813 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1817 * Set up a drawing area and IOPS and bandwidth graphs
1819 ui->drawing_area = gtk_drawing_area_new();
1820 gtk_widget_set_size_request(GTK_WIDGET(ui->drawing_area),
1821 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1822 g_signal_connect(G_OBJECT(ui->drawing_area), "expose_event",
1823 G_CALLBACK (on_expose_drawing_area), ui);
1824 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1825 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1826 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1827 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1829 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1832 setup_iops_graph(ui);
1833 setup_bandwidth_graph(ui);
1836 * Set up alignments for widgets at the bottom of ui,
1837 * align bottom left, expand horizontally but not vertically
1839 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1840 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1841 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1842 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1845 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1848 * Set up thread status progress bar
1850 ui->thread_status_pb = gtk_progress_bar_new();
1851 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1852 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1853 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1855 gfio_ui_setup_log(ui);
1857 gtk_widget_show_all(ui->window);
1860 int main(int argc, char *argv[], char *envp[])
1862 if (initialize_fio(envp))
1864 if (fio_init_options())
1867 init_ui(&argc, &argv, &ui);
1869 gdk_threads_enter();
1871 gdk_threads_leave();