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;
37 static unsigned int gfio_graph_limit = 100;
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);
47 static void send_clicked(GtkWidget *widget, gpointer data);
49 static struct button_spec {
50 const char *buttontext;
52 const char *tooltiptext;
53 const int start_insensitive;
54 } buttonspeclist[] = {
55 #define CONNECT_BUTTON 0
57 #define START_JOB_BUTTON 2
58 { "Connect", connect_clicked, "Connect to host", 0 },
59 { "Send", send_clicked, "Send job description to host", 1 },
60 { "Start Job", start_job_clicked,
61 "Start the current job on the server", 1 },
83 GtkWidget *write_iops;
89 #define DRAWING_AREA_XDIM 1000
90 #define DRAWING_AREA_YDIM 400
91 GtkWidget *drawing_area;
92 int drawing_area_xdim;
93 int drawing_area_ydim;
95 struct graph *iops_graph;
96 struct graph *bandwidth_graph;
100 * Main window widgets and data
107 GtkWidget *bottomalign;
108 GtkWidget *thread_status_pb;
109 GtkWidget *buttonbox;
110 GtkWidget *scrolled_window;
112 GtkWidget *error_info_bar;
113 GtkWidget *error_label;
114 GtkListStore *log_model;
117 struct gfio_graphs graphs;
118 struct probe_widget probe;
119 struct eta_widget eta;
125 struct flist_head list;
132 struct flist_head list;
138 GtkWidget *bottomalign;
139 GtkWidget *thread_status_pb;
140 GtkWidget *buttonbox;
141 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
142 GtkWidget *scrolled_window;
144 GtkWidget *error_info_bar;
145 GtkWidget *error_label;
146 GtkWidget *results_notebook;
147 GtkWidget *results_window;
148 GtkListStore *log_model;
151 struct gfio_graphs graphs;
152 struct probe_widget probe;
153 struct eta_widget eta;
154 GtkWidget *page_label;
158 struct gfio_client *client;
164 struct gui_entry *ge;
165 struct fio_client *client;
166 GtkWidget *results_widget;
167 GtkWidget *disk_util_frame;
168 GtkWidget *err_entry;
169 unsigned int job_added;
170 struct thread_options o;
173 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
174 static void gfio_update_thread_status_all(char *status_message, double perc);
175 void report_error(GError *error);
177 static struct graph *setup_iops_graph(void)
181 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
182 graph_title(g, "IOPS");
183 graph_x_title(g, "Time (secs)");
184 graph_y_title(g, "IOs / sec");
185 graph_add_label(g, "Read IOPS");
186 graph_add_label(g, "Write IOPS");
187 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
188 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
189 line_graph_set_data_count_limit(g, gfio_graph_limit);
193 static struct graph *setup_bandwidth_graph(void)
197 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
198 graph_title(g, "Bandwidth");
199 graph_x_title(g, "Time (secs)");
200 graph_y_title(g, "Kbytes / sec");
201 graph_add_label(g, "Read Bandwidth");
202 graph_add_label(g, "Write Bandwidth");
203 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
204 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
205 line_graph_set_data_count_limit(g, 100);
209 static void setup_graphs(struct gfio_graphs *g)
211 g->iops_graph = setup_iops_graph();
212 g->bandwidth_graph = setup_bandwidth_graph();
215 static void clear_ge_ui_info(struct gui_entry *ge)
217 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
218 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
219 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
220 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
222 /* should we empty it... */
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
225 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
226 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
227 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
228 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
229 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
230 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
231 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
232 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
233 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
236 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
238 GtkWidget *entry, *frame;
240 frame = gtk_frame_new(label);
241 entry = gtk_combo_box_new_text();
242 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
243 gtk_container_add(GTK_CONTAINER(frame), entry);
248 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
250 GtkWidget *entry, *frame;
252 frame = gtk_frame_new(label);
253 entry = gtk_entry_new();
254 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
255 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
256 gtk_container_add(GTK_CONTAINER(frame), entry);
261 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
263 GtkWidget *label_widget;
266 frame = gtk_frame_new(label);
267 label_widget = gtk_label_new(NULL);
268 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
269 gtk_container_add(GTK_CONTAINER(frame), label_widget);
274 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
276 GtkWidget *button, *box;
278 box = gtk_hbox_new(FALSE, 3);
279 gtk_container_add(GTK_CONTAINER(hbox), box);
281 button = gtk_spin_button_new_with_range(min, max, 1.0);
282 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
284 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
285 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
290 static void gfio_set_connected(struct gui_entry *ge, int connected)
293 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
295 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
296 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
299 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
300 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
301 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
302 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
306 static void label_set_int_value(GtkWidget *entry, unsigned int val)
310 sprintf(tmp, "%u", val);
311 gtk_label_set_text(GTK_LABEL(entry), tmp);
314 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
318 sprintf(tmp, "%u", val);
319 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
323 #define ALIGN_RIGHT 2
327 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
329 GtkCellRenderer *renderer;
330 GtkTreeViewColumn *col;
331 double xalign = 0.0; /* left as default */
332 PangoAlignment align;
335 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
336 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
338 visible = !(flags & INVISIBLE);
340 renderer = gtk_cell_renderer_text_new();
341 col = gtk_tree_view_column_new();
343 gtk_tree_view_column_set_title(col, title);
344 if (!(flags & UNSORTABLE))
345 gtk_tree_view_column_set_sort_column_id(col, index);
346 gtk_tree_view_column_set_resizable(col, TRUE);
347 gtk_tree_view_column_pack_start(col, renderer, TRUE);
348 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
349 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
351 case PANGO_ALIGN_LEFT:
354 case PANGO_ALIGN_CENTER:
357 case PANGO_ALIGN_RIGHT:
361 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
362 gtk_tree_view_column_set_visible(col, visible);
363 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
367 static void gfio_ui_setup_log(struct gui *ui)
369 GtkTreeSelection *selection;
371 GtkWidget *tree_view;
373 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
375 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
376 gtk_widget_set_can_focus(tree_view, FALSE);
378 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
379 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
380 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
381 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
383 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
384 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
385 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
386 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
388 ui->log_model = model;
389 ui->log_tree = tree_view;
392 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
398 GType types[FIO_IO_U_LIST_MAX_LEN];
399 GtkWidget *tree_view;
400 GtkTreeSelection *selection;
405 for (i = 0; i < len; i++)
406 types[i] = G_TYPE_INT;
408 model = gtk_list_store_newv(len, types);
410 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
411 gtk_widget_set_can_focus(tree_view, FALSE);
413 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
414 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
416 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
417 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
419 for (i = 0; i < len; i++) {
422 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
423 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
426 gtk_list_store_append(model, &iter);
428 for (i = 0; i < len; i++) {
430 ovals[i] = (ovals[i] + 999) / 1000;
431 gtk_list_store_set(model, &iter, i, ovals[i], -1);
437 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
440 unsigned int *io_u_plat = ts->io_u_plat[ddir];
441 unsigned long nr = ts->clat_stat[ddir].samples;
442 fio_fp64_t *plist = ts->percentile_list;
443 unsigned int *ovals, len, minv, maxv, scale_down;
445 GtkWidget *tree_view, *frame, *hbox;
448 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
453 * We default to usecs, but if the value range is such that we
454 * should scale down to msecs, do that.
456 if (minv > 2000 && maxv > 99999) {
464 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
466 sprintf(tmp, "Completion percentiles (%s)", base);
467 frame = gtk_frame_new(tmp);
468 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
470 hbox = gtk_hbox_new(FALSE, 3);
471 gtk_container_add(GTK_CONTAINER(frame), hbox);
473 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
479 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
480 unsigned long max, double mean, double dev)
482 const char *base = "(usec)";
483 GtkWidget *hbox, *label, *frame;
487 if (!usec_to_msec(&min, &max, &mean, &dev))
490 minp = num2str(min, 6, 1, 0);
491 maxp = num2str(max, 6, 1, 0);
493 sprintf(tmp, "%s %s", name, base);
494 frame = gtk_frame_new(tmp);
495 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
497 hbox = gtk_hbox_new(FALSE, 3);
498 gtk_container_add(GTK_CONTAINER(frame), hbox);
500 label = new_info_label_in_frame(hbox, "Minimum");
501 gtk_label_set_text(GTK_LABEL(label), minp);
502 label = new_info_label_in_frame(hbox, "Maximum");
503 gtk_label_set_text(GTK_LABEL(label), maxp);
504 label = new_info_label_in_frame(hbox, "Average");
505 sprintf(tmp, "%5.02f", mean);
506 gtk_label_set_text(GTK_LABEL(label), tmp);
507 label = new_info_label_in_frame(hbox, "Standard deviation");
508 sprintf(tmp, "%5.02f", dev);
509 gtk_label_set_text(GTK_LABEL(label), tmp);
520 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
521 struct thread_stat *ts, int ddir)
523 const char *ddir_label[2] = { "Read", "Write" };
524 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
525 unsigned long min[3], max[3], runt;
526 unsigned long long bw, iops;
527 unsigned int flags = 0;
528 double mean[3], dev[3];
529 char *io_p, *bw_p, *iops_p;
532 if (!ts->runtime[ddir])
535 i2p = is_power_of_2(rs->kb_base);
536 runt = ts->runtime[ddir];
538 bw = (1000 * ts->io_bytes[ddir]) / runt;
539 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
540 bw_p = num2str(bw, 6, 1, i2p);
542 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
543 iops_p = num2str(iops, 6, 1, 0);
545 box = gtk_hbox_new(FALSE, 3);
546 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
548 frame = gtk_frame_new(ddir_label[ddir]);
549 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
551 main_vbox = gtk_vbox_new(FALSE, 3);
552 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
554 box = gtk_hbox_new(FALSE, 3);
555 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
557 label = new_info_label_in_frame(box, "IO");
558 gtk_label_set_text(GTK_LABEL(label), io_p);
559 label = new_info_label_in_frame(box, "Bandwidth");
560 gtk_label_set_text(GTK_LABEL(label), bw_p);
561 label = new_info_label_in_frame(box, "IOPS");
562 gtk_label_set_text(GTK_LABEL(label), iops_p);
563 label = new_info_label_in_frame(box, "Runtime (msec)");
564 label_set_int_value(label, ts->runtime[ddir]);
566 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
567 double p_of_agg = 100.0;
568 const char *bw_str = "KB";
572 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
573 if (p_of_agg > 100.0)
577 if (mean[0] > 999999.9) {
585 sprintf(tmp, "Bandwidth (%s)", bw_str);
586 frame = gtk_frame_new(tmp);
587 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
589 box = gtk_hbox_new(FALSE, 3);
590 gtk_container_add(GTK_CONTAINER(frame), box);
592 label = new_info_label_in_frame(box, "Minimum");
593 label_set_int_value(label, min[0]);
594 label = new_info_label_in_frame(box, "Maximum");
595 label_set_int_value(label, max[0]);
596 label = new_info_label_in_frame(box, "Percentage of jobs");
597 sprintf(tmp, "%3.2f%%", p_of_agg);
598 gtk_label_set_text(GTK_LABEL(label), tmp);
599 label = new_info_label_in_frame(box, "Average");
600 sprintf(tmp, "%5.02f", mean[0]);
601 gtk_label_set_text(GTK_LABEL(label), tmp);
602 label = new_info_label_in_frame(box, "Standard deviation");
603 sprintf(tmp, "%5.02f", dev[0]);
604 gtk_label_set_text(GTK_LABEL(label), tmp);
607 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
609 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
611 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
615 frame = gtk_frame_new("Latency");
616 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
618 vbox = gtk_vbox_new(FALSE, 3);
619 gtk_container_add(GTK_CONTAINER(frame), vbox);
621 if (flags & GFIO_SLAT)
622 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
623 if (flags & GFIO_CLAT)
624 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
625 if (flags & GFIO_LAT)
626 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
629 if (ts->clat_percentiles)
630 gfio_show_clat_percentiles(main_vbox, ts, ddir);
638 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
641 GtkWidget *tree_view;
642 GtkTreeSelection *selection;
649 * Check if all are empty, in which case don't bother
651 for (i = 0, skipped = 0; i < num; i++)
658 types = malloc(num * sizeof(GType));
660 for (i = 0; i < num; i++)
661 types[i] = G_TYPE_STRING;
663 model = gtk_list_store_newv(num, types);
667 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
668 gtk_widget_set_can_focus(tree_view, FALSE);
670 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
671 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
673 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
674 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
676 for (i = 0; i < num; i++)
677 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
679 gtk_list_store_append(model, &iter);
681 for (i = 0; i < num; i++) {
685 sprintf(fbuf, "0.00");
687 sprintf(fbuf, "%3.2f%%", lat[i]);
689 gtk_list_store_set(model, &iter, i, fbuf, -1);
695 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
697 GtkWidget *box, *frame, *tree_view;
698 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
699 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
700 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
701 "250", "500", "750", "1000", };
702 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
703 "250", "500", "750", "1000", "2000",
706 stat_calc_lat_u(ts, io_u_lat_u);
707 stat_calc_lat_m(ts, io_u_lat_m);
709 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
711 frame = gtk_frame_new("Latency buckets (usec)");
712 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
714 box = gtk_hbox_new(FALSE, 3);
715 gtk_container_add(GTK_CONTAINER(frame), box);
716 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
719 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
721 frame = gtk_frame_new("Latency buckets (msec)");
722 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
724 box = gtk_hbox_new(FALSE, 3);
725 gtk_container_add(GTK_CONTAINER(frame), box);
726 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
730 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
732 GtkWidget *box, *frame, *entry;
733 double usr_cpu, sys_cpu;
734 unsigned long runtime;
737 runtime = ts->total_run_time;
739 double runt = (double) runtime;
741 usr_cpu = (double) ts->usr_time * 100 / runt;
742 sys_cpu = (double) ts->sys_time * 100 / runt;
748 frame = gtk_frame_new("OS resources");
749 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
751 box = gtk_hbox_new(FALSE, 3);
752 gtk_container_add(GTK_CONTAINER(frame), box);
754 entry = new_info_entry_in_frame(box, "User CPU");
755 sprintf(tmp, "%3.2f%%", usr_cpu);
756 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
757 entry = new_info_entry_in_frame(box, "System CPU");
758 sprintf(tmp, "%3.2f%%", sys_cpu);
759 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
760 entry = new_info_entry_in_frame(box, "Context switches");
761 entry_set_int_value(entry, ts->ctx);
762 entry = new_info_entry_in_frame(box, "Major faults");
763 entry_set_int_value(entry, ts->majf);
764 entry = new_info_entry_in_frame(box, "Minor faults");
765 entry_set_int_value(entry, ts->minf);
767 static void gfio_add_sc_depths_tree(GtkListStore *model,
768 struct thread_stat *ts, unsigned int len,
771 double io_u_dist[FIO_IO_U_MAP_NR];
773 /* Bits 0, and 3-8 */
774 const int add_mask = 0x1f9;
778 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
780 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
782 gtk_list_store_append(model, &iter);
784 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
786 for (i = 1, j = 0; i < len; i++) {
789 if (!(add_mask & (1UL << (i - 1))))
790 sprintf(fbuf, "0.0%%");
792 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
796 gtk_list_store_set(model, &iter, i, fbuf, -1);
801 static void gfio_add_total_depths_tree(GtkListStore *model,
802 struct thread_stat *ts, unsigned int len)
804 double io_u_dist[FIO_IO_U_MAP_NR];
806 /* Bits 1-6, and 8 */
807 const int add_mask = 0x17e;
810 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
812 gtk_list_store_append(model, &iter);
814 gtk_list_store_set(model, &iter, 0, "Total", -1);
816 for (i = 1, j = 0; i < len; i++) {
819 if (!(add_mask & (1UL << (i - 1))))
820 sprintf(fbuf, "0.0%%");
822 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
826 gtk_list_store_set(model, &iter, i, fbuf, -1);
831 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
833 GtkWidget *frame, *box, *tree_view;
834 GtkTreeSelection *selection;
836 GType types[FIO_IO_U_MAP_NR + 1];
839 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
841 frame = gtk_frame_new("IO depths");
842 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
844 box = gtk_hbox_new(FALSE, 3);
845 gtk_container_add(GTK_CONTAINER(frame), box);
847 for (i = 0; i < NR_LABELS; i++)
848 types[i] = G_TYPE_STRING;
850 model = gtk_list_store_newv(NR_LABELS, types);
852 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
853 gtk_widget_set_can_focus(tree_view, FALSE);
855 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
856 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
858 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
859 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
861 for (i = 0; i < NR_LABELS; i++)
862 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
864 gfio_add_total_depths_tree(model, ts, NR_LABELS);
865 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
866 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
868 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
871 static gboolean results_window_delete(GtkWidget *w, gpointer data)
873 struct gui_entry *ge = (struct gui_entry *) data;
875 gtk_widget_destroy(w);
876 ge->results_window = NULL;
877 ge->results_notebook = NULL;
881 static GtkWidget *get_results_window(struct gui_entry *ge)
883 GtkWidget *win, *notebook;
885 if (ge->results_window)
886 return ge->results_notebook;
888 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
889 gtk_window_set_title(GTK_WINDOW(win), "Results");
890 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
891 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
892 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
894 notebook = gtk_notebook_new();
895 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
896 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
897 gtk_container_add(GTK_CONTAINER(win), notebook);
899 ge->results_window = win;
900 ge->results_notebook = notebook;
901 return ge->results_notebook;
904 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
905 struct group_run_stats *rs)
907 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
908 struct gfio_client *gc = client->client_data;
912 res_win = get_results_window(gc->ge);
914 scroll = gtk_scrolled_window_new(NULL, NULL);
915 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
916 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
918 vbox = gtk_vbox_new(FALSE, 3);
920 box = gtk_hbox_new(FALSE, 0);
921 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
923 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
925 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
927 gc->results_widget = vbox;
929 entry = new_info_entry_in_frame(box, "Name");
930 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
931 if (strlen(ts->description)) {
932 entry = new_info_entry_in_frame(box, "Description");
933 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
935 entry = new_info_entry_in_frame(box, "Group ID");
936 entry_set_int_value(entry, ts->groupid);
937 entry = new_info_entry_in_frame(box, "Jobs");
938 entry_set_int_value(entry, ts->members);
939 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
940 entry_set_int_value(entry, ts->error);
941 entry = new_info_entry_in_frame(box, "PID");
942 entry_set_int_value(entry, ts->pid);
944 if (ts->io_bytes[DDIR_READ])
945 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
946 if (ts->io_bytes[DDIR_WRITE])
947 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
949 gfio_show_latency_buckets(vbox, ts);
950 gfio_show_cpu_usage(vbox, ts);
951 gfio_show_io_depths(vbox, ts);
953 gtk_widget_show_all(gc->ge->results_window);
957 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
959 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
960 struct gui *ui = &main_ui;
964 char tmp[64], timebuf[80];
967 tm = localtime(&sec);
968 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
969 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
973 gtk_list_store_append(ui->log_model, &iter);
974 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
975 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
976 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
977 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
979 if (p->level == FIO_LOG_ERR)
980 view_log(NULL, (gpointer) ui);
985 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
987 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
988 struct gfio_client *gc = client->client_data;
989 GtkWidget *box, *frame, *entry, *vbox;
995 if (!gc->results_widget)
998 if (!gc->disk_util_frame) {
999 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1000 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1003 vbox = gtk_vbox_new(FALSE, 3);
1004 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1006 frame = gtk_frame_new((char *) p->dus.name);
1007 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1009 box = gtk_vbox_new(FALSE, 3);
1010 gtk_container_add(GTK_CONTAINER(frame), box);
1012 frame = gtk_frame_new("Read");
1013 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1014 vbox = gtk_hbox_new(TRUE, 3);
1015 gtk_container_add(GTK_CONTAINER(frame), vbox);
1016 entry = new_info_entry_in_frame(vbox, "IOs");
1017 entry_set_int_value(entry, p->dus.ios[0]);
1018 entry = new_info_entry_in_frame(vbox, "Merges");
1019 entry_set_int_value(entry, p->dus.merges[0]);
1020 entry = new_info_entry_in_frame(vbox, "Sectors");
1021 entry_set_int_value(entry, p->dus.sectors[0]);
1022 entry = new_info_entry_in_frame(vbox, "Ticks");
1023 entry_set_int_value(entry, p->dus.ticks[0]);
1025 frame = gtk_frame_new("Write");
1026 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1027 vbox = gtk_hbox_new(TRUE, 3);
1028 gtk_container_add(GTK_CONTAINER(frame), vbox);
1029 entry = new_info_entry_in_frame(vbox, "IOs");
1030 entry_set_int_value(entry, p->dus.ios[1]);
1031 entry = new_info_entry_in_frame(vbox, "Merges");
1032 entry_set_int_value(entry, p->dus.merges[1]);
1033 entry = new_info_entry_in_frame(vbox, "Sectors");
1034 entry_set_int_value(entry, p->dus.sectors[1]);
1035 entry = new_info_entry_in_frame(vbox, "Ticks");
1036 entry_set_int_value(entry, p->dus.ticks[1]);
1038 frame = gtk_frame_new("Shared");
1039 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1040 vbox = gtk_hbox_new(TRUE, 3);
1041 gtk_container_add(GTK_CONTAINER(frame), vbox);
1042 entry = new_info_entry_in_frame(vbox, "IO ticks");
1043 entry_set_int_value(entry, p->dus.io_ticks);
1044 entry = new_info_entry_in_frame(vbox, "Time in queue");
1045 entry_set_int_value(entry, p->dus.time_in_queue);
1049 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1053 sprintf(tmp, "%3.2f%%", util);
1054 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1055 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1057 gtk_widget_show_all(gc->results_widget);
1059 gdk_threads_leave();
1062 extern int sum_stat_clients;
1063 extern struct thread_stat client_ts;
1064 extern struct group_run_stats client_gs;
1066 static int sum_stat_nr;
1068 static void gfio_thread_status_op(struct fio_client *client,
1069 struct fio_net_cmd *cmd)
1071 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1073 gfio_display_ts(client, &p->ts, &p->rs);
1075 if (sum_stat_clients == 1)
1078 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1079 sum_group_stats(&client_gs, &p->rs);
1081 client_ts.members++;
1082 client_ts.groupid = p->ts.groupid;
1084 if (++sum_stat_nr == sum_stat_clients) {
1085 strcpy(client_ts.name, "All clients");
1086 gfio_display_ts(client, &client_ts, &client_gs);
1090 static void gfio_group_stats_op(struct fio_client *client,
1091 struct fio_net_cmd *cmd)
1093 /* We're ignoring group stats for now */
1096 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1099 struct gfio_graphs *g = data;
1101 g->drawing_area_xdim = w->allocation.width;
1102 g->drawing_area_ydim = w->allocation.height;
1106 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1108 struct gfio_graphs *g = p;
1111 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1112 g->drawing_area_ydim);
1113 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1114 g->drawing_area_ydim);
1115 cr = gdk_cairo_create(w->window);
1117 cairo_set_source_rgb(cr, 0, 0, 0);
1120 cairo_translate(cr, 0, 0);
1121 line_graph_draw(g->bandwidth_graph, cr);
1126 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1127 line_graph_draw(g->iops_graph, cr);
1136 * Client specific ETA
1138 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1140 struct gfio_client *gc = client->client_data;
1141 struct gui_entry *ge = gc->ge;
1142 static int eta_good;
1149 gdk_threads_enter();
1154 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1155 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1156 eta_to_str(eta_str, je->eta_sec);
1159 sprintf(tmp, "%u", je->nr_running);
1160 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1161 sprintf(tmp, "%u", je->files_open);
1162 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1165 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1166 if (je->m_rate || je->t_rate) {
1169 mr = num2str(je->m_rate, 4, 0, i2p);
1170 tr = num2str(je->t_rate, 4, 0, i2p);
1171 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1172 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1175 } else if (je->m_iops || je->t_iops)
1176 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1178 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1179 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1180 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1181 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1184 if (je->eta_sec != INT_MAX && je->nr_running) {
1188 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1189 strcpy(output, "-.-% done");
1193 sprintf(output, "%3.1f%% done", perc);
1196 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1197 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1199 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1200 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1202 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1203 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1204 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1205 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1207 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1208 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1209 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1210 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1219 char *dst = output + strlen(output);
1221 sprintf(dst, " - %s", eta_str);
1224 gfio_update_thread_status(ge, output, perc);
1225 gdk_threads_leave();
1229 * Update ETA in main window for all clients
1231 static void gfio_update_all_eta(struct jobs_eta *je)
1233 struct gui *ui = &main_ui;
1234 static int eta_good;
1240 gdk_threads_enter();
1245 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1246 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1247 eta_to_str(eta_str, je->eta_sec);
1251 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1252 if (je->m_rate || je->t_rate) {
1255 mr = num2str(je->m_rate, 4, 0, i2p);
1256 tr = num2str(je->t_rate, 4, 0, i2p);
1257 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1258 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1261 } else if (je->m_iops || je->t_iops)
1262 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1264 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1265 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1266 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1267 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1270 entry_set_int_value(ui->eta.jobs, je->nr_running);
1272 if (je->eta_sec != INT_MAX && je->nr_running) {
1276 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1277 strcpy(output, "-.-% done");
1281 sprintf(output, "%3.1f%% done", perc);
1284 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1285 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1287 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1288 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1290 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1291 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1292 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1293 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1295 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1296 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1297 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1298 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1307 char *dst = output + strlen(output);
1309 sprintf(dst, " - %s", eta_str);
1312 gfio_update_thread_status_all(output, perc);
1313 gdk_threads_leave();
1316 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1318 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1319 struct gfio_client *gc = client->client_data;
1320 struct gui_entry *ge = gc->ge;
1321 const char *os, *arch;
1324 os = fio_get_os_string(probe->os);
1328 arch = fio_get_arch_string(probe->arch);
1333 client->name = strdup((char *) probe->hostname);
1335 gdk_threads_enter();
1337 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1338 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1339 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1340 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1341 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1343 gfio_set_connected(ge, 1);
1345 gdk_threads_leave();
1348 static void gfio_update_thread_status(struct gui_entry *ge,
1349 char *status_message, double perc)
1351 static char message[100];
1352 const char *m = message;
1354 strncpy(message, status_message, sizeof(message) - 1);
1355 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1356 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1357 gtk_widget_queue_draw(main_ui.window);
1360 static void gfio_update_thread_status_all(char *status_message, double perc)
1362 struct gui *ui = &main_ui;
1363 static char message[100];
1364 const char *m = message;
1366 strncpy(message, status_message, sizeof(message) - 1);
1367 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1368 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1369 gtk_widget_queue_draw(ui->window);
1372 static void gfio_quit_op(struct fio_client *client)
1374 struct gfio_client *gc = client->client_data;
1376 gdk_threads_enter();
1377 gfio_set_connected(gc->ge, 0);
1378 gdk_threads_leave();
1381 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1383 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1384 struct gfio_client *gc = client->client_data;
1385 struct thread_options *o = &gc->o;
1386 struct gui_entry *ge = gc->ge;
1389 convert_thread_options_to_cpu(o, &p->top);
1391 gdk_threads_enter();
1393 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1395 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1396 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1398 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1399 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1401 sprintf(tmp, "%u", o->iodepth);
1402 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1406 gdk_threads_leave();
1409 static void gfio_client_timed_out(struct fio_client *client)
1411 struct gfio_client *gc = client->client_data;
1412 GtkWidget *dialog, *label, *content;
1415 gdk_threads_enter();
1417 gfio_set_connected(gc->ge, 0);
1418 clear_ge_ui_info(gc->ge);
1420 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1422 dialog = gtk_dialog_new_with_buttons("Timed out!",
1423 GTK_WINDOW(main_ui.window),
1424 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1425 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1427 /* gtk_dialog_get_content_area() is 2.14 and newer */
1428 content = GTK_DIALOG(dialog)->vbox;
1430 label = gtk_label_new((const gchar *) buf);
1431 gtk_container_add(GTK_CONTAINER(content), label);
1432 gtk_widget_show_all(dialog);
1433 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1435 gtk_dialog_run(GTK_DIALOG(dialog));
1436 gtk_widget_destroy(dialog);
1438 gdk_threads_leave();
1441 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1443 struct gfio_client *gc = client->client_data;
1445 gdk_threads_enter();
1447 gfio_set_connected(gc->ge, 0);
1450 entry_set_int_value(gc->err_entry, client->error);
1452 gdk_threads_leave();
1455 struct client_ops gfio_client_ops = {
1456 .text_op = gfio_text_op,
1457 .disk_util = gfio_disk_util_op,
1458 .thread_status = gfio_thread_status_op,
1459 .group_stats = gfio_group_stats_op,
1460 .jobs_eta = gfio_update_client_eta,
1461 .eta = gfio_update_all_eta,
1462 .probe = gfio_probe_op,
1463 .quit = gfio_quit_op,
1464 .add_job = gfio_add_job_op,
1465 .timed_out = gfio_client_timed_out,
1466 .stop = gfio_client_stop,
1467 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1468 .stay_connected = 1,
1471 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1472 __attribute__((unused)) gpointer data)
1477 static void *job_thread(void *arg)
1479 struct gui *ui = arg;
1481 ui->handler_running = 1;
1482 fio_handle_clients(&gfio_client_ops);
1483 ui->handler_running = 0;
1487 static int send_job_files(struct gui_entry *ge)
1489 struct gfio_client *gc = ge->client;
1492 for (i = 0; i < ge->nr_job_files; i++) {
1493 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1497 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1498 report_error(error);
1499 g_error_free(error);
1504 free(ge->job_files[i]);
1505 ge->job_files[i] = NULL;
1507 while (i < ge->nr_job_files) {
1508 free(ge->job_files[i]);
1509 ge->job_files[i] = NULL;
1516 static void *server_thread(void *arg)
1519 gfio_server_running = 1;
1520 fio_start_server(NULL);
1521 gfio_server_running = 0;
1525 static void gfio_start_server(void)
1527 struct gui *ui = &main_ui;
1529 if (!gfio_server_running) {
1530 gfio_server_running = 1;
1531 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1532 pthread_detach(ui->server_t);
1536 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1539 struct gui_entry *ge = data;
1540 struct gfio_client *gc = ge->client;
1542 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1543 fio_start_client(gc->client);
1546 static void file_open(GtkWidget *w, gpointer data);
1548 static void connect_clicked(GtkWidget *widget, gpointer data)
1550 struct gui_entry *ge = data;
1551 struct gfio_client *gc = ge->client;
1553 if (!ge->connected) {
1556 if (!ge->nr_job_files)
1557 file_open(widget, data);
1558 if (!ge->nr_job_files)
1561 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1562 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1563 ret = fio_client_connect(gc->client);
1565 if (!ge->ui->handler_running)
1566 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1567 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1568 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1572 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1573 report_error(error);
1574 g_error_free(error);
1577 fio_client_terminate(gc->client);
1578 gfio_set_connected(ge, 0);
1579 clear_ge_ui_info(ge);
1583 static void send_clicked(GtkWidget *widget, gpointer data)
1585 struct gui_entry *ge = data;
1587 if (send_job_files(ge)) {
1590 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1591 report_error(error);
1592 g_error_free(error);
1594 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1597 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1598 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1601 static GtkWidget *add_button(GtkWidget *buttonbox,
1602 struct button_spec *buttonspec, gpointer data)
1604 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1606 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1607 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1608 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1609 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1614 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1619 for (i = 0; i < nbuttons; i++)
1620 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1623 static void on_info_bar_response(GtkWidget *widget, gint response,
1626 struct gui *ui = &main_ui;
1628 if (response == GTK_RESPONSE_OK) {
1629 gtk_widget_destroy(widget);
1630 ui->error_info_bar = NULL;
1634 void report_error(GError *error)
1636 struct gui *ui = &main_ui;
1638 if (ui->error_info_bar == NULL) {
1639 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1642 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1643 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1646 ui->error_label = gtk_label_new(error->message);
1647 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1648 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1650 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1651 gtk_widget_show_all(ui->vbox);
1654 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1655 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1659 struct connection_widgets
1666 static void hostname_cb(GtkEntry *entry, gpointer data)
1668 struct connection_widgets *cw = data;
1669 int uses_net = 0, is_localhost = 0;
1674 * Check whether to display the 'auto start backend' box
1675 * or not. Show it if we are a localhost and using network,
1676 * or using a socket.
1678 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1679 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1684 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1685 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1686 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1687 !strcmp(text, "ip6-loopback"))
1691 if (!uses_net || is_localhost) {
1692 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1693 gtk_widget_set_sensitive(cw->button, 1);
1695 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1696 gtk_widget_set_sensitive(cw->button, 0);
1700 static int get_connection_details(char **host, int *port, int *type,
1703 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1704 struct connection_widgets cw;
1707 dialog = gtk_dialog_new_with_buttons("Connection details",
1708 GTK_WINDOW(main_ui.window),
1709 GTK_DIALOG_DESTROY_WITH_PARENT,
1710 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1711 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1713 frame = gtk_frame_new("Hostname / socket name");
1714 /* gtk_dialog_get_content_area() is 2.14 and newer */
1715 vbox = GTK_DIALOG(dialog)->vbox;
1716 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1718 box = gtk_vbox_new(FALSE, 6);
1719 gtk_container_add(GTK_CONTAINER(frame), box);
1721 hbox = gtk_hbox_new(TRUE, 10);
1722 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1723 cw.hentry = gtk_entry_new();
1724 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1725 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1727 frame = gtk_frame_new("Port");
1728 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1729 box = gtk_vbox_new(FALSE, 10);
1730 gtk_container_add(GTK_CONTAINER(frame), box);
1732 hbox = gtk_hbox_new(TRUE, 4);
1733 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1734 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1736 frame = gtk_frame_new("Type");
1737 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1738 box = gtk_vbox_new(FALSE, 10);
1739 gtk_container_add(GTK_CONTAINER(frame), box);
1741 hbox = gtk_hbox_new(TRUE, 4);
1742 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1744 cw.combo = gtk_combo_box_new_text();
1745 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1746 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1747 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1748 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1750 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1752 frame = gtk_frame_new("Options");
1753 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1754 box = gtk_vbox_new(FALSE, 10);
1755 gtk_container_add(GTK_CONTAINER(frame), box);
1757 hbox = gtk_hbox_new(TRUE, 4);
1758 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1760 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1761 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1762 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.");
1763 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1766 * Connect edit signal, so we can show/not-show the auto start button
1768 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1769 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1771 gtk_widget_show_all(dialog);
1773 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1774 gtk_widget_destroy(dialog);
1778 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1779 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1781 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1782 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1783 *type = Fio_client_ipv4;
1784 else if (!strncmp(typeentry, "IPv6", 4))
1785 *type = Fio_client_ipv6;
1787 *type = Fio_client_socket;
1790 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1792 gtk_widget_destroy(dialog);
1796 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1798 struct gfio_client *gc;
1800 gc = malloc(sizeof(*gc));
1801 memset(gc, 0, sizeof(*gc));
1803 gc->client = fio_get_client(client);
1807 client->client_data = gc;
1810 static GtkWidget *new_client_page(struct gui_entry *ge);
1812 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1814 struct gui_entry *ge;
1816 ge = malloc(sizeof(*ge));
1817 memset(ge, 0, sizeof(*ge));
1818 INIT_FLIST_HEAD(&ge->list);
1819 flist_add_tail(&ge->list, &ui->list);
1825 * FIXME: need more handling here
1827 static void ge_destroy(GtkWidget *w, gpointer data)
1829 struct gui_entry *ge = data;
1830 struct gfio_client *gc = ge->client;
1833 fio_put_client(gc->client);
1835 flist_del(&ge->list);
1839 static struct gui_entry *get_new_ge_with_tab(const char *name)
1841 struct gui_entry *ge;
1843 ge = alloc_new_gui_entry(&main_ui);
1845 ge->vbox = new_client_page(ge);
1846 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1848 ge->page_label = gtk_label_new(name);
1849 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1851 gtk_widget_show_all(main_ui.window);
1855 static void file_new(GtkWidget *w, gpointer data)
1857 get_new_ge_with_tab("Untitled");
1861 * Return the 'ge' corresponding to the tab. If the active tab is the
1862 * main tab, open a new tab.
1864 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1866 struct flist_head *entry;
1867 struct gui_entry *ge;
1870 return get_new_ge_with_tab("Untitled");
1872 flist_for_each(entry, &main_ui.list) {
1873 ge = flist_entry(entry, struct gui_entry, list);
1874 if (ge->page_num == cur_page)
1881 static void file_open(GtkWidget *w, gpointer data)
1883 struct gui *ui = data;
1885 GSList *filenames, *fn_glist;
1886 GtkFileFilter *filter;
1888 int port, type, server_start;
1889 struct gui_entry *ge;
1893 * Creates new tab if current tab is the main window, or the
1894 * current tab already has a client.
1896 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1897 ge = get_ge_from_page(cur_page);
1899 ge = get_new_ge_with_tab("Untitled");
1901 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1903 dialog = gtk_file_chooser_dialog_new("Open File",
1904 GTK_WINDOW(ui->window),
1905 GTK_FILE_CHOOSER_ACTION_OPEN,
1906 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1907 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1909 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1911 filter = gtk_file_filter_new();
1912 gtk_file_filter_add_pattern(filter, "*.fio");
1913 gtk_file_filter_add_pattern(filter, "*.job");
1914 gtk_file_filter_add_pattern(filter, "*.ini");
1915 gtk_file_filter_add_mime_type(filter, "text/fio");
1916 gtk_file_filter_set_name(filter, "Fio job file");
1917 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1919 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1920 gtk_widget_destroy(dialog);
1924 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1926 gtk_widget_destroy(dialog);
1928 if (get_connection_details(&host, &port, &type, &server_start))
1931 filenames = fn_glist;
1932 while (filenames != NULL) {
1933 struct fio_client *client;
1935 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1936 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1939 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1943 error = g_error_new(g_quark_from_string("fio"), 1,
1944 "Failed to add client %s", host);
1945 report_error(error);
1946 g_error_free(error);
1948 gfio_client_added(ge, client);
1950 g_free(filenames->data);
1951 filenames = g_slist_next(filenames);
1956 gfio_start_server();
1958 g_slist_free(fn_glist);
1961 static void file_save(GtkWidget *w, gpointer data)
1963 struct gui *ui = data;
1966 dialog = gtk_file_chooser_dialog_new("Save File",
1967 GTK_WINDOW(ui->window),
1968 GTK_FILE_CHOOSER_ACTION_SAVE,
1969 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1970 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1973 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1974 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1976 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1979 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1980 // save_job_file(filename);
1983 gtk_widget_destroy(dialog);
1986 static void view_log_destroy(GtkWidget *w, gpointer data)
1988 struct gui *ui = (struct gui *) data;
1990 gtk_widget_ref(ui->log_tree);
1991 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1992 gtk_widget_destroy(w);
1993 ui->log_view = NULL;
1996 static void view_log(GtkWidget *w, gpointer data)
1998 GtkWidget *win, *scroll, *vbox, *box;
1999 struct gui *ui = (struct gui *) data;
2004 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2005 gtk_window_set_title(GTK_WINDOW(win), "Log");
2006 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2008 scroll = gtk_scrolled_window_new(NULL, NULL);
2010 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2012 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2014 box = gtk_hbox_new(TRUE, 0);
2015 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2016 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2017 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2019 vbox = gtk_vbox_new(TRUE, 5);
2020 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2022 gtk_container_add(GTK_CONTAINER(win), vbox);
2023 gtk_widget_show_all(win);
2026 static void __update_graph_limits(struct gfio_graphs *g)
2028 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2029 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2032 static void update_graph_limits(void)
2034 struct flist_head *entry;
2035 struct gui_entry *ge;
2037 __update_graph_limits(&main_ui.graphs);
2039 flist_for_each(entry, &main_ui.list) {
2040 ge = flist_entry(entry, struct gui_entry, list);
2041 __update_graph_limits(&ge->graphs);
2045 static void preferences(GtkWidget *w, gpointer data)
2047 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2048 GtkWidget *hbox, *spin, *entry, *spin_int;
2051 dialog = gtk_dialog_new_with_buttons("Preferences",
2052 GTK_WINDOW(main_ui.window),
2053 GTK_DIALOG_DESTROY_WITH_PARENT,
2054 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2055 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2058 frame = gtk_frame_new("Debug logging");
2059 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2061 vbox = gtk_vbox_new(FALSE, 6);
2062 gtk_container_add(GTK_CONTAINER(frame), vbox);
2064 box = gtk_hbox_new(FALSE, 6);
2065 gtk_container_add(GTK_CONTAINER(vbox), box);
2067 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2069 for (i = 0; i < FD_DEBUG_MAX; i++) {
2071 box = gtk_hbox_new(FALSE, 6);
2072 gtk_container_add(GTK_CONTAINER(vbox), box);
2076 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2077 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2078 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2081 frame = gtk_frame_new("Graphing");
2082 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2083 vbox = gtk_vbox_new(FALSE, 6);
2084 gtk_container_add(GTK_CONTAINER(frame), vbox);
2086 hbox = gtk_hbox_new(FALSE, 5);
2087 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2088 entry = gtk_label_new("Font face to use for graph labels");
2089 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2091 font = gtk_font_button_new();
2092 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2094 box = gtk_vbox_new(FALSE, 6);
2095 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2097 hbox = gtk_hbox_new(FALSE, 5);
2098 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2099 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2100 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2102 spin = create_spinbutton(hbox, 10, 1000000, 100);
2104 box = gtk_vbox_new(FALSE, 6);
2105 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2107 hbox = gtk_hbox_new(FALSE, 5);
2108 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2109 entry = gtk_label_new("Client ETA request interval (msec)");
2110 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2112 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2113 gtk_widget_show_all(dialog);
2115 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2116 gtk_widget_destroy(dialog);
2120 for (i = 0; i < FD_DEBUG_MAX; i++) {
2123 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2125 fio_debug |= (1UL << i);
2128 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2129 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2130 update_graph_limits();
2131 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2133 gtk_widget_destroy(dialog);
2136 static void about_dialog(GtkWidget *w, gpointer data)
2138 const char *authors[] = {
2139 "Jens Axboe <axboe@kernel.dk>",
2140 "Stephen Carmeron <stephenmcameron@gmail.com>",
2143 const char *license[] = {
2144 "Fio is free software; you can redistribute it and/or modify "
2145 "it under the terms of the GNU General Public License as published by "
2146 "the Free Software Foundation; either version 2 of the License, or "
2147 "(at your option) any later version.\n",
2148 "Fio is distributed in the hope that it will be useful, "
2149 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2150 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2151 "GNU General Public License for more details.\n",
2152 "You should have received a copy of the GNU General Public License "
2153 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2154 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2156 char *license_trans;
2158 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2159 license[2], "\n", NULL);
2161 gtk_show_about_dialog(NULL,
2162 "program-name", "gfio",
2163 "comments", "Gtk2 UI for fio",
2164 "license", license_trans,
2165 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2167 "version", fio_version_string,
2168 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2169 "logo-icon-name", "fio",
2171 "wrap-license", TRUE,
2174 g_free(license_trans);
2177 static GtkActionEntry menu_items[] = {
2178 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2179 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2180 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2181 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2182 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2183 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2184 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2185 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2186 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2187 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2189 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2191 static const gchar *ui_string = " \
2193 <menubar name=\"MainMenu\"> \
2194 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2195 <menuitem name=\"New\" action=\"NewFile\" /> \
2196 <separator name=\"Separator1\"/> \
2197 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2198 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2199 <separator name=\"Separator2\"/> \
2200 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2201 <separator name=\"Separator3\"/> \
2202 <menuitem name=\"Quit\" action=\"Quit\" /> \
2204 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2205 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2207 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2208 <menuitem name=\"About\" action=\"About\" /> \
2214 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2217 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2220 action_group = gtk_action_group_new("Menu");
2221 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2223 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2224 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2226 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2227 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2230 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2231 GtkWidget *vbox, GtkUIManager *ui_manager)
2233 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2236 static GtkWidget *new_client_page(struct gui_entry *ge)
2238 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2241 main_vbox = gtk_vbox_new(FALSE, 3);
2243 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2244 ge->topvbox = gtk_vbox_new(FALSE, 3);
2245 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2246 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2248 probe = gtk_frame_new("Job");
2249 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2250 probe_frame = gtk_vbox_new(FALSE, 3);
2251 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2253 probe_box = gtk_hbox_new(FALSE, 3);
2254 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2255 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2256 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2257 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2258 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2260 probe_box = gtk_hbox_new(FALSE, 3);
2261 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2263 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2264 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2265 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2266 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2267 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2268 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2270 probe_box = gtk_hbox_new(FALSE, 3);
2271 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2272 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2273 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2274 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2275 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2278 * Only add this if we have a commit rate
2281 probe_box = gtk_hbox_new(FALSE, 3);
2282 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2284 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2285 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2287 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2288 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2292 * Set up a drawing area and IOPS and bandwidth graphs
2294 gdk_color_parse("white", &white);
2295 ge->graphs.drawing_area = gtk_drawing_area_new();
2296 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2297 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2298 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2299 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2300 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2301 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2302 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2303 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2304 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2305 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2306 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2307 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2308 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2309 ge->graphs.drawing_area);
2310 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2313 setup_graphs(&ge->graphs);
2316 * Set up alignments for widgets at the bottom of ui,
2317 * align bottom left, expand horizontally but not vertically
2319 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2320 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2321 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2322 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2325 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2328 * Set up thread status progress bar
2330 ge->thread_status_pb = gtk_progress_bar_new();
2331 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2332 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2333 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2339 static GtkWidget *new_main_page(struct gui *ui)
2341 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2344 main_vbox = gtk_vbox_new(FALSE, 3);
2347 * Set up alignments for widgets at the top of ui,
2348 * align top left, expand horizontally but not vertically
2350 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2351 ui->topvbox = gtk_vbox_new(FALSE, 0);
2352 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2353 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2355 probe = gtk_frame_new("Run statistics");
2356 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2357 probe_frame = gtk_vbox_new(FALSE, 3);
2358 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2360 probe_box = gtk_hbox_new(FALSE, 3);
2361 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2362 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2363 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2364 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2365 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2366 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2369 * Only add this if we have a commit rate
2372 probe_box = gtk_hbox_new(FALSE, 3);
2373 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2375 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2376 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2378 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2379 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2383 * Set up a drawing area and IOPS and bandwidth graphs
2385 gdk_color_parse("white", &white);
2386 ui->graphs.drawing_area = gtk_drawing_area_new();
2387 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2388 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2389 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2390 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2391 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2392 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2393 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2394 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2395 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2396 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2397 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2398 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2399 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2400 ui->graphs.drawing_area);
2401 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2404 setup_graphs(&ui->graphs);
2407 * Set up alignments for widgets at the bottom of ui,
2408 * align bottom left, expand horizontally but not vertically
2410 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2411 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2412 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2413 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2417 * Set up thread status progress bar
2419 ui->thread_status_pb = gtk_progress_bar_new();
2420 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2421 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2422 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2427 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2428 guint page, gpointer data)
2434 static void init_ui(int *argc, char **argv[], struct gui *ui)
2436 GtkSettings *settings;
2437 GtkUIManager *uimanager;
2438 GtkWidget *menu, *vbox;
2440 /* Magical g*thread incantation, you just need this thread stuff.
2441 * Without it, the update that happens in gfio_update_thread_status
2442 * doesn't really happen in a timely fashion, you need expose events
2444 if (!g_thread_supported())
2445 g_thread_init(NULL);
2448 gtk_init(argc, argv);
2449 settings = gtk_settings_get_default();
2450 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2453 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2454 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2455 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2457 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2458 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2460 ui->vbox = gtk_vbox_new(FALSE, 0);
2461 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2463 uimanager = gtk_ui_manager_new();
2464 menu = get_menubar_menu(ui->window, uimanager, ui);
2465 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2467 ui->notebook = gtk_notebook_new();
2468 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2469 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2470 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2471 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2473 vbox = new_main_page(ui);
2475 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2477 gfio_ui_setup_log(ui);
2479 gtk_widget_show_all(ui->window);
2482 int main(int argc, char *argv[], char *envp[])
2484 if (initialize_fio(envp))
2486 if (fio_init_options())
2489 memset(&main_ui, 0, sizeof(main_ui));
2490 INIT_FLIST_HEAD(&main_ui.list);
2492 init_ui(&argc, &argv, &main_ui);
2494 gdk_threads_enter();
2496 gdk_threads_leave();