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 },
71 struct multitext_widget {
74 unsigned int cur_text;
75 unsigned int max_text;
80 struct multitext_widget iotype;
81 struct multitext_widget ioengine;
82 struct multitext_widget iodepth;
90 GtkWidget *write_iops;
96 #define DRAWING_AREA_XDIM 1000
97 #define DRAWING_AREA_YDIM 400
98 GtkWidget *drawing_area;
99 struct graph *iops_graph;
100 struct graph *bandwidth_graph;
104 * Main window widgets and data
111 GtkWidget *bottomalign;
112 GtkWidget *thread_status_pb;
113 GtkWidget *buttonbox;
114 GtkWidget *scrolled_window;
116 GtkWidget *error_info_bar;
117 GtkWidget *error_label;
118 GtkListStore *log_model;
121 struct gfio_graphs graphs;
122 struct probe_widget probe;
123 struct eta_widget eta;
129 struct flist_head list;
136 struct flist_head list;
142 GtkWidget *bottomalign;
143 GtkWidget *job_notebook;
144 GtkWidget *thread_status_pb;
145 GtkWidget *buttonbox;
146 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
147 GtkWidget *scrolled_window;
149 GtkWidget *error_info_bar;
150 GtkWidget *error_label;
151 GtkWidget *results_notebook;
152 GtkWidget *results_window;
153 GtkListStore *log_model;
156 struct gfio_graphs graphs;
157 struct probe_widget probe;
158 struct eta_widget eta;
159 GtkWidget *page_label;
163 struct gfio_client *client;
169 struct gui_entry *ge;
170 struct fio_client *client;
171 GtkWidget *results_widget;
172 GtkWidget *disk_util_frame;
173 GtkWidget *err_entry;
174 unsigned int job_added;
175 struct thread_options o;
178 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
179 static void gfio_update_thread_status_all(char *status_message, double perc);
180 void report_error(GError *error);
182 static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
184 switch (power_of_ten) {
185 case 9: graph_y_title(g, "Billions of IOs / sec");
187 case 6: graph_y_title(g, "Millions of IOs / sec");
189 case 3: graph_y_title(g, "Thousands of IOs / sec");
192 default: graph_y_title(g, "IOs / sec");
197 static struct graph *setup_iops_graph(void)
201 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
202 graph_title(g, "IOPS");
203 graph_x_title(g, "Time (secs)");
204 graph_y_title(g, "IOs / sec");
205 graph_add_label(g, "Read IOPS");
206 graph_add_label(g, "Write IOPS");
207 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
208 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
209 line_graph_set_data_count_limit(g, gfio_graph_limit);
210 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
211 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
215 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
217 switch (power_of_ten) {
218 case 9: graph_y_title(g, "Petabytes / sec");
220 case 6: graph_y_title(g, "Gigabytes / sec");
222 case 3: graph_y_title(g, "Megabytes / sec");
225 default: graph_y_title(g, "Kilobytes / sec");
230 static struct graph *setup_bandwidth_graph(void)
234 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
235 graph_title(g, "Bandwidth");
236 graph_x_title(g, "Time (secs)");
237 graph_y_title(g, "Kbytes / sec");
238 graph_add_label(g, "Read Bandwidth");
239 graph_add_label(g, "Write Bandwidth");
240 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
241 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
242 line_graph_set_data_count_limit(g, 100);
243 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
244 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
249 static void setup_graphs(struct gfio_graphs *g)
251 g->iops_graph = setup_iops_graph();
252 g->bandwidth_graph = setup_bandwidth_graph();
255 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
257 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
258 mt->text[mt->max_text] = strdup(text);
262 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
264 if (index >= mt->max_text)
266 if (!mt->text || !mt->text[index])
269 mt->cur_text = index;
270 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
273 static void multitext_update_entry(struct multitext_widget *mt,
274 unsigned int index, const char *text)
280 free(mt->text[index]);
282 mt->text[index] = strdup(text);
283 if (mt->cur_text == index)
284 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
287 static void multitext_free(struct multitext_widget *mt)
291 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
293 for (i = 0; i < mt->max_text; i++) {
303 static void clear_ge_ui_info(struct gui_entry *ge)
305 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
306 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
307 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
308 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
310 /* should we empty it... */
311 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
313 multitext_update_entry(&ge->eta.iotype, 0, "");
314 multitext_update_entry(&ge->eta.ioengine, 0, "");
315 multitext_update_entry(&ge->eta.iodepth, 0, "");
316 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
317 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
318 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
319 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
320 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
321 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
324 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
326 GtkWidget *entry, *frame;
328 frame = gtk_frame_new(label);
329 entry = gtk_combo_box_new_text();
330 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
331 gtk_container_add(GTK_CONTAINER(frame), entry);
336 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
338 GtkWidget *entry, *frame;
340 frame = gtk_frame_new(label);
341 entry = gtk_entry_new();
342 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
343 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
344 gtk_container_add(GTK_CONTAINER(frame), entry);
349 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
351 GtkWidget *label_widget;
354 frame = gtk_frame_new(label);
355 label_widget = gtk_label_new(NULL);
356 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
357 gtk_container_add(GTK_CONTAINER(frame), label_widget);
362 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
364 GtkWidget *button, *box;
366 box = gtk_hbox_new(FALSE, 3);
367 gtk_container_add(GTK_CONTAINER(hbox), box);
369 button = gtk_spin_button_new_with_range(min, max, 1.0);
370 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
372 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
373 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
378 static void gfio_set_connected(struct gui_entry *ge, int connected)
381 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
383 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
384 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
387 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
388 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
389 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
390 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
394 static void label_set_int_value(GtkWidget *entry, unsigned int val)
398 sprintf(tmp, "%u", val);
399 gtk_label_set_text(GTK_LABEL(entry), tmp);
402 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
406 sprintf(tmp, "%u", val);
407 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
411 #define ALIGN_RIGHT 2
415 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
417 GtkCellRenderer *renderer;
418 GtkTreeViewColumn *col;
419 double xalign = 0.0; /* left as default */
420 PangoAlignment align;
423 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
424 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
426 visible = !(flags & INVISIBLE);
428 renderer = gtk_cell_renderer_text_new();
429 col = gtk_tree_view_column_new();
431 gtk_tree_view_column_set_title(col, title);
432 if (!(flags & UNSORTABLE))
433 gtk_tree_view_column_set_sort_column_id(col, index);
434 gtk_tree_view_column_set_resizable(col, TRUE);
435 gtk_tree_view_column_pack_start(col, renderer, TRUE);
436 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
437 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
439 case PANGO_ALIGN_LEFT:
442 case PANGO_ALIGN_CENTER:
445 case PANGO_ALIGN_RIGHT:
449 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
450 gtk_tree_view_column_set_visible(col, visible);
451 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
455 static void gfio_ui_setup_log(struct gui *ui)
457 GtkTreeSelection *selection;
459 GtkWidget *tree_view;
461 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
463 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
464 gtk_widget_set_can_focus(tree_view, FALSE);
466 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
467 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
468 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
469 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
471 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
472 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
473 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
474 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
476 ui->log_model = model;
477 ui->log_tree = tree_view;
480 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
486 GType types[FIO_IO_U_LIST_MAX_LEN];
487 GtkWidget *tree_view;
488 GtkTreeSelection *selection;
493 for (i = 0; i < len; i++)
494 types[i] = G_TYPE_INT;
496 model = gtk_list_store_newv(len, types);
498 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
499 gtk_widget_set_can_focus(tree_view, FALSE);
501 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
502 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
504 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
505 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
507 for (i = 0; i < len; i++) {
510 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
511 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
514 gtk_list_store_append(model, &iter);
516 for (i = 0; i < len; i++) {
518 ovals[i] = (ovals[i] + 999) / 1000;
519 gtk_list_store_set(model, &iter, i, ovals[i], -1);
525 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
528 unsigned int *io_u_plat = ts->io_u_plat[ddir];
529 unsigned long nr = ts->clat_stat[ddir].samples;
530 fio_fp64_t *plist = ts->percentile_list;
531 unsigned int *ovals, len, minv, maxv, scale_down;
533 GtkWidget *tree_view, *frame, *hbox;
536 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
541 * We default to usecs, but if the value range is such that we
542 * should scale down to msecs, do that.
544 if (minv > 2000 && maxv > 99999) {
552 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
554 sprintf(tmp, "Completion percentiles (%s)", base);
555 frame = gtk_frame_new(tmp);
556 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
558 hbox = gtk_hbox_new(FALSE, 3);
559 gtk_container_add(GTK_CONTAINER(frame), hbox);
561 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
567 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
568 unsigned long max, double mean, double dev)
570 const char *base = "(usec)";
571 GtkWidget *hbox, *label, *frame;
575 if (!usec_to_msec(&min, &max, &mean, &dev))
578 minp = num2str(min, 6, 1, 0);
579 maxp = num2str(max, 6, 1, 0);
581 sprintf(tmp, "%s %s", name, base);
582 frame = gtk_frame_new(tmp);
583 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
585 hbox = gtk_hbox_new(FALSE, 3);
586 gtk_container_add(GTK_CONTAINER(frame), hbox);
588 label = new_info_label_in_frame(hbox, "Minimum");
589 gtk_label_set_text(GTK_LABEL(label), minp);
590 label = new_info_label_in_frame(hbox, "Maximum");
591 gtk_label_set_text(GTK_LABEL(label), maxp);
592 label = new_info_label_in_frame(hbox, "Average");
593 sprintf(tmp, "%5.02f", mean);
594 gtk_label_set_text(GTK_LABEL(label), tmp);
595 label = new_info_label_in_frame(hbox, "Standard deviation");
596 sprintf(tmp, "%5.02f", dev);
597 gtk_label_set_text(GTK_LABEL(label), tmp);
608 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
609 struct thread_stat *ts, int ddir)
611 const char *ddir_label[2] = { "Read", "Write" };
612 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
613 unsigned long min[3], max[3], runt;
614 unsigned long long bw, iops;
615 unsigned int flags = 0;
616 double mean[3], dev[3];
617 char *io_p, *bw_p, *iops_p;
620 if (!ts->runtime[ddir])
623 i2p = is_power_of_2(rs->kb_base);
624 runt = ts->runtime[ddir];
626 bw = (1000 * ts->io_bytes[ddir]) / runt;
627 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
628 bw_p = num2str(bw, 6, 1, i2p);
630 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
631 iops_p = num2str(iops, 6, 1, 0);
633 box = gtk_hbox_new(FALSE, 3);
634 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
636 frame = gtk_frame_new(ddir_label[ddir]);
637 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
639 main_vbox = gtk_vbox_new(FALSE, 3);
640 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
642 box = gtk_hbox_new(FALSE, 3);
643 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
645 label = new_info_label_in_frame(box, "IO");
646 gtk_label_set_text(GTK_LABEL(label), io_p);
647 label = new_info_label_in_frame(box, "Bandwidth");
648 gtk_label_set_text(GTK_LABEL(label), bw_p);
649 label = new_info_label_in_frame(box, "IOPS");
650 gtk_label_set_text(GTK_LABEL(label), iops_p);
651 label = new_info_label_in_frame(box, "Runtime (msec)");
652 label_set_int_value(label, ts->runtime[ddir]);
654 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
655 double p_of_agg = 100.0;
656 const char *bw_str = "KB";
660 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
661 if (p_of_agg > 100.0)
665 if (mean[0] > 999999.9) {
673 sprintf(tmp, "Bandwidth (%s)", bw_str);
674 frame = gtk_frame_new(tmp);
675 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
677 box = gtk_hbox_new(FALSE, 3);
678 gtk_container_add(GTK_CONTAINER(frame), box);
680 label = new_info_label_in_frame(box, "Minimum");
681 label_set_int_value(label, min[0]);
682 label = new_info_label_in_frame(box, "Maximum");
683 label_set_int_value(label, max[0]);
684 label = new_info_label_in_frame(box, "Percentage of jobs");
685 sprintf(tmp, "%3.2f%%", p_of_agg);
686 gtk_label_set_text(GTK_LABEL(label), tmp);
687 label = new_info_label_in_frame(box, "Average");
688 sprintf(tmp, "%5.02f", mean[0]);
689 gtk_label_set_text(GTK_LABEL(label), tmp);
690 label = new_info_label_in_frame(box, "Standard deviation");
691 sprintf(tmp, "%5.02f", dev[0]);
692 gtk_label_set_text(GTK_LABEL(label), tmp);
695 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
697 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
699 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
703 frame = gtk_frame_new("Latency");
704 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
706 vbox = gtk_vbox_new(FALSE, 3);
707 gtk_container_add(GTK_CONTAINER(frame), vbox);
709 if (flags & GFIO_SLAT)
710 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
711 if (flags & GFIO_CLAT)
712 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
713 if (flags & GFIO_LAT)
714 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
717 if (ts->clat_percentiles)
718 gfio_show_clat_percentiles(main_vbox, ts, ddir);
726 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
729 GtkWidget *tree_view;
730 GtkTreeSelection *selection;
737 * Check if all are empty, in which case don't bother
739 for (i = 0, skipped = 0; i < num; i++)
746 types = malloc(num * sizeof(GType));
748 for (i = 0; i < num; i++)
749 types[i] = G_TYPE_STRING;
751 model = gtk_list_store_newv(num, types);
755 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
756 gtk_widget_set_can_focus(tree_view, FALSE);
758 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
759 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
761 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
762 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
764 for (i = 0; i < num; i++)
765 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
767 gtk_list_store_append(model, &iter);
769 for (i = 0; i < num; i++) {
773 sprintf(fbuf, "0.00");
775 sprintf(fbuf, "%3.2f%%", lat[i]);
777 gtk_list_store_set(model, &iter, i, fbuf, -1);
783 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
785 GtkWidget *box, *frame, *tree_view;
786 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
787 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
788 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
789 "250", "500", "750", "1000", };
790 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
791 "250", "500", "750", "1000", "2000",
794 stat_calc_lat_u(ts, io_u_lat_u);
795 stat_calc_lat_m(ts, io_u_lat_m);
797 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
799 frame = gtk_frame_new("Latency buckets (usec)");
800 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
802 box = gtk_hbox_new(FALSE, 3);
803 gtk_container_add(GTK_CONTAINER(frame), box);
804 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
807 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
809 frame = gtk_frame_new("Latency buckets (msec)");
810 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
812 box = gtk_hbox_new(FALSE, 3);
813 gtk_container_add(GTK_CONTAINER(frame), box);
814 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
818 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
820 GtkWidget *box, *frame, *entry;
821 double usr_cpu, sys_cpu;
822 unsigned long runtime;
825 runtime = ts->total_run_time;
827 double runt = (double) runtime;
829 usr_cpu = (double) ts->usr_time * 100 / runt;
830 sys_cpu = (double) ts->sys_time * 100 / runt;
836 frame = gtk_frame_new("OS resources");
837 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
839 box = gtk_hbox_new(FALSE, 3);
840 gtk_container_add(GTK_CONTAINER(frame), box);
842 entry = new_info_entry_in_frame(box, "User CPU");
843 sprintf(tmp, "%3.2f%%", usr_cpu);
844 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
845 entry = new_info_entry_in_frame(box, "System CPU");
846 sprintf(tmp, "%3.2f%%", sys_cpu);
847 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
848 entry = new_info_entry_in_frame(box, "Context switches");
849 entry_set_int_value(entry, ts->ctx);
850 entry = new_info_entry_in_frame(box, "Major faults");
851 entry_set_int_value(entry, ts->majf);
852 entry = new_info_entry_in_frame(box, "Minor faults");
853 entry_set_int_value(entry, ts->minf);
855 static void gfio_add_sc_depths_tree(GtkListStore *model,
856 struct thread_stat *ts, unsigned int len,
859 double io_u_dist[FIO_IO_U_MAP_NR];
861 /* Bits 0, and 3-8 */
862 const int add_mask = 0x1f9;
866 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
868 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
870 gtk_list_store_append(model, &iter);
872 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
874 for (i = 1, j = 0; i < len; i++) {
877 if (!(add_mask & (1UL << (i - 1))))
878 sprintf(fbuf, "0.0%%");
880 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
884 gtk_list_store_set(model, &iter, i, fbuf, -1);
889 static void gfio_add_total_depths_tree(GtkListStore *model,
890 struct thread_stat *ts, unsigned int len)
892 double io_u_dist[FIO_IO_U_MAP_NR];
894 /* Bits 1-6, and 8 */
895 const int add_mask = 0x17e;
898 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
900 gtk_list_store_append(model, &iter);
902 gtk_list_store_set(model, &iter, 0, "Total", -1);
904 for (i = 1, j = 0; i < len; i++) {
907 if (!(add_mask & (1UL << (i - 1))))
908 sprintf(fbuf, "0.0%%");
910 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
914 gtk_list_store_set(model, &iter, i, fbuf, -1);
919 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
921 GtkWidget *frame, *box, *tree_view;
922 GtkTreeSelection *selection;
924 GType types[FIO_IO_U_MAP_NR + 1];
927 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
929 frame = gtk_frame_new("IO depths");
930 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
932 box = gtk_hbox_new(FALSE, 3);
933 gtk_container_add(GTK_CONTAINER(frame), box);
935 for (i = 0; i < NR_LABELS; i++)
936 types[i] = G_TYPE_STRING;
938 model = gtk_list_store_newv(NR_LABELS, types);
940 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
941 gtk_widget_set_can_focus(tree_view, FALSE);
943 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
944 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
946 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
947 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
949 for (i = 0; i < NR_LABELS; i++)
950 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
952 gfio_add_total_depths_tree(model, ts, NR_LABELS);
953 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
954 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
956 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
959 static gboolean results_window_delete(GtkWidget *w, gpointer data)
961 struct gui_entry *ge = (struct gui_entry *) data;
963 gtk_widget_destroy(w);
964 ge->results_window = NULL;
965 ge->results_notebook = NULL;
969 static GtkWidget *get_results_window(struct gui_entry *ge)
971 GtkWidget *win, *notebook;
973 if (ge->results_window)
974 return ge->results_notebook;
976 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
977 gtk_window_set_title(GTK_WINDOW(win), "Results");
978 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
979 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
980 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
982 notebook = gtk_notebook_new();
983 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
984 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
985 gtk_container_add(GTK_CONTAINER(win), notebook);
987 ge->results_window = win;
988 ge->results_notebook = notebook;
989 return ge->results_notebook;
992 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
993 struct group_run_stats *rs)
995 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
996 struct gfio_client *gc = client->client_data;
1000 res_win = get_results_window(gc->ge);
1002 scroll = gtk_scrolled_window_new(NULL, NULL);
1003 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1004 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1006 vbox = gtk_vbox_new(FALSE, 3);
1008 box = gtk_hbox_new(FALSE, 0);
1009 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1011 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1013 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1015 gc->results_widget = vbox;
1017 entry = new_info_entry_in_frame(box, "Name");
1018 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1019 if (strlen(ts->description)) {
1020 entry = new_info_entry_in_frame(box, "Description");
1021 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1023 entry = new_info_entry_in_frame(box, "Group ID");
1024 entry_set_int_value(entry, ts->groupid);
1025 entry = new_info_entry_in_frame(box, "Jobs");
1026 entry_set_int_value(entry, ts->members);
1027 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1028 entry_set_int_value(entry, ts->error);
1029 entry = new_info_entry_in_frame(box, "PID");
1030 entry_set_int_value(entry, ts->pid);
1032 if (ts->io_bytes[DDIR_READ])
1033 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1034 if (ts->io_bytes[DDIR_WRITE])
1035 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1037 gfio_show_latency_buckets(vbox, ts);
1038 gfio_show_cpu_usage(vbox, ts);
1039 gfio_show_io_depths(vbox, ts);
1041 gtk_widget_show_all(gc->ge->results_window);
1042 gdk_threads_leave();
1045 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1047 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1048 struct gui *ui = &main_ui;
1052 char tmp[64], timebuf[80];
1055 tm = localtime(&sec);
1056 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1057 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1059 gdk_threads_enter();
1061 gtk_list_store_append(ui->log_model, &iter);
1062 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1063 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1064 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1065 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1067 if (p->level == FIO_LOG_ERR)
1068 view_log(NULL, (gpointer) ui);
1070 gdk_threads_leave();
1073 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1075 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1076 struct gfio_client *gc = client->client_data;
1077 GtkWidget *box, *frame, *entry, *vbox;
1081 gdk_threads_enter();
1083 if (!gc->results_widget)
1086 if (!gc->disk_util_frame) {
1087 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1088 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1091 vbox = gtk_vbox_new(FALSE, 3);
1092 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1094 frame = gtk_frame_new((char *) p->dus.name);
1095 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1097 box = gtk_vbox_new(FALSE, 3);
1098 gtk_container_add(GTK_CONTAINER(frame), box);
1100 frame = gtk_frame_new("Read");
1101 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1102 vbox = gtk_hbox_new(TRUE, 3);
1103 gtk_container_add(GTK_CONTAINER(frame), vbox);
1104 entry = new_info_entry_in_frame(vbox, "IOs");
1105 entry_set_int_value(entry, p->dus.ios[0]);
1106 entry = new_info_entry_in_frame(vbox, "Merges");
1107 entry_set_int_value(entry, p->dus.merges[0]);
1108 entry = new_info_entry_in_frame(vbox, "Sectors");
1109 entry_set_int_value(entry, p->dus.sectors[0]);
1110 entry = new_info_entry_in_frame(vbox, "Ticks");
1111 entry_set_int_value(entry, p->dus.ticks[0]);
1113 frame = gtk_frame_new("Write");
1114 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1115 vbox = gtk_hbox_new(TRUE, 3);
1116 gtk_container_add(GTK_CONTAINER(frame), vbox);
1117 entry = new_info_entry_in_frame(vbox, "IOs");
1118 entry_set_int_value(entry, p->dus.ios[1]);
1119 entry = new_info_entry_in_frame(vbox, "Merges");
1120 entry_set_int_value(entry, p->dus.merges[1]);
1121 entry = new_info_entry_in_frame(vbox, "Sectors");
1122 entry_set_int_value(entry, p->dus.sectors[1]);
1123 entry = new_info_entry_in_frame(vbox, "Ticks");
1124 entry_set_int_value(entry, p->dus.ticks[1]);
1126 frame = gtk_frame_new("Shared");
1127 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1128 vbox = gtk_hbox_new(TRUE, 3);
1129 gtk_container_add(GTK_CONTAINER(frame), vbox);
1130 entry = new_info_entry_in_frame(vbox, "IO ticks");
1131 entry_set_int_value(entry, p->dus.io_ticks);
1132 entry = new_info_entry_in_frame(vbox, "Time in queue");
1133 entry_set_int_value(entry, p->dus.time_in_queue);
1137 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1141 sprintf(tmp, "%3.2f%%", util);
1142 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1143 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1145 gtk_widget_show_all(gc->results_widget);
1147 gdk_threads_leave();
1150 extern int sum_stat_clients;
1151 extern struct thread_stat client_ts;
1152 extern struct group_run_stats client_gs;
1154 static int sum_stat_nr;
1156 static void gfio_thread_status_op(struct fio_client *client,
1157 struct fio_net_cmd *cmd)
1159 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1161 gfio_display_ts(client, &p->ts, &p->rs);
1163 if (sum_stat_clients == 1)
1166 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1167 sum_group_stats(&client_gs, &p->rs);
1169 client_ts.members++;
1170 client_ts.groupid = p->ts.groupid;
1172 if (++sum_stat_nr == sum_stat_clients) {
1173 strcpy(client_ts.name, "All clients");
1174 gfio_display_ts(client, &client_ts, &client_gs);
1178 static void gfio_group_stats_op(struct fio_client *client,
1179 struct fio_net_cmd *cmd)
1181 /* We're ignoring group stats for now */
1184 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1187 struct gfio_graphs *g = data;
1189 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1190 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1191 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1192 graph_set_position(g->bandwidth_graph, 0, 0);
1196 static void draw_graph(struct graph *g, cairo_t *cr)
1198 line_graph_draw(g, cr);
1202 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1204 struct gfio_graphs *g = p;
1207 cr = gdk_cairo_create(w->window);
1208 cairo_set_source_rgb(cr, 0, 0, 0);
1209 draw_graph(g->iops_graph, cr);
1210 draw_graph(g->bandwidth_graph, cr);
1217 * Client specific ETA
1219 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1221 struct gfio_client *gc = client->client_data;
1222 struct gui_entry *ge = gc->ge;
1223 static int eta_good;
1230 gdk_threads_enter();
1235 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1236 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1237 eta_to_str(eta_str, je->eta_sec);
1240 sprintf(tmp, "%u", je->nr_running);
1241 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1242 sprintf(tmp, "%u", je->files_open);
1243 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1246 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1247 if (je->m_rate || je->t_rate) {
1250 mr = num2str(je->m_rate, 4, 0, i2p);
1251 tr = num2str(je->t_rate, 4, 0, i2p);
1252 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1253 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1256 } else if (je->m_iops || je->t_iops)
1257 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1259 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1260 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1261 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1262 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1265 if (je->eta_sec != INT_MAX && je->nr_running) {
1269 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1270 strcpy(output, "-.-% done");
1274 sprintf(output, "%3.1f%% done", perc);
1277 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1278 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1280 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1281 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1283 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1284 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1285 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1286 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1288 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1289 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1290 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1291 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1300 char *dst = output + strlen(output);
1302 sprintf(dst, " - %s", eta_str);
1305 gfio_update_thread_status(ge, output, perc);
1306 gdk_threads_leave();
1310 * Update ETA in main window for all clients
1312 static void gfio_update_all_eta(struct jobs_eta *je)
1314 struct gui *ui = &main_ui;
1315 static int eta_good;
1321 gdk_threads_enter();
1326 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1327 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1328 eta_to_str(eta_str, je->eta_sec);
1332 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1333 if (je->m_rate || je->t_rate) {
1336 mr = num2str(je->m_rate, 4, 0, i2p);
1337 tr = num2str(je->t_rate, 4, 0, i2p);
1338 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1339 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1342 } else if (je->m_iops || je->t_iops)
1343 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1345 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1346 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1347 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1348 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1351 entry_set_int_value(ui->eta.jobs, je->nr_running);
1353 if (je->eta_sec != INT_MAX && je->nr_running) {
1357 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1358 strcpy(output, "-.-% done");
1362 sprintf(output, "%3.1f%% done", perc);
1365 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1366 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1368 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1369 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1371 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1372 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1373 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1374 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1376 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1377 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1378 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1379 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1388 char *dst = output + strlen(output);
1390 sprintf(dst, " - %s", eta_str);
1393 gfio_update_thread_status_all(output, perc);
1394 gdk_threads_leave();
1397 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1399 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1400 struct gfio_client *gc = client->client_data;
1401 struct gui_entry *ge = gc->ge;
1402 const char *os, *arch;
1405 os = fio_get_os_string(probe->os);
1409 arch = fio_get_arch_string(probe->arch);
1414 client->name = strdup((char *) probe->hostname);
1416 gdk_threads_enter();
1418 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1419 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1420 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1421 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1422 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1424 gfio_set_connected(ge, 1);
1426 gdk_threads_leave();
1429 static void gfio_update_thread_status(struct gui_entry *ge,
1430 char *status_message, double perc)
1432 static char message[100];
1433 const char *m = message;
1435 strncpy(message, status_message, sizeof(message) - 1);
1436 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1437 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1438 gtk_widget_queue_draw(main_ui.window);
1441 static void gfio_update_thread_status_all(char *status_message, double perc)
1443 struct gui *ui = &main_ui;
1444 static char message[100];
1445 const char *m = message;
1447 strncpy(message, status_message, sizeof(message) - 1);
1448 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1449 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1450 gtk_widget_queue_draw(ui->window);
1453 static void gfio_quit_op(struct fio_client *client)
1455 struct gfio_client *gc = client->client_data;
1457 gdk_threads_enter();
1458 gfio_set_connected(gc->ge, 0);
1459 gdk_threads_leave();
1462 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1464 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1465 struct gfio_client *gc = client->client_data;
1466 struct thread_options *o = &gc->o;
1467 struct gui_entry *ge = gc->ge;
1470 convert_thread_options_to_cpu(o, &p->top);
1472 gdk_threads_enter();
1474 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1476 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1477 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1479 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1480 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1482 sprintf(tmp, "%u", o->iodepth);
1483 multitext_add_entry(&ge->eta.iodepth, tmp);
1485 multitext_set_entry(&ge->eta.iotype, 0);
1486 multitext_set_entry(&ge->eta.ioengine, 0);
1487 multitext_set_entry(&ge->eta.iodepth, 0);
1491 gdk_threads_leave();
1494 static void gfio_client_timed_out(struct fio_client *client)
1496 struct gfio_client *gc = client->client_data;
1497 GtkWidget *dialog, *label, *content;
1500 gdk_threads_enter();
1502 gfio_set_connected(gc->ge, 0);
1503 clear_ge_ui_info(gc->ge);
1505 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1507 dialog = gtk_dialog_new_with_buttons("Timed out!",
1508 GTK_WINDOW(main_ui.window),
1509 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1510 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1512 /* gtk_dialog_get_content_area() is 2.14 and newer */
1513 content = GTK_DIALOG(dialog)->vbox;
1515 label = gtk_label_new((const gchar *) buf);
1516 gtk_container_add(GTK_CONTAINER(content), label);
1517 gtk_widget_show_all(dialog);
1518 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1520 gtk_dialog_run(GTK_DIALOG(dialog));
1521 gtk_widget_destroy(dialog);
1523 gdk_threads_leave();
1526 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1528 struct gfio_client *gc = client->client_data;
1530 gdk_threads_enter();
1532 gfio_set_connected(gc->ge, 0);
1535 entry_set_int_value(gc->err_entry, client->error);
1537 gdk_threads_leave();
1540 struct client_ops gfio_client_ops = {
1541 .text_op = gfio_text_op,
1542 .disk_util = gfio_disk_util_op,
1543 .thread_status = gfio_thread_status_op,
1544 .group_stats = gfio_group_stats_op,
1545 .jobs_eta = gfio_update_client_eta,
1546 .eta = gfio_update_all_eta,
1547 .probe = gfio_probe_op,
1548 .quit = gfio_quit_op,
1549 .add_job = gfio_add_job_op,
1550 .timed_out = gfio_client_timed_out,
1551 .stop = gfio_client_stop,
1552 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1553 .stay_connected = 1,
1556 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1557 __attribute__((unused)) gpointer data)
1562 static void *job_thread(void *arg)
1564 struct gui *ui = arg;
1566 ui->handler_running = 1;
1567 fio_handle_clients(&gfio_client_ops);
1568 ui->handler_running = 0;
1572 static int send_job_files(struct gui_entry *ge)
1574 struct gfio_client *gc = ge->client;
1577 for (i = 0; i < ge->nr_job_files; i++) {
1578 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1582 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1583 report_error(error);
1584 g_error_free(error);
1589 free(ge->job_files[i]);
1590 ge->job_files[i] = NULL;
1592 while (i < ge->nr_job_files) {
1593 free(ge->job_files[i]);
1594 ge->job_files[i] = NULL;
1601 static void *server_thread(void *arg)
1604 gfio_server_running = 1;
1605 fio_start_server(NULL);
1606 gfio_server_running = 0;
1610 static void gfio_start_server(void)
1612 struct gui *ui = &main_ui;
1614 if (!gfio_server_running) {
1615 gfio_server_running = 1;
1616 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1617 pthread_detach(ui->server_t);
1621 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1624 struct gui_entry *ge = data;
1625 struct gfio_client *gc = ge->client;
1627 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1628 fio_start_client(gc->client);
1631 static void file_open(GtkWidget *w, gpointer data);
1633 static void connect_clicked(GtkWidget *widget, gpointer data)
1635 struct gui_entry *ge = data;
1636 struct gfio_client *gc = ge->client;
1638 if (!ge->connected) {
1641 if (!ge->nr_job_files)
1642 file_open(widget, data);
1643 if (!ge->nr_job_files)
1646 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1647 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1648 ret = fio_client_connect(gc->client);
1650 if (!ge->ui->handler_running)
1651 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1652 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1653 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1657 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1658 report_error(error);
1659 g_error_free(error);
1662 fio_client_terminate(gc->client);
1663 gfio_set_connected(ge, 0);
1664 clear_ge_ui_info(ge);
1668 static void send_clicked(GtkWidget *widget, gpointer data)
1670 struct gui_entry *ge = data;
1672 if (send_job_files(ge)) {
1675 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);
1676 report_error(error);
1677 g_error_free(error);
1679 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1682 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1683 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1686 static GtkWidget *add_button(GtkWidget *buttonbox,
1687 struct button_spec *buttonspec, gpointer data)
1689 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1691 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1692 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1693 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1694 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1699 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1704 for (i = 0; i < nbuttons; i++)
1705 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1708 static void on_info_bar_response(GtkWidget *widget, gint response,
1711 struct gui *ui = &main_ui;
1713 if (response == GTK_RESPONSE_OK) {
1714 gtk_widget_destroy(widget);
1715 ui->error_info_bar = NULL;
1719 void report_error(GError *error)
1721 struct gui *ui = &main_ui;
1723 if (ui->error_info_bar == NULL) {
1724 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1727 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1728 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1731 ui->error_label = gtk_label_new(error->message);
1732 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1733 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1735 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1736 gtk_widget_show_all(ui->vbox);
1739 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1740 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1744 struct connection_widgets
1751 static void hostname_cb(GtkEntry *entry, gpointer data)
1753 struct connection_widgets *cw = data;
1754 int uses_net = 0, is_localhost = 0;
1759 * Check whether to display the 'auto start backend' box
1760 * or not. Show it if we are a localhost and using network,
1761 * or using a socket.
1763 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1764 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1769 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1770 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1771 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1772 !strcmp(text, "ip6-loopback"))
1776 if (!uses_net || is_localhost) {
1777 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1778 gtk_widget_set_sensitive(cw->button, 1);
1780 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1781 gtk_widget_set_sensitive(cw->button, 0);
1785 static int get_connection_details(char **host, int *port, int *type,
1788 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1789 struct connection_widgets cw;
1792 dialog = gtk_dialog_new_with_buttons("Connection details",
1793 GTK_WINDOW(main_ui.window),
1794 GTK_DIALOG_DESTROY_WITH_PARENT,
1795 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1796 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1798 frame = gtk_frame_new("Hostname / socket name");
1799 /* gtk_dialog_get_content_area() is 2.14 and newer */
1800 vbox = GTK_DIALOG(dialog)->vbox;
1801 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1803 box = gtk_vbox_new(FALSE, 6);
1804 gtk_container_add(GTK_CONTAINER(frame), box);
1806 hbox = gtk_hbox_new(TRUE, 10);
1807 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1808 cw.hentry = gtk_entry_new();
1809 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1810 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1812 frame = gtk_frame_new("Port");
1813 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1814 box = gtk_vbox_new(FALSE, 10);
1815 gtk_container_add(GTK_CONTAINER(frame), box);
1817 hbox = gtk_hbox_new(TRUE, 4);
1818 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1819 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1821 frame = gtk_frame_new("Type");
1822 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1823 box = gtk_vbox_new(FALSE, 10);
1824 gtk_container_add(GTK_CONTAINER(frame), box);
1826 hbox = gtk_hbox_new(TRUE, 4);
1827 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1829 cw.combo = gtk_combo_box_new_text();
1830 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1831 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1832 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1833 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1835 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1837 frame = gtk_frame_new("Options");
1838 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1839 box = gtk_vbox_new(FALSE, 10);
1840 gtk_container_add(GTK_CONTAINER(frame), box);
1842 hbox = gtk_hbox_new(TRUE, 4);
1843 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1845 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1846 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1847 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.");
1848 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1851 * Connect edit signal, so we can show/not-show the auto start button
1853 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1854 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1856 gtk_widget_show_all(dialog);
1858 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1859 gtk_widget_destroy(dialog);
1863 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1864 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1866 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1867 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1868 *type = Fio_client_ipv4;
1869 else if (!strncmp(typeentry, "IPv6", 4))
1870 *type = Fio_client_ipv6;
1872 *type = Fio_client_socket;
1875 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1877 gtk_widget_destroy(dialog);
1881 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1883 struct gfio_client *gc;
1885 gc = malloc(sizeof(*gc));
1886 memset(gc, 0, sizeof(*gc));
1888 gc->client = fio_get_client(client);
1892 client->client_data = gc;
1895 static GtkWidget *new_client_page(struct gui_entry *ge);
1897 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1899 struct gui_entry *ge;
1901 ge = malloc(sizeof(*ge));
1902 memset(ge, 0, sizeof(*ge));
1903 INIT_FLIST_HEAD(&ge->list);
1904 flist_add_tail(&ge->list, &ui->list);
1910 * FIXME: need more handling here
1912 static void ge_destroy(GtkWidget *w, gpointer data)
1914 struct gui_entry *ge = data;
1915 struct gfio_client *gc = ge->client;
1918 fio_put_client(gc->client);
1920 flist_del(&ge->list);
1924 static struct gui_entry *get_new_ge_with_tab(const char *name)
1926 struct gui_entry *ge;
1928 ge = alloc_new_gui_entry(&main_ui);
1930 ge->vbox = new_client_page(ge);
1931 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1933 ge->page_label = gtk_label_new(name);
1934 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1936 gtk_widget_show_all(main_ui.window);
1940 static void file_new(GtkWidget *w, gpointer data)
1942 get_new_ge_with_tab("Untitled");
1946 * Return the 'ge' corresponding to the tab. If the active tab is the
1947 * main tab, open a new tab.
1949 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1951 struct flist_head *entry;
1952 struct gui_entry *ge;
1955 return get_new_ge_with_tab("Untitled");
1957 flist_for_each(entry, &main_ui.list) {
1958 ge = flist_entry(entry, struct gui_entry, list);
1959 if (ge->page_num == cur_page)
1966 static void file_open(GtkWidget *w, gpointer data)
1968 struct gui *ui = data;
1970 GSList *filenames, *fn_glist;
1971 GtkFileFilter *filter;
1973 int port, type, server_start;
1974 struct gui_entry *ge;
1978 * Creates new tab if current tab is the main window, or the
1979 * current tab already has a client.
1981 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1982 ge = get_ge_from_page(cur_page);
1984 ge = get_new_ge_with_tab("Untitled");
1986 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1988 dialog = gtk_file_chooser_dialog_new("Open File",
1989 GTK_WINDOW(ui->window),
1990 GTK_FILE_CHOOSER_ACTION_OPEN,
1991 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1992 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1994 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1996 filter = gtk_file_filter_new();
1997 gtk_file_filter_add_pattern(filter, "*.fio");
1998 gtk_file_filter_add_pattern(filter, "*.job");
1999 gtk_file_filter_add_pattern(filter, "*.ini");
2000 gtk_file_filter_add_mime_type(filter, "text/fio");
2001 gtk_file_filter_set_name(filter, "Fio job file");
2002 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2004 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2005 gtk_widget_destroy(dialog);
2009 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2011 gtk_widget_destroy(dialog);
2013 if (get_connection_details(&host, &port, &type, &server_start))
2016 filenames = fn_glist;
2017 while (filenames != NULL) {
2018 struct fio_client *client;
2020 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2021 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2024 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2028 error = g_error_new(g_quark_from_string("fio"), 1,
2029 "Failed to add client %s", host);
2030 report_error(error);
2031 g_error_free(error);
2033 gfio_client_added(ge, client);
2035 g_free(filenames->data);
2036 filenames = g_slist_next(filenames);
2041 gfio_start_server();
2043 g_slist_free(fn_glist);
2046 static void file_save(GtkWidget *w, gpointer data)
2048 struct gui *ui = data;
2051 dialog = gtk_file_chooser_dialog_new("Save File",
2052 GTK_WINDOW(ui->window),
2053 GTK_FILE_CHOOSER_ACTION_SAVE,
2054 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2055 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2058 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2059 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2061 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2064 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2065 // save_job_file(filename);
2068 gtk_widget_destroy(dialog);
2071 static void view_log_destroy(GtkWidget *w, gpointer data)
2073 struct gui *ui = (struct gui *) data;
2075 gtk_widget_ref(ui->log_tree);
2076 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2077 gtk_widget_destroy(w);
2078 ui->log_view = NULL;
2081 static void view_log(GtkWidget *w, gpointer data)
2083 GtkWidget *win, *scroll, *vbox, *box;
2084 struct gui *ui = (struct gui *) data;
2089 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2090 gtk_window_set_title(GTK_WINDOW(win), "Log");
2091 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2093 scroll = gtk_scrolled_window_new(NULL, NULL);
2095 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2097 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2099 box = gtk_hbox_new(TRUE, 0);
2100 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2101 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2102 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2104 vbox = gtk_vbox_new(TRUE, 5);
2105 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2107 gtk_container_add(GTK_CONTAINER(win), vbox);
2108 gtk_widget_show_all(win);
2111 static void __update_graph_limits(struct gfio_graphs *g)
2113 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2114 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2117 static void update_graph_limits(void)
2119 struct flist_head *entry;
2120 struct gui_entry *ge;
2122 __update_graph_limits(&main_ui.graphs);
2124 flist_for_each(entry, &main_ui.list) {
2125 ge = flist_entry(entry, struct gui_entry, list);
2126 __update_graph_limits(&ge->graphs);
2130 static void preferences(GtkWidget *w, gpointer data)
2132 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2133 GtkWidget *hbox, *spin, *entry, *spin_int;
2136 dialog = gtk_dialog_new_with_buttons("Preferences",
2137 GTK_WINDOW(main_ui.window),
2138 GTK_DIALOG_DESTROY_WITH_PARENT,
2139 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2140 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2143 frame = gtk_frame_new("Graphing");
2144 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2145 vbox = gtk_vbox_new(FALSE, 6);
2146 gtk_container_add(GTK_CONTAINER(frame), vbox);
2148 hbox = gtk_hbox_new(FALSE, 5);
2149 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2150 entry = gtk_label_new("Font face to use for graph labels");
2151 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2153 font = gtk_font_button_new();
2154 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2156 box = gtk_vbox_new(FALSE, 6);
2157 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2159 hbox = gtk_hbox_new(FALSE, 5);
2160 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2161 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2162 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2164 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2166 box = gtk_vbox_new(FALSE, 6);
2167 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2169 hbox = gtk_hbox_new(FALSE, 5);
2170 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2171 entry = gtk_label_new("Client ETA request interval (msec)");
2172 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2174 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2175 frame = gtk_frame_new("Debug logging");
2176 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2177 vbox = gtk_vbox_new(FALSE, 6);
2178 gtk_container_add(GTK_CONTAINER(frame), vbox);
2180 box = gtk_hbox_new(FALSE, 6);
2181 gtk_container_add(GTK_CONTAINER(vbox), box);
2183 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2185 for (i = 0; i < FD_DEBUG_MAX; i++) {
2187 box = gtk_hbox_new(FALSE, 6);
2188 gtk_container_add(GTK_CONTAINER(vbox), box);
2192 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2193 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2194 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2197 gtk_widget_show_all(dialog);
2199 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2200 gtk_widget_destroy(dialog);
2204 for (i = 0; i < FD_DEBUG_MAX; i++) {
2207 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2209 fio_debug |= (1UL << i);
2212 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2213 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2214 update_graph_limits();
2215 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2217 gtk_widget_destroy(dialog);
2220 static void about_dialog(GtkWidget *w, gpointer data)
2222 const char *authors[] = {
2223 "Jens Axboe <axboe@kernel.dk>",
2224 "Stephen Carmeron <stephenmcameron@gmail.com>",
2227 const char *license[] = {
2228 "Fio is free software; you can redistribute it and/or modify "
2229 "it under the terms of the GNU General Public License as published by "
2230 "the Free Software Foundation; either version 2 of the License, or "
2231 "(at your option) any later version.\n",
2232 "Fio is distributed in the hope that it will be useful, "
2233 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2234 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2235 "GNU General Public License for more details.\n",
2236 "You should have received a copy of the GNU General Public License "
2237 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2238 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2240 char *license_trans;
2242 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2243 license[2], "\n", NULL);
2245 gtk_show_about_dialog(NULL,
2246 "program-name", "gfio",
2247 "comments", "Gtk2 UI for fio",
2248 "license", license_trans,
2249 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2251 "version", fio_version_string,
2252 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2253 "logo-icon-name", "fio",
2255 "wrap-license", TRUE,
2258 g_free(license_trans);
2261 static GtkActionEntry menu_items[] = {
2262 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2263 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2264 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2265 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2266 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2267 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2268 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2269 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2270 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2271 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2273 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2275 static const gchar *ui_string = " \
2277 <menubar name=\"MainMenu\"> \
2278 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2279 <menuitem name=\"New\" action=\"NewFile\" /> \
2280 <separator name=\"Separator1\"/> \
2281 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2282 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2283 <separator name=\"Separator2\"/> \
2284 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2285 <separator name=\"Separator3\"/> \
2286 <menuitem name=\"Quit\" action=\"Quit\" /> \
2288 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2289 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2291 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2292 <menuitem name=\"About\" action=\"About\" /> \
2298 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2301 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2304 action_group = gtk_action_group_new("Menu");
2305 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2307 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2308 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2310 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2311 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2314 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2315 GtkWidget *vbox, GtkUIManager *ui_manager)
2317 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2320 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2322 struct gui_entry *ge = (struct gui_entry *) data;
2325 index = gtk_combo_box_get_active(box);
2327 multitext_set_entry(&ge->eta.iotype, index);
2328 multitext_set_entry(&ge->eta.ioengine, index);
2329 multitext_set_entry(&ge->eta.iodepth, index);
2332 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2334 struct gui_entry *ge = (struct gui_entry *) data;
2336 multitext_free(&ge->eta.iotype);
2337 multitext_free(&ge->eta.ioengine);
2338 multitext_free(&ge->eta.iodepth);
2341 static GtkWidget *new_client_page(struct gui_entry *ge)
2343 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2346 main_vbox = gtk_vbox_new(FALSE, 3);
2348 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2349 ge->topvbox = gtk_vbox_new(FALSE, 3);
2350 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2351 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2353 probe = gtk_frame_new("Job");
2354 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2355 probe_frame = gtk_vbox_new(FALSE, 3);
2356 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2358 probe_box = gtk_hbox_new(FALSE, 3);
2359 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2360 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2361 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2362 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2363 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2365 probe_box = gtk_hbox_new(FALSE, 3);
2366 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2368 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2369 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2370 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2371 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2372 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2373 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2374 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2375 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2377 probe_box = gtk_hbox_new(FALSE, 3);
2378 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2379 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2380 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2381 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2382 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2385 * Only add this if we have a commit rate
2388 probe_box = gtk_hbox_new(FALSE, 3);
2389 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2391 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2392 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2394 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2395 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2399 * Set up a drawing area and IOPS and bandwidth graphs
2401 gdk_color_parse("white", &white);
2402 ge->graphs.drawing_area = gtk_drawing_area_new();
2403 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2404 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2405 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2406 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2407 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2408 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2409 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2410 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2411 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2412 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2413 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2414 ge->graphs.drawing_area);
2415 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2418 setup_graphs(&ge->graphs);
2421 * Set up alignments for widgets at the bottom of ui,
2422 * align bottom left, expand horizontally but not vertically
2424 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2425 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2426 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2427 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2430 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2433 * Set up thread status progress bar
2435 ge->thread_status_pb = gtk_progress_bar_new();
2436 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2437 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2438 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2444 static GtkWidget *new_main_page(struct gui *ui)
2446 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2449 main_vbox = gtk_vbox_new(FALSE, 3);
2452 * Set up alignments for widgets at the top of ui,
2453 * align top left, expand horizontally but not vertically
2455 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2456 ui->topvbox = gtk_vbox_new(FALSE, 0);
2457 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2458 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2460 probe = gtk_frame_new("Run statistics");
2461 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2462 probe_frame = gtk_vbox_new(FALSE, 3);
2463 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2465 probe_box = gtk_hbox_new(FALSE, 3);
2466 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2467 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2468 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2469 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2470 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2471 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2474 * Only add this if we have a commit rate
2477 probe_box = gtk_hbox_new(FALSE, 3);
2478 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2480 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2481 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2483 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2484 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2488 * Set up a drawing area and IOPS and bandwidth graphs
2490 gdk_color_parse("white", &white);
2491 ui->graphs.drawing_area = gtk_drawing_area_new();
2492 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2493 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2494 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2495 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2496 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2497 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2498 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2499 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2500 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2501 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2502 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2503 ui->graphs.drawing_area);
2504 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2507 setup_graphs(&ui->graphs);
2510 * Set up alignments for widgets at the bottom of ui,
2511 * align bottom left, expand horizontally but not vertically
2513 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2514 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2515 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2516 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2520 * Set up thread status progress bar
2522 ui->thread_status_pb = gtk_progress_bar_new();
2523 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2524 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2525 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2530 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2531 guint page, gpointer data)
2537 static void init_ui(int *argc, char **argv[], struct gui *ui)
2539 GtkSettings *settings;
2540 GtkUIManager *uimanager;
2541 GtkWidget *menu, *vbox;
2543 /* Magical g*thread incantation, you just need this thread stuff.
2544 * Without it, the update that happens in gfio_update_thread_status
2545 * doesn't really happen in a timely fashion, you need expose events
2547 if (!g_thread_supported())
2548 g_thread_init(NULL);
2551 gtk_init(argc, argv);
2552 settings = gtk_settings_get_default();
2553 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2556 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2557 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2558 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2560 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2561 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2563 ui->vbox = gtk_vbox_new(FALSE, 0);
2564 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2566 uimanager = gtk_ui_manager_new();
2567 menu = get_menubar_menu(ui->window, uimanager, ui);
2568 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2570 ui->notebook = gtk_notebook_new();
2571 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2572 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2573 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2574 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2576 vbox = new_main_page(ui);
2578 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2580 gfio_ui_setup_log(ui);
2582 gtk_widget_show_all(ui->window);
2585 int main(int argc, char *argv[], char *envp[])
2587 if (initialize_fio(envp))
2589 if (fio_init_options())
2592 memset(&main_ui, 0, sizeof(main_ui));
2593 INIT_FLIST_HEAD(&main_ui.list);
2595 init_ui(&argc, &argv, &main_ui);
2597 gdk_threads_enter();
2599 gdk_threads_leave();