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 #define GFIO_MIME "text/fio"
37 static int gfio_server_running;
38 static const char *gfio_graph_font;
39 static unsigned int gfio_graph_limit = 100;
40 static GdkColor white;
42 static void view_log(GtkWidget *w, gpointer data);
44 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
46 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
48 static void connect_clicked(GtkWidget *widget, gpointer data);
49 static void start_job_clicked(GtkWidget *widget, gpointer data);
50 static void send_clicked(GtkWidget *widget, gpointer data);
52 static struct button_spec {
53 const char *buttontext;
55 const char *tooltiptext;
56 const int start_insensitive;
57 } buttonspeclist[] = {
58 #define CONNECT_BUTTON 0
60 #define START_JOB_BUTTON 2
61 { "Connect", connect_clicked, "Connect to host", 0 },
62 { "Send", send_clicked, "Send job description to host", 1 },
63 { "Start Job", start_job_clicked,
64 "Start the current job on the server", 1 },
74 struct multitext_widget {
77 unsigned int cur_text;
78 unsigned int max_text;
83 struct multitext_widget iotype;
84 struct multitext_widget ioengine;
85 struct multitext_widget iodepth;
93 GtkWidget *write_iops;
99 #define DRAWING_AREA_XDIM 1000
100 #define DRAWING_AREA_YDIM 400
101 GtkWidget *drawing_area;
102 struct graph *iops_graph;
103 struct graph *bandwidth_graph;
107 * Main window widgets and data
110 GtkUIManager *uimanager;
111 GtkRecentManager *recentmanager;
112 GtkActionGroup *actiongroup;
117 GtkWidget *thread_status_pb;
118 GtkWidget *buttonbox;
120 GtkWidget *error_info_bar;
121 GtkWidget *error_label;
122 GtkListStore *log_model;
125 struct gfio_graphs graphs;
126 struct probe_widget probe;
127 struct eta_widget eta;
133 struct flist_head list;
140 GE_STATE_JOB_STARTED,
141 GE_STATE_JOB_RUNNING,
149 struct flist_head list;
153 GtkWidget *job_notebook;
154 GtkWidget *thread_status_pb;
155 GtkWidget *buttonbox;
156 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
158 GtkWidget *error_info_bar;
159 GtkWidget *error_label;
160 GtkWidget *results_notebook;
161 GtkWidget *results_window;
162 GtkUIManager *results_uimanager;
163 GtkWidget *results_vbox;
164 GtkWidget *results_menu;
165 GtkListStore *log_model;
168 struct gfio_graphs graphs;
169 struct probe_widget probe;
170 struct eta_widget eta;
171 GtkWidget *page_label;
175 struct gfio_client *client;
178 struct graph *clat_graph;
182 struct group_run_stats gs;
183 struct thread_stat ts;
187 struct gui_entry *ge;
188 struct fio_client *client;
189 GtkWidget *results_widget;
190 GtkWidget *disk_util_frame;
191 GtkWidget *err_entry;
192 unsigned int job_added;
193 struct thread_options o;
195 struct end_results *results;
196 unsigned int nr_results;
199 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
200 static void gfio_update_thread_status_all(char *status_message, double perc);
201 void report_error(GError *error);
203 static struct graph *setup_iops_graph(void)
207 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
208 graph_title(g, "IOPS (IOs/sec)");
209 graph_x_title(g, "Time (secs)");
210 graph_add_label(g, "Read IOPS");
211 graph_add_label(g, "Write IOPS");
212 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
213 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
214 line_graph_set_data_count_limit(g, gfio_graph_limit);
215 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
219 static struct graph *setup_bandwidth_graph(void)
223 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
224 graph_title(g, "Bandwidth (bytes/sec)");
225 graph_x_title(g, "Time (secs)");
226 graph_add_label(g, "Read Bandwidth");
227 graph_add_label(g, "Write Bandwidth");
228 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
229 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
230 graph_set_base_offset(g, 1);
231 line_graph_set_data_count_limit(g, 100);
232 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
236 static void setup_graphs(struct gfio_graphs *g)
238 g->iops_graph = setup_iops_graph();
239 g->bandwidth_graph = setup_bandwidth_graph();
242 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
244 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
245 mt->text[mt->max_text] = strdup(text);
249 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
251 if (index >= mt->max_text)
253 if (!mt->text || !mt->text[index])
256 mt->cur_text = index;
257 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
260 static void multitext_update_entry(struct multitext_widget *mt,
261 unsigned int index, const char *text)
267 free(mt->text[index]);
269 mt->text[index] = strdup(text);
270 if (mt->cur_text == index)
271 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
274 static void multitext_free(struct multitext_widget *mt)
278 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
280 for (i = 0; i < mt->max_text; i++) {
290 static void clear_ge_ui_info(struct gui_entry *ge)
292 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
293 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
294 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
295 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
297 /* should we empty it... */
298 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
300 multitext_update_entry(&ge->eta.iotype, 0, "");
301 multitext_update_entry(&ge->eta.ioengine, 0, "");
302 multitext_update_entry(&ge->eta.iodepth, 0, "");
303 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
304 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
305 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
306 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
311 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
313 GtkWidget *entry, *frame;
315 frame = gtk_frame_new(label);
316 entry = gtk_combo_box_new_text();
317 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
318 gtk_container_add(GTK_CONTAINER(frame), entry);
323 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
325 GtkWidget *entry, *frame;
327 frame = gtk_frame_new(label);
328 entry = gtk_entry_new();
329 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
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_label_in_frame(GtkWidget *box, const char *label)
338 GtkWidget *label_widget;
341 frame = gtk_frame_new(label);
342 label_widget = gtk_label_new(NULL);
343 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
344 gtk_container_add(GTK_CONTAINER(frame), label_widget);
349 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
351 GtkWidget *button, *box;
353 box = gtk_hbox_new(FALSE, 3);
354 gtk_container_add(GTK_CONTAINER(hbox), box);
356 button = gtk_spin_button_new_with_range(min, max, 1.0);
357 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
359 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
360 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
365 static void label_set_int_value(GtkWidget *entry, unsigned int val)
369 sprintf(tmp, "%u", val);
370 gtk_label_set_text(GTK_LABEL(entry), tmp);
373 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
377 sprintf(tmp, "%u", val);
378 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
381 static void show_info_dialog(struct gui *ui, const char *title,
384 GtkWidget *dialog, *content, *label;
386 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
387 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
388 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
390 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
391 label = gtk_label_new(message);
392 gtk_container_add(GTK_CONTAINER(content), label);
393 gtk_widget_show_all(dialog);
394 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
395 gtk_dialog_run(GTK_DIALOG(dialog));
396 gtk_widget_destroy(dialog);
399 static void set_menu_entry_text(struct gui *ui, const char *path,
404 w = gtk_ui_manager_get_widget(ui->uimanager, path);
406 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
408 fprintf(stderr, "gfio: can't find path %s\n", path);
412 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
416 w = gtk_ui_manager_get_widget(ui->uimanager, path);
418 gtk_widget_set_sensitive(w, show);
420 fprintf(stderr, "gfio: can't find path %s\n", path);
423 static void set_job_menu_visible(struct gui *ui, int visible)
425 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
428 static void set_view_results_visible(struct gui *ui, int visible)
430 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
434 * Update sensitivity of job buttons and job menu items, based on the
435 * state of the client.
437 static void update_button_states(struct gui *ui, struct gui_entry *ge)
439 unsigned int connect_state, send_state, start_state, edit_state;
440 const char *connect_str = NULL;
446 sprintf(tmp, "Bad client state: %u\n", ge->state);
447 show_info_dialog(ui, "Error", tmp);
448 /* fall through to new state */
454 connect_str = "Connect";
458 case GE_STATE_CONNECTED:
461 connect_str = "Disconnect";
465 case GE_STATE_JOB_SENT:
468 connect_str = "Disconnect";
472 case GE_STATE_JOB_STARTED:
475 connect_str = "Disconnect";
479 case GE_STATE_JOB_RUNNING:
482 connect_str = "Disconnect";
486 case GE_STATE_JOB_DONE:
489 connect_str = "Connect";
495 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
496 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
497 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
498 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
500 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
501 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
503 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
504 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
505 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
507 if (ge->client && ge->client->nr_results)
508 set_view_results_visible(ui, 1);
510 set_view_results_visible(ui, 0);
513 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
516 update_button_states(ge->ui, ge);
520 #define ALIGN_RIGHT 2
524 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
526 GtkCellRenderer *renderer;
527 GtkTreeViewColumn *col;
528 double xalign = 0.0; /* left as default */
529 PangoAlignment align;
532 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
533 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
535 visible = !(flags & INVISIBLE);
537 renderer = gtk_cell_renderer_text_new();
538 col = gtk_tree_view_column_new();
540 gtk_tree_view_column_set_title(col, title);
541 if (!(flags & UNSORTABLE))
542 gtk_tree_view_column_set_sort_column_id(col, index);
543 gtk_tree_view_column_set_resizable(col, TRUE);
544 gtk_tree_view_column_pack_start(col, renderer, TRUE);
545 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
546 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
548 case PANGO_ALIGN_LEFT:
551 case PANGO_ALIGN_CENTER:
554 case PANGO_ALIGN_RIGHT:
558 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
559 gtk_tree_view_column_set_visible(col, visible);
560 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
564 static void gfio_ui_setup_log(struct gui *ui)
566 GtkTreeSelection *selection;
568 GtkWidget *tree_view;
570 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
572 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
573 gtk_widget_set_can_focus(tree_view, FALSE);
575 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
576 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
577 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
578 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
580 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
581 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
582 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
583 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
585 ui->log_model = model;
586 ui->log_tree = tree_view;
589 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
592 double xdim, double ydim)
597 g = graph_new(xdim, ydim, gfio_graph_font);
598 graph_title(g, title);
599 graph_x_title(g, "Percentile");
601 for (i = 0; i < len; i++) {
604 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
605 graph_add_label(g, fbuf);
606 graph_add_data(g, fbuf, (double) ovals[i]);
612 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
618 GType types[FIO_IO_U_LIST_MAX_LEN];
619 GtkWidget *tree_view;
620 GtkTreeSelection *selection;
625 for (i = 0; i < len; i++)
626 types[i] = G_TYPE_INT;
628 model = gtk_list_store_newv(len, types);
630 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
631 gtk_widget_set_can_focus(tree_view, FALSE);
633 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
634 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
636 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
637 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
639 for (i = 0; i < len; i++) {
642 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
643 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
646 gtk_list_store_append(model, &iter);
648 for (i = 0; i < len; i++) {
650 ovals[i] = (ovals[i] + 999) / 1000;
651 gtk_list_store_set(model, &iter, i, ovals[i], -1);
657 static int on_expose_clat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
662 cr = gdk_cairo_create(w->window);
664 if (graph_has_tooltips(g)) {
665 g_object_set(w, "has-tooltip", TRUE, NULL);
666 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
669 cairo_set_source_rgb(cr, 0, 0, 0);
670 bar_graph_draw(g, cr);
676 static gint on_config_clat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
679 struct graph *g = data;
681 graph_set_size(g, w->allocation.width, w->allocation.height);
682 graph_set_size(g, w->allocation.width, w->allocation.height);
683 graph_set_position(g, 0, 0);
687 static void gfio_show_clat_percentiles(struct gfio_client *gc,
688 GtkWidget *vbox, struct thread_stat *ts,
691 unsigned int *io_u_plat = ts->io_u_plat[ddir];
692 unsigned long nr = ts->clat_stat[ddir].samples;
693 fio_fp64_t *plist = ts->percentile_list;
694 unsigned int *ovals, len, minv, maxv, scale_down;
696 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
699 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
704 * We default to usecs, but if the value range is such that we
705 * should scale down to msecs, do that.
707 if (minv > 2000 && maxv > 99999) {
715 sprintf(tmp, "Completion percentiles (%s)", base);
716 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
717 gc->ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
719 frame = gtk_frame_new(tmp);
720 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
722 completion_vbox = gtk_vbox_new(FALSE, 3);
723 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
724 hbox = gtk_hbox_new(FALSE, 3);
725 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
726 drawing_area = gtk_drawing_area_new();
727 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
728 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
729 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
730 g_signal_connect(G_OBJECT(drawing_area), "expose_event",
731 G_CALLBACK(on_expose_clat_drawing_area), gc->ge->clat_graph);
732 g_signal_connect(G_OBJECT(drawing_area), "configure_event",
733 G_CALLBACK(on_config_clat_drawing_area), gc->ge->clat_graph);
735 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
741 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
742 unsigned long max, double mean, double dev)
744 const char *base = "(usec)";
745 GtkWidget *hbox, *label, *frame;
749 if (!usec_to_msec(&min, &max, &mean, &dev))
752 minp = num2str(min, 6, 1, 0);
753 maxp = num2str(max, 6, 1, 0);
755 sprintf(tmp, "%s %s", name, base);
756 frame = gtk_frame_new(tmp);
757 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
759 hbox = gtk_hbox_new(FALSE, 3);
760 gtk_container_add(GTK_CONTAINER(frame), hbox);
762 label = new_info_label_in_frame(hbox, "Minimum");
763 gtk_label_set_text(GTK_LABEL(label), minp);
764 label = new_info_label_in_frame(hbox, "Maximum");
765 gtk_label_set_text(GTK_LABEL(label), maxp);
766 label = new_info_label_in_frame(hbox, "Average");
767 sprintf(tmp, "%5.02f", mean);
768 gtk_label_set_text(GTK_LABEL(label), tmp);
769 label = new_info_label_in_frame(hbox, "Standard deviation");
770 sprintf(tmp, "%5.02f", dev);
771 gtk_label_set_text(GTK_LABEL(label), tmp);
782 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
783 struct group_run_stats *rs,
784 struct thread_stat *ts, int ddir)
786 const char *ddir_label[2] = { "Read", "Write" };
787 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
788 unsigned long min[3], max[3], runt;
789 unsigned long long bw, iops;
790 unsigned int flags = 0;
791 double mean[3], dev[3];
792 char *io_p, *bw_p, *iops_p;
795 if (!ts->runtime[ddir])
798 i2p = is_power_of_2(rs->kb_base);
799 runt = ts->runtime[ddir];
801 bw = (1000 * ts->io_bytes[ddir]) / runt;
802 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
803 bw_p = num2str(bw, 6, 1, i2p);
805 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
806 iops_p = num2str(iops, 6, 1, 0);
808 box = gtk_hbox_new(FALSE, 3);
809 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
811 frame = gtk_frame_new(ddir_label[ddir]);
812 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
814 main_vbox = gtk_vbox_new(FALSE, 3);
815 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
817 box = gtk_hbox_new(FALSE, 3);
818 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
820 label = new_info_label_in_frame(box, "IO");
821 gtk_label_set_text(GTK_LABEL(label), io_p);
822 label = new_info_label_in_frame(box, "Bandwidth");
823 gtk_label_set_text(GTK_LABEL(label), bw_p);
824 label = new_info_label_in_frame(box, "IOPS");
825 gtk_label_set_text(GTK_LABEL(label), iops_p);
826 label = new_info_label_in_frame(box, "Runtime (msec)");
827 label_set_int_value(label, ts->runtime[ddir]);
829 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
830 double p_of_agg = 100.0;
831 const char *bw_str = "KB";
835 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
836 if (p_of_agg > 100.0)
840 if (mean[0] > 999999.9) {
848 sprintf(tmp, "Bandwidth (%s)", bw_str);
849 frame = gtk_frame_new(tmp);
850 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
852 box = gtk_hbox_new(FALSE, 3);
853 gtk_container_add(GTK_CONTAINER(frame), box);
855 label = new_info_label_in_frame(box, "Minimum");
856 label_set_int_value(label, min[0]);
857 label = new_info_label_in_frame(box, "Maximum");
858 label_set_int_value(label, max[0]);
859 label = new_info_label_in_frame(box, "Percentage of jobs");
860 sprintf(tmp, "%3.2f%%", p_of_agg);
861 gtk_label_set_text(GTK_LABEL(label), tmp);
862 label = new_info_label_in_frame(box, "Average");
863 sprintf(tmp, "%5.02f", mean[0]);
864 gtk_label_set_text(GTK_LABEL(label), tmp);
865 label = new_info_label_in_frame(box, "Standard deviation");
866 sprintf(tmp, "%5.02f", dev[0]);
867 gtk_label_set_text(GTK_LABEL(label), tmp);
870 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
872 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
874 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
878 frame = gtk_frame_new("Latency");
879 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
881 vbox = gtk_vbox_new(FALSE, 3);
882 gtk_container_add(GTK_CONTAINER(frame), vbox);
884 if (flags & GFIO_SLAT)
885 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
886 if (flags & GFIO_CLAT)
887 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
888 if (flags & GFIO_LAT)
889 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
892 if (ts->clat_percentiles)
893 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
901 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
904 GtkWidget *tree_view;
905 GtkTreeSelection *selection;
912 * Check if all are empty, in which case don't bother
914 for (i = 0, skipped = 0; i < num; i++)
921 types = malloc(num * sizeof(GType));
923 for (i = 0; i < num; i++)
924 types[i] = G_TYPE_STRING;
926 model = gtk_list_store_newv(num, types);
930 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
931 gtk_widget_set_can_focus(tree_view, FALSE);
933 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
934 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
936 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
937 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
939 for (i = 0; i < num; i++)
940 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
942 gtk_list_store_append(model, &iter);
944 for (i = 0; i < num; i++) {
948 sprintf(fbuf, "0.00");
950 sprintf(fbuf, "%3.2f%%", lat[i]);
952 gtk_list_store_set(model, &iter, i, fbuf, -1);
958 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
960 GtkWidget *box, *frame, *tree_view;
961 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
962 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
963 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
964 "250", "500", "750", "1000", };
965 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
966 "250", "500", "750", "1000", "2000",
969 stat_calc_lat_u(ts, io_u_lat_u);
970 stat_calc_lat_m(ts, io_u_lat_m);
972 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
974 frame = gtk_frame_new("Latency buckets (usec)");
975 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
977 box = gtk_hbox_new(FALSE, 3);
978 gtk_container_add(GTK_CONTAINER(frame), box);
979 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
982 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
984 frame = gtk_frame_new("Latency buckets (msec)");
985 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
987 box = gtk_hbox_new(FALSE, 3);
988 gtk_container_add(GTK_CONTAINER(frame), box);
989 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
993 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
995 GtkWidget *box, *frame, *entry;
996 double usr_cpu, sys_cpu;
997 unsigned long runtime;
1000 runtime = ts->total_run_time;
1002 double runt = (double) runtime;
1004 usr_cpu = (double) ts->usr_time * 100 / runt;
1005 sys_cpu = (double) ts->sys_time * 100 / runt;
1011 frame = gtk_frame_new("OS resources");
1012 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1014 box = gtk_hbox_new(FALSE, 3);
1015 gtk_container_add(GTK_CONTAINER(frame), box);
1017 entry = new_info_entry_in_frame(box, "User CPU");
1018 sprintf(tmp, "%3.2f%%", usr_cpu);
1019 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1020 entry = new_info_entry_in_frame(box, "System CPU");
1021 sprintf(tmp, "%3.2f%%", sys_cpu);
1022 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1023 entry = new_info_entry_in_frame(box, "Context switches");
1024 entry_set_int_value(entry, ts->ctx);
1025 entry = new_info_entry_in_frame(box, "Major faults");
1026 entry_set_int_value(entry, ts->majf);
1027 entry = new_info_entry_in_frame(box, "Minor faults");
1028 entry_set_int_value(entry, ts->minf);
1030 static void gfio_add_sc_depths_tree(GtkListStore *model,
1031 struct thread_stat *ts, unsigned int len,
1034 double io_u_dist[FIO_IO_U_MAP_NR];
1036 /* Bits 0, and 3-8 */
1037 const int add_mask = 0x1f9;
1041 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1043 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1045 gtk_list_store_append(model, &iter);
1047 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1049 for (i = 1, j = 0; i < len; i++) {
1052 if (!(add_mask & (1UL << (i - 1))))
1053 sprintf(fbuf, "0.0%%");
1055 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1059 gtk_list_store_set(model, &iter, i, fbuf, -1);
1064 static void gfio_add_total_depths_tree(GtkListStore *model,
1065 struct thread_stat *ts, unsigned int len)
1067 double io_u_dist[FIO_IO_U_MAP_NR];
1069 /* Bits 1-6, and 8 */
1070 const int add_mask = 0x17e;
1073 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1075 gtk_list_store_append(model, &iter);
1077 gtk_list_store_set(model, &iter, 0, "Total", -1);
1079 for (i = 1, j = 0; i < len; i++) {
1082 if (!(add_mask & (1UL << (i - 1))))
1083 sprintf(fbuf, "0.0%%");
1085 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1089 gtk_list_store_set(model, &iter, i, fbuf, -1);
1094 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1096 GtkWidget *frame, *box, *tree_view;
1097 GtkTreeSelection *selection;
1098 GtkListStore *model;
1099 GType types[FIO_IO_U_MAP_NR + 1];
1101 #define NR_LABELS 10
1102 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1104 frame = gtk_frame_new("IO depths");
1105 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1107 box = gtk_hbox_new(FALSE, 3);
1108 gtk_container_add(GTK_CONTAINER(frame), box);
1110 for (i = 0; i < NR_LABELS; i++)
1111 types[i] = G_TYPE_STRING;
1113 model = gtk_list_store_newv(NR_LABELS, types);
1115 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1116 gtk_widget_set_can_focus(tree_view, FALSE);
1118 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1119 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1121 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1122 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1124 for (i = 0; i < NR_LABELS; i++)
1125 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1127 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1128 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1129 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1131 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1134 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1136 struct gui_entry *ge = (struct gui_entry *) data;
1138 gtk_widget_destroy(w);
1139 ge->results_window = NULL;
1140 ge->results_notebook = NULL;
1144 static void results_close(GtkWidget *w, gpointer *data)
1146 struct gui_entry *ge = (struct gui_entry *) data;
1148 gtk_widget_destroy(ge->results_window);
1151 static GtkActionEntry results_menu_items[] = {
1152 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1153 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1154 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1156 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1158 static const gchar *results_ui_string = " \
1160 <menubar name=\"MainMenu\"> \
1161 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1162 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1164 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1170 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1172 GtkActionGroup *action_group;
1176 ge->results_uimanager = gtk_ui_manager_new();
1178 action_group = gtk_action_group_new("ResultsMenu");
1179 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1181 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1182 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1184 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1186 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1190 static GtkWidget *get_results_window(struct gui_entry *ge)
1192 GtkWidget *win, *notebook, *vbox;
1194 if (ge->results_window)
1195 return ge->results_notebook;
1197 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1198 gtk_window_set_title(GTK_WINDOW(win), "Results");
1199 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1200 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1201 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1203 vbox = gtk_vbox_new(FALSE, 0);
1204 gtk_container_add(GTK_CONTAINER(win), vbox);
1206 ge->results_menu = get_results_menubar(win, ge);
1207 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1209 notebook = gtk_notebook_new();
1210 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1211 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1212 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1214 ge->results_window = win;
1215 ge->results_notebook = notebook;
1216 return ge->results_notebook;
1219 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1220 struct group_run_stats *rs)
1222 unsigned int nr = gc->nr_results;
1224 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1225 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1226 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1230 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1231 struct thread_stat *ts,
1232 struct group_run_stats *rs)
1234 GtkWidget *box, *vbox, *entry, *scroll;
1236 scroll = gtk_scrolled_window_new(NULL, NULL);
1237 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1238 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1240 vbox = gtk_vbox_new(FALSE, 3);
1242 box = gtk_hbox_new(FALSE, 0);
1243 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1245 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1247 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1249 gc->results_widget = vbox;
1251 entry = new_info_entry_in_frame(box, "Name");
1252 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1253 if (strlen(ts->description)) {
1254 entry = new_info_entry_in_frame(box, "Description");
1255 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1257 entry = new_info_entry_in_frame(box, "Group ID");
1258 entry_set_int_value(entry, ts->groupid);
1259 entry = new_info_entry_in_frame(box, "Jobs");
1260 entry_set_int_value(entry, ts->members);
1261 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1262 entry_set_int_value(entry, ts->error);
1263 entry = new_info_entry_in_frame(box, "PID");
1264 entry_set_int_value(entry, ts->pid);
1266 if (ts->io_bytes[DDIR_READ])
1267 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1268 if (ts->io_bytes[DDIR_WRITE])
1269 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1271 gfio_show_latency_buckets(vbox, ts);
1272 gfio_show_cpu_usage(vbox, ts);
1273 gfio_show_io_depths(vbox, ts);
1276 static void gfio_display_end_results(struct gfio_client *gc)
1281 res_win = get_results_window(gc->ge);
1283 for (i = 0; i < gc->nr_results; i++) {
1284 struct end_results *e = &gc->results[i];
1286 __gfio_display_end_results(res_win, gc, &e->ts, &e->gs);
1289 gtk_widget_show_all(gc->ge->results_window);
1292 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1293 struct group_run_stats *rs)
1295 struct gfio_client *gc = client->client_data;
1297 gfio_add_end_results(gc, ts, rs);
1299 gdk_threads_enter();
1300 gfio_display_end_results(gc);
1301 gdk_threads_leave();
1304 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1306 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1307 struct gui *ui = &main_ui;
1311 char tmp[64], timebuf[80];
1314 tm = localtime(&sec);
1315 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1316 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1318 gdk_threads_enter();
1320 gtk_list_store_append(ui->log_model, &iter);
1321 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1322 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1323 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1324 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1326 if (p->level == FIO_LOG_ERR)
1327 view_log(NULL, (gpointer) ui);
1329 gdk_threads_leave();
1332 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1334 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1335 struct gfio_client *gc = client->client_data;
1336 GtkWidget *box, *frame, *entry, *vbox;
1340 gdk_threads_enter();
1342 if (!gc->results_widget)
1345 if (!gc->disk_util_frame) {
1346 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1347 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1350 vbox = gtk_vbox_new(FALSE, 3);
1351 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1353 frame = gtk_frame_new((char *) p->dus.name);
1354 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1356 box = gtk_vbox_new(FALSE, 3);
1357 gtk_container_add(GTK_CONTAINER(frame), box);
1359 frame = gtk_frame_new("Read");
1360 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1361 vbox = gtk_hbox_new(TRUE, 3);
1362 gtk_container_add(GTK_CONTAINER(frame), vbox);
1363 entry = new_info_entry_in_frame(vbox, "IOs");
1364 entry_set_int_value(entry, p->dus.ios[0]);
1365 entry = new_info_entry_in_frame(vbox, "Merges");
1366 entry_set_int_value(entry, p->dus.merges[0]);
1367 entry = new_info_entry_in_frame(vbox, "Sectors");
1368 entry_set_int_value(entry, p->dus.sectors[0]);
1369 entry = new_info_entry_in_frame(vbox, "Ticks");
1370 entry_set_int_value(entry, p->dus.ticks[0]);
1372 frame = gtk_frame_new("Write");
1373 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1374 vbox = gtk_hbox_new(TRUE, 3);
1375 gtk_container_add(GTK_CONTAINER(frame), vbox);
1376 entry = new_info_entry_in_frame(vbox, "IOs");
1377 entry_set_int_value(entry, p->dus.ios[1]);
1378 entry = new_info_entry_in_frame(vbox, "Merges");
1379 entry_set_int_value(entry, p->dus.merges[1]);
1380 entry = new_info_entry_in_frame(vbox, "Sectors");
1381 entry_set_int_value(entry, p->dus.sectors[1]);
1382 entry = new_info_entry_in_frame(vbox, "Ticks");
1383 entry_set_int_value(entry, p->dus.ticks[1]);
1385 frame = gtk_frame_new("Shared");
1386 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1387 vbox = gtk_hbox_new(TRUE, 3);
1388 gtk_container_add(GTK_CONTAINER(frame), vbox);
1389 entry = new_info_entry_in_frame(vbox, "IO ticks");
1390 entry_set_int_value(entry, p->dus.io_ticks);
1391 entry = new_info_entry_in_frame(vbox, "Time in queue");
1392 entry_set_int_value(entry, p->dus.time_in_queue);
1396 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1400 sprintf(tmp, "%3.2f%%", util);
1401 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1402 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1404 gtk_widget_show_all(gc->results_widget);
1406 gdk_threads_leave();
1409 extern int sum_stat_clients;
1410 extern struct thread_stat client_ts;
1411 extern struct group_run_stats client_gs;
1413 static int sum_stat_nr;
1415 static void gfio_thread_status_op(struct fio_client *client,
1416 struct fio_net_cmd *cmd)
1418 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1420 gfio_display_ts(client, &p->ts, &p->rs);
1422 if (sum_stat_clients == 1)
1425 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1426 sum_group_stats(&client_gs, &p->rs);
1428 client_ts.members++;
1429 client_ts.groupid = p->ts.groupid;
1431 if (++sum_stat_nr == sum_stat_clients) {
1432 strcpy(client_ts.name, "All clients");
1433 gfio_display_ts(client, &client_ts, &client_gs);
1437 static void gfio_group_stats_op(struct fio_client *client,
1438 struct fio_net_cmd *cmd)
1440 /* We're ignoring group stats for now */
1443 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1446 struct gfio_graphs *g = data;
1448 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1449 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1450 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1451 graph_set_position(g->bandwidth_graph, 0, 0);
1455 static void draw_graph(struct graph *g, cairo_t *cr)
1457 line_graph_draw(g, cr);
1461 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1462 gboolean keyboard_mode, GtkTooltip *tooltip,
1465 struct gfio_graphs *g = data;
1466 const char *text = NULL;
1468 if (graph_contains_xy(g->iops_graph, x, y))
1469 text = graph_find_tooltip(g->iops_graph, x, y);
1470 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1471 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1474 gtk_tooltip_set_text(tooltip, text);
1481 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1483 struct gfio_graphs *g = p;
1486 cr = gdk_cairo_create(w->window);
1488 if (graph_has_tooltips(g->iops_graph) ||
1489 graph_has_tooltips(g->bandwidth_graph)) {
1490 g_object_set(w, "has-tooltip", TRUE, NULL);
1491 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1494 cairo_set_source_rgb(cr, 0, 0, 0);
1495 draw_graph(g->iops_graph, cr);
1496 draw_graph(g->bandwidth_graph, cr);
1503 * Client specific ETA
1505 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1507 struct gfio_client *gc = client->client_data;
1508 struct gui_entry *ge = gc->ge;
1509 static int eta_good;
1516 gdk_threads_enter();
1521 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1522 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1523 eta_to_str(eta_str, je->eta_sec);
1526 sprintf(tmp, "%u", je->nr_running);
1527 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1528 sprintf(tmp, "%u", je->files_open);
1529 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1532 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1533 if (je->m_rate || je->t_rate) {
1536 mr = num2str(je->m_rate, 4, 0, i2p);
1537 tr = num2str(je->t_rate, 4, 0, i2p);
1538 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1539 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1542 } else if (je->m_iops || je->t_iops)
1543 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1545 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1546 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1547 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1548 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1551 if (je->eta_sec != INT_MAX && je->nr_running) {
1555 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1556 strcpy(output, "-.-% done");
1560 sprintf(output, "%3.1f%% done", perc);
1563 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1564 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1566 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1567 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1569 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1570 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1571 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1572 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1574 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1575 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1576 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1577 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1586 char *dst = output + strlen(output);
1588 sprintf(dst, " - %s", eta_str);
1591 gfio_update_thread_status(ge, output, perc);
1592 gdk_threads_leave();
1596 * Update ETA in main window for all clients
1598 static void gfio_update_all_eta(struct jobs_eta *je)
1600 struct gui *ui = &main_ui;
1601 static int eta_good;
1607 gdk_threads_enter();
1612 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1613 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1614 eta_to_str(eta_str, je->eta_sec);
1618 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1619 if (je->m_rate || je->t_rate) {
1622 mr = num2str(je->m_rate, 4, 0, i2p);
1623 tr = num2str(je->t_rate, 4, 0, i2p);
1624 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1625 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1628 } else if (je->m_iops || je->t_iops)
1629 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1631 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1632 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1633 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1634 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1637 entry_set_int_value(ui->eta.jobs, je->nr_running);
1639 if (je->eta_sec != INT_MAX && je->nr_running) {
1643 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1644 strcpy(output, "-.-% done");
1648 sprintf(output, "%3.1f%% done", perc);
1651 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1652 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1654 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1655 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1657 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1658 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1659 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1660 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1662 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1663 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1664 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1665 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1674 char *dst = output + strlen(output);
1676 sprintf(dst, " - %s", eta_str);
1679 gfio_update_thread_status_all(output, perc);
1680 gdk_threads_leave();
1683 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1685 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1686 struct gfio_client *gc = client->client_data;
1687 struct gui_entry *ge = gc->ge;
1688 const char *os, *arch;
1691 os = fio_get_os_string(probe->os);
1695 arch = fio_get_arch_string(probe->arch);
1700 client->name = strdup((char *) probe->hostname);
1702 gdk_threads_enter();
1704 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1705 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1706 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1707 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1708 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1710 gfio_set_state(ge, GE_STATE_CONNECTED);
1712 gdk_threads_leave();
1715 static void gfio_update_thread_status(struct gui_entry *ge,
1716 char *status_message, double perc)
1718 static char message[100];
1719 const char *m = message;
1721 strncpy(message, status_message, sizeof(message) - 1);
1722 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1723 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1724 gtk_widget_queue_draw(main_ui.window);
1727 static void gfio_update_thread_status_all(char *status_message, double perc)
1729 struct gui *ui = &main_ui;
1730 static char message[100];
1731 const char *m = message;
1733 strncpy(message, status_message, sizeof(message) - 1);
1734 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1735 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1736 gtk_widget_queue_draw(ui->window);
1739 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1741 struct gfio_client *gc = client->client_data;
1743 gdk_threads_enter();
1744 gfio_set_state(gc->ge, GE_STATE_NEW);
1745 gdk_threads_leave();
1748 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1750 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1751 struct gfio_client *gc = client->client_data;
1752 struct thread_options *o = &gc->o;
1753 struct gui_entry *ge = gc->ge;
1756 convert_thread_options_to_cpu(o, &p->top);
1758 gdk_threads_enter();
1760 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1762 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1763 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1765 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1766 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1768 sprintf(tmp, "%u", o->iodepth);
1769 multitext_add_entry(&ge->eta.iodepth, tmp);
1771 multitext_set_entry(&ge->eta.iotype, 0);
1772 multitext_set_entry(&ge->eta.ioengine, 0);
1773 multitext_set_entry(&ge->eta.iodepth, 0);
1777 gfio_set_state(ge, GE_STATE_JOB_SENT);
1779 gdk_threads_leave();
1782 static void gfio_client_timed_out(struct fio_client *client)
1784 struct gfio_client *gc = client->client_data;
1787 gdk_threads_enter();
1789 gfio_set_state(gc->ge, GE_STATE_NEW);
1790 clear_ge_ui_info(gc->ge);
1792 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1793 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1795 gdk_threads_leave();
1798 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1800 struct gfio_client *gc = client->client_data;
1802 gdk_threads_enter();
1804 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1807 entry_set_int_value(gc->err_entry, client->error);
1809 gdk_threads_leave();
1812 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1814 struct gfio_client *gc = client->client_data;
1816 gdk_threads_enter();
1817 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1818 gdk_threads_leave();
1821 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1823 struct gfio_client *gc = client->client_data;
1825 gdk_threads_enter();
1826 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1827 gdk_threads_leave();
1830 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1832 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1836 struct client_ops gfio_client_ops = {
1837 .text = gfio_text_op,
1838 .disk_util = gfio_disk_util_op,
1839 .thread_status = gfio_thread_status_op,
1840 .group_stats = gfio_group_stats_op,
1841 .jobs_eta = gfio_update_client_eta,
1842 .eta = gfio_update_all_eta,
1843 .probe = gfio_probe_op,
1844 .quit = gfio_quit_op,
1845 .add_job = gfio_add_job_op,
1846 .timed_out = gfio_client_timed_out,
1847 .stop = gfio_client_stop,
1848 .start = gfio_client_start,
1849 .job_start = gfio_client_job_start,
1850 .iolog = gfio_client_iolog,
1851 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1852 .stay_connected = 1,
1853 .client_type = FIO_CLIENT_TYPE_GUI,
1857 * FIXME: need more handling here
1859 static void ge_destroy(struct gui_entry *ge)
1861 struct gfio_client *gc = ge->client;
1863 if (gc && gc->client) {
1864 if (ge->state >= GE_STATE_CONNECTED)
1865 fio_client_terminate(gc->client);
1867 fio_put_client(gc->client);
1870 flist_del(&ge->list);
1874 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1878 static void gfio_quit(struct gui *ui)
1880 struct gui_entry *ge;
1882 while (!flist_empty(&ui->list)) {
1883 ge = flist_entry(ui->list.next, struct gui_entry, list);
1890 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1891 __attribute__((unused)) gpointer data)
1896 static void *job_thread(void *arg)
1898 struct gui *ui = arg;
1900 ui->handler_running = 1;
1901 fio_handle_clients(&gfio_client_ops);
1902 ui->handler_running = 0;
1906 static int send_job_files(struct gui_entry *ge)
1908 struct gfio_client *gc = ge->client;
1911 for (i = 0; i < ge->nr_job_files; i++) {
1912 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1916 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1917 report_error(error);
1918 g_error_free(error);
1923 free(ge->job_files[i]);
1924 ge->job_files[i] = NULL;
1926 while (i < ge->nr_job_files) {
1927 free(ge->job_files[i]);
1928 ge->job_files[i] = NULL;
1932 free(ge->job_files);
1933 ge->job_files = NULL;
1934 ge->nr_job_files = 0;
1938 static void *server_thread(void *arg)
1941 gfio_server_running = 1;
1942 fio_start_server(NULL);
1943 gfio_server_running = 0;
1947 static void gfio_start_server(void)
1949 struct gui *ui = &main_ui;
1951 if (!gfio_server_running) {
1952 gfio_server_running = 1;
1953 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1954 pthread_detach(ui->server_t);
1958 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1961 struct gui_entry *ge = data;
1962 struct gfio_client *gc = ge->client;
1965 fio_start_client(gc->client);
1968 static void file_open(GtkWidget *w, gpointer data);
1970 static void connect_clicked(GtkWidget *widget, gpointer data)
1972 struct gui_entry *ge = data;
1973 struct gfio_client *gc = ge->client;
1975 if (ge->state == GE_STATE_NEW) {
1978 if (!ge->nr_job_files)
1979 file_open(widget, ge->ui);
1980 if (!ge->nr_job_files)
1983 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1984 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1985 ret = fio_client_connect(gc->client);
1987 if (!ge->ui->handler_running)
1988 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1989 gfio_set_state(ge, GE_STATE_CONNECTED);
1993 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1994 report_error(error);
1995 g_error_free(error);
1998 fio_client_terminate(gc->client);
1999 gfio_set_state(ge, GE_STATE_NEW);
2000 clear_ge_ui_info(ge);
2004 static void send_clicked(GtkWidget *widget, gpointer data)
2006 struct gui_entry *ge = data;
2008 if (send_job_files(ge)) {
2011 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);
2012 report_error(error);
2013 g_error_free(error);
2015 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2019 static GtkWidget *add_button(GtkWidget *buttonbox,
2020 struct button_spec *buttonspec, gpointer data)
2022 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
2024 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
2025 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
2026 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
2027 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
2032 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
2037 for (i = 0; i < nbuttons; i++)
2038 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
2041 static void on_info_bar_response(GtkWidget *widget, gint response,
2044 struct gui *ui = &main_ui;
2046 if (response == GTK_RESPONSE_OK) {
2047 gtk_widget_destroy(widget);
2048 ui->error_info_bar = NULL;
2052 void report_error(GError *error)
2054 struct gui *ui = &main_ui;
2056 if (ui->error_info_bar == NULL) {
2057 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2060 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2061 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2064 ui->error_label = gtk_label_new(error->message);
2065 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2066 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2068 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2069 gtk_widget_show_all(ui->vbox);
2072 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2073 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2077 struct connection_widgets
2084 static void hostname_cb(GtkEntry *entry, gpointer data)
2086 struct connection_widgets *cw = data;
2087 int uses_net = 0, is_localhost = 0;
2092 * Check whether to display the 'auto start backend' box
2093 * or not. Show it if we are a localhost and using network,
2094 * or using a socket.
2096 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2097 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2102 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2103 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2104 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2105 !strcmp(text, "ip6-loopback"))
2109 if (!uses_net || is_localhost) {
2110 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2111 gtk_widget_set_sensitive(cw->button, 1);
2113 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2114 gtk_widget_set_sensitive(cw->button, 0);
2118 static int get_connection_details(char **host, int *port, int *type,
2121 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2122 struct connection_widgets cw;
2125 dialog = gtk_dialog_new_with_buttons("Connection details",
2126 GTK_WINDOW(main_ui.window),
2127 GTK_DIALOG_DESTROY_WITH_PARENT,
2128 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2129 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2131 frame = gtk_frame_new("Hostname / socket name");
2132 /* gtk_dialog_get_content_area() is 2.14 and newer */
2133 vbox = GTK_DIALOG(dialog)->vbox;
2134 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2136 box = gtk_vbox_new(FALSE, 6);
2137 gtk_container_add(GTK_CONTAINER(frame), box);
2139 hbox = gtk_hbox_new(TRUE, 10);
2140 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2141 cw.hentry = gtk_entry_new();
2142 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2143 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2145 frame = gtk_frame_new("Port");
2146 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2147 box = gtk_vbox_new(FALSE, 10);
2148 gtk_container_add(GTK_CONTAINER(frame), box);
2150 hbox = gtk_hbox_new(TRUE, 4);
2151 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2152 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2154 frame = gtk_frame_new("Type");
2155 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2156 box = gtk_vbox_new(FALSE, 10);
2157 gtk_container_add(GTK_CONTAINER(frame), box);
2159 hbox = gtk_hbox_new(TRUE, 4);
2160 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2162 cw.combo = gtk_combo_box_new_text();
2163 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2164 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2165 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2166 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2168 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2170 frame = gtk_frame_new("Options");
2171 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2172 box = gtk_vbox_new(FALSE, 10);
2173 gtk_container_add(GTK_CONTAINER(frame), box);
2175 hbox = gtk_hbox_new(TRUE, 4);
2176 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2178 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2179 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2180 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.");
2181 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2184 * Connect edit signal, so we can show/not-show the auto start button
2186 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2187 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2189 gtk_widget_show_all(dialog);
2191 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2192 gtk_widget_destroy(dialog);
2196 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2197 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2199 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2200 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2201 *type = Fio_client_ipv4;
2202 else if (!strncmp(typeentry, "IPv6", 4))
2203 *type = Fio_client_ipv6;
2205 *type = Fio_client_socket;
2208 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2210 gtk_widget_destroy(dialog);
2214 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2216 struct gfio_client *gc;
2218 gc = malloc(sizeof(*gc));
2219 memset(gc, 0, sizeof(*gc));
2221 gc->client = fio_get_client(client);
2225 client->client_data = gc;
2228 static GtkWidget *new_client_page(struct gui_entry *ge);
2230 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2232 struct gui_entry *ge;
2234 ge = malloc(sizeof(*ge));
2235 memset(ge, 0, sizeof(*ge));
2236 ge->state = GE_STATE_NEW;
2237 INIT_FLIST_HEAD(&ge->list);
2238 flist_add_tail(&ge->list, &ui->list);
2243 static struct gui_entry *get_new_ge_with_tab(const char *name)
2245 struct gui_entry *ge;
2247 ge = alloc_new_gui_entry(&main_ui);
2249 ge->vbox = new_client_page(ge);
2250 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2252 ge->page_label = gtk_label_new(name);
2253 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2255 gtk_widget_show_all(main_ui.window);
2259 static void file_new(GtkWidget *w, gpointer data)
2261 struct gui *ui = (struct gui *) data;
2262 struct gui_entry *ge;
2264 ge = get_new_ge_with_tab("Untitled");
2265 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2269 * Return the 'ge' corresponding to the tab. If the active tab is the
2270 * main tab, open a new tab.
2272 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2274 struct flist_head *entry;
2275 struct gui_entry *ge;
2280 return get_new_ge_with_tab("Untitled");
2286 flist_for_each(entry, &main_ui.list) {
2287 ge = flist_entry(entry, struct gui_entry, list);
2288 if (ge->page_num == cur_page)
2295 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2300 * Main tab is tab 0, so any current page other than 0 holds
2303 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2305 return get_ge_from_page(cur_page, NULL);
2310 static void file_close(GtkWidget *w, gpointer data)
2312 struct gui *ui = (struct gui *) data;
2313 struct gui_entry *ge;
2316 * Can't close the main tab
2318 ge = get_ge_from_cur_tab(ui);
2320 gtk_widget_destroy(ge->vbox);
2324 if (!flist_empty(&ui->list)) {
2325 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2332 static void file_add_recent(struct gui *ui, const gchar *uri)
2336 memset(&grd, 0, sizeof(grd));
2337 grd.display_name = strdup("gfio");
2338 grd.description = strdup("Fio job file");
2339 grd.mime_type = strdup(GFIO_MIME);
2340 grd.app_name = strdup(g_get_application_name());
2341 grd.app_exec = strdup("gfio %f/%u");
2343 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2346 static gchar *get_filename_from_uri(const gchar *uri)
2348 if (strncmp(uri, "file://", 7))
2351 return strdup(uri + 7);
2354 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2357 struct fio_client *client;
2360 filename = get_filename_from_uri(uri);
2362 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2363 ge->job_files[ge->nr_job_files] = strdup(filename);
2366 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2370 error = g_error_new(g_quark_from_string("fio"), 1,
2371 "Failed to add client %s", host);
2372 report_error(error);
2373 g_error_free(error);
2377 gfio_client_added(ge, client);
2378 file_add_recent(ge->ui, uri);
2382 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2384 int port, type, server_start;
2385 struct gui_entry *ge;
2388 int ret, ge_is_new = 0;
2391 * Creates new tab if current tab is the main window, or the
2392 * current tab already has a client.
2394 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2395 ge = get_ge_from_page(cur_page, &ge_is_new);
2397 ge = get_new_ge_with_tab("Untitled");
2401 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2403 if (get_connection_details(&host, &port, &type, &server_start)) {
2405 gtk_widget_destroy(ge->vbox);
2410 ret = do_file_open(ge, uri, host, type, port);
2416 gfio_start_server();
2419 gtk_widget_destroy(ge->vbox);
2425 static void recent_open(GtkAction *action, gpointer data)
2427 struct gui *ui = (struct gui *) data;
2428 GtkRecentInfo *info;
2431 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2432 uri = gtk_recent_info_get_uri(info);
2434 do_file_open_with_tab(ui, uri);
2437 static void file_open(GtkWidget *w, gpointer data)
2439 struct gui *ui = data;
2441 GSList *filenames, *fn_glist;
2442 GtkFileFilter *filter;
2444 dialog = gtk_file_chooser_dialog_new("Open File",
2445 GTK_WINDOW(ui->window),
2446 GTK_FILE_CHOOSER_ACTION_OPEN,
2447 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2448 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2450 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2452 filter = gtk_file_filter_new();
2453 gtk_file_filter_add_pattern(filter, "*.fio");
2454 gtk_file_filter_add_pattern(filter, "*.job");
2455 gtk_file_filter_add_pattern(filter, "*.ini");
2456 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2457 gtk_file_filter_set_name(filter, "Fio job file");
2458 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2460 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2461 gtk_widget_destroy(dialog);
2465 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2467 gtk_widget_destroy(dialog);
2469 filenames = fn_glist;
2470 while (filenames != NULL) {
2471 if (do_file_open_with_tab(ui, filenames->data))
2473 filenames = g_slist_next(filenames);
2476 g_slist_free(fn_glist);
2479 static void file_save(GtkWidget *w, gpointer data)
2481 struct gui *ui = data;
2484 dialog = gtk_file_chooser_dialog_new("Save File",
2485 GTK_WINDOW(ui->window),
2486 GTK_FILE_CHOOSER_ACTION_SAVE,
2487 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2488 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2491 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2492 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2494 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2497 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2498 // save_job_file(filename);
2501 gtk_widget_destroy(dialog);
2504 static void view_log_destroy(GtkWidget *w, gpointer data)
2506 struct gui *ui = (struct gui *) data;
2508 gtk_widget_ref(ui->log_tree);
2509 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2510 gtk_widget_destroy(w);
2511 ui->log_view = NULL;
2514 static void view_log(GtkWidget *w, gpointer data)
2516 GtkWidget *win, *scroll, *vbox, *box;
2517 struct gui *ui = (struct gui *) data;
2522 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2523 gtk_window_set_title(GTK_WINDOW(win), "Log");
2524 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2526 scroll = gtk_scrolled_window_new(NULL, NULL);
2528 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2530 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2532 box = gtk_hbox_new(TRUE, 0);
2533 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2534 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2535 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2537 vbox = gtk_vbox_new(TRUE, 5);
2538 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2540 gtk_container_add(GTK_CONTAINER(win), vbox);
2541 gtk_widget_show_all(win);
2544 static void connect_job_entry(GtkWidget *w, gpointer data)
2546 struct gui *ui = (struct gui *) data;
2547 struct gui_entry *ge;
2549 ge = get_ge_from_cur_tab(ui);
2551 connect_clicked(w, ge);
2554 static void send_job_entry(GtkWidget *w, gpointer data)
2556 struct gui *ui = (struct gui *) data;
2557 struct gui_entry *ge;
2559 ge = get_ge_from_cur_tab(ui);
2561 send_clicked(w, ge);
2565 static void edit_job_entry(GtkWidget *w, gpointer data)
2569 static void start_job_entry(GtkWidget *w, gpointer data)
2571 struct gui *ui = (struct gui *) data;
2572 struct gui_entry *ge;
2574 ge = get_ge_from_cur_tab(ui);
2576 start_job_clicked(w, ge);
2579 static void view_results(GtkWidget *w, gpointer data)
2581 struct gui *ui = (struct gui *) data;
2582 struct gfio_client *gc;
2583 struct gui_entry *ge;
2585 ge = get_ge_from_cur_tab(ui);
2589 if (ge->results_window)
2593 if (gc && gc->nr_results)
2594 gfio_display_end_results(gc);
2598 static void __update_graph_limits(struct gfio_graphs *g)
2600 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2601 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2604 static void update_graph_limits(void)
2606 struct flist_head *entry;
2607 struct gui_entry *ge;
2609 __update_graph_limits(&main_ui.graphs);
2611 flist_for_each(entry, &main_ui.list) {
2612 ge = flist_entry(entry, struct gui_entry, list);
2613 __update_graph_limits(&ge->graphs);
2617 static void preferences(GtkWidget *w, gpointer data)
2619 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2620 GtkWidget *hbox, *spin, *entry, *spin_int;
2623 dialog = gtk_dialog_new_with_buttons("Preferences",
2624 GTK_WINDOW(main_ui.window),
2625 GTK_DIALOG_DESTROY_WITH_PARENT,
2626 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2627 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2630 frame = gtk_frame_new("Graphing");
2631 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2632 vbox = gtk_vbox_new(FALSE, 6);
2633 gtk_container_add(GTK_CONTAINER(frame), vbox);
2635 hbox = gtk_hbox_new(FALSE, 5);
2636 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2637 entry = gtk_label_new("Font face to use for graph labels");
2638 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2640 font = gtk_font_button_new();
2641 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2643 box = gtk_vbox_new(FALSE, 6);
2644 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2646 hbox = gtk_hbox_new(FALSE, 5);
2647 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2648 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2649 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2651 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2653 box = gtk_vbox_new(FALSE, 6);
2654 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2656 hbox = gtk_hbox_new(FALSE, 5);
2657 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2658 entry = gtk_label_new("Client ETA request interval (msec)");
2659 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2661 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2662 frame = gtk_frame_new("Debug logging");
2663 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2664 vbox = gtk_vbox_new(FALSE, 6);
2665 gtk_container_add(GTK_CONTAINER(frame), vbox);
2667 box = gtk_hbox_new(FALSE, 6);
2668 gtk_container_add(GTK_CONTAINER(vbox), box);
2670 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2672 for (i = 0; i < FD_DEBUG_MAX; i++) {
2674 box = gtk_hbox_new(FALSE, 6);
2675 gtk_container_add(GTK_CONTAINER(vbox), box);
2679 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2680 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2681 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2684 gtk_widget_show_all(dialog);
2686 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2687 gtk_widget_destroy(dialog);
2691 for (i = 0; i < FD_DEBUG_MAX; i++) {
2694 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2696 fio_debug |= (1UL << i);
2699 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2700 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2701 update_graph_limits();
2702 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2704 gtk_widget_destroy(dialog);
2707 static void about_dialog(GtkWidget *w, gpointer data)
2709 const char *authors[] = {
2710 "Jens Axboe <axboe@kernel.dk>",
2711 "Stephen Carmeron <stephenmcameron@gmail.com>",
2714 const char *license[] = {
2715 "Fio is free software; you can redistribute it and/or modify "
2716 "it under the terms of the GNU General Public License as published by "
2717 "the Free Software Foundation; either version 2 of the License, or "
2718 "(at your option) any later version.\n",
2719 "Fio is distributed in the hope that it will be useful, "
2720 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2721 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2722 "GNU General Public License for more details.\n",
2723 "You should have received a copy of the GNU General Public License "
2724 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2725 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2727 char *license_trans;
2729 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2730 license[2], "\n", NULL);
2732 gtk_show_about_dialog(NULL,
2733 "program-name", "gfio",
2734 "comments", "Gtk2 UI for fio",
2735 "license", license_trans,
2736 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2738 "version", fio_version_string,
2739 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2740 "logo-icon-name", "fio",
2742 "wrap-license", TRUE,
2745 g_free(license_trans);
2748 static GtkActionEntry menu_items[] = {
2749 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2750 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2751 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2752 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2753 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2754 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2755 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2756 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2757 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2758 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2759 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2760 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2761 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2762 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2763 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2764 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2765 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2767 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2769 static const gchar *ui_string = " \
2771 <menubar name=\"MainMenu\"> \
2772 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2773 <menuitem name=\"New\" action=\"NewFile\" /> \
2774 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2775 <separator name=\"Separator1\"/> \
2776 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2777 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2778 <separator name=\"Separator2\"/> \
2779 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2780 <separator name=\"Separator3\"/> \
2781 <placeholder name=\"FileRecentFiles\"/> \
2782 <separator name=\"Separator4\"/> \
2783 <menuitem name=\"Quit\" action=\"Quit\" /> \
2785 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2786 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2787 <separator name=\"Separator5\"/> \
2788 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2789 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2790 <separator name=\"Separator6\"/> \
2791 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2793 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2794 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2795 <separator name=\"Separator7\"/> \
2796 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2798 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2799 <menuitem name=\"About\" action=\"About\" /> \
2805 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2808 GtkActionGroup *action_group;
2811 action_group = gtk_action_group_new("Menu");
2812 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2814 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2815 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2817 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2819 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2822 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2823 GtkWidget *vbox, GtkUIManager *ui_manager)
2825 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2828 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2830 struct gui_entry *ge = (struct gui_entry *) data;
2833 index = gtk_combo_box_get_active(box);
2835 multitext_set_entry(&ge->eta.iotype, index);
2836 multitext_set_entry(&ge->eta.ioengine, index);
2837 multitext_set_entry(&ge->eta.iodepth, index);
2840 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2842 struct gui_entry *ge = (struct gui_entry *) data;
2844 multitext_free(&ge->eta.iotype);
2845 multitext_free(&ge->eta.ioengine);
2846 multitext_free(&ge->eta.iodepth);
2849 static GtkWidget *new_client_page(struct gui_entry *ge)
2851 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2852 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2854 main_vbox = gtk_vbox_new(FALSE, 3);
2856 top_align = gtk_alignment_new(0, 0, 1, 0);
2857 top_vbox = gtk_vbox_new(FALSE, 3);
2858 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2859 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2861 probe = gtk_frame_new("Job");
2862 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2863 probe_frame = gtk_vbox_new(FALSE, 3);
2864 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2866 probe_box = gtk_hbox_new(FALSE, 3);
2867 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2868 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2869 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2870 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2871 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2873 probe_box = gtk_hbox_new(FALSE, 3);
2874 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2876 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2877 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2878 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2879 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2880 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2881 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2882 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2883 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2885 probe_box = gtk_hbox_new(FALSE, 3);
2886 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2887 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2888 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2889 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2890 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2893 * Only add this if we have a commit rate
2896 probe_box = gtk_hbox_new(FALSE, 3);
2897 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2899 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2900 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2902 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2903 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2907 * Set up a drawing area and IOPS and bandwidth graphs
2909 ge->graphs.drawing_area = gtk_drawing_area_new();
2910 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2911 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2912 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2913 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2914 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2915 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2916 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2917 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2918 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2919 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2920 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2921 ge->graphs.drawing_area);
2922 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2924 setup_graphs(&ge->graphs);
2927 * Set up alignments for widgets at the bottom of ui,
2928 * align bottom left, expand horizontally but not vertically
2930 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2931 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2932 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2933 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2935 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2938 * Set up thread status progress bar
2940 ge->thread_status_pb = gtk_progress_bar_new();
2941 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2942 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2943 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2949 static GtkWidget *new_main_page(struct gui *ui)
2951 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2952 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2954 main_vbox = gtk_vbox_new(FALSE, 3);
2957 * Set up alignments for widgets at the top of ui,
2958 * align top left, expand horizontally but not vertically
2960 top_align = gtk_alignment_new(0, 0, 1, 0);
2961 top_vbox = gtk_vbox_new(FALSE, 0);
2962 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2963 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2965 probe = gtk_frame_new("Run statistics");
2966 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2967 probe_frame = gtk_vbox_new(FALSE, 3);
2968 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2970 probe_box = gtk_hbox_new(FALSE, 3);
2971 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2972 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2973 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2974 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2975 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2976 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2979 * Only add this if we have a commit rate
2982 probe_box = gtk_hbox_new(FALSE, 3);
2983 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2985 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2986 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2988 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2989 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2993 * Set up a drawing area and IOPS and bandwidth graphs
2995 ui->graphs.drawing_area = gtk_drawing_area_new();
2996 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2997 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2998 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2999 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3000 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3001 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3002 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3003 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3004 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3005 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3006 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3007 ui->graphs.drawing_area);
3008 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3011 setup_graphs(&ui->graphs);
3014 * Set up alignments for widgets at the bottom of ui,
3015 * align bottom left, expand horizontally but not vertically
3017 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3018 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3019 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3020 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3023 * Set up thread status progress bar
3025 ui->thread_status_pb = gtk_progress_bar_new();
3026 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3027 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3028 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3033 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3034 guint page, gpointer data)
3037 struct gui *ui = (struct gui *) data;
3038 struct gui_entry *ge;
3041 set_job_menu_visible(ui, 0);
3042 set_view_results_visible(ui, 0);
3046 set_job_menu_visible(ui, 1);
3047 ge = get_ge_from_page(page, NULL);
3049 update_button_states(ui, ge);
3054 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3056 time_t time_a = gtk_recent_info_get_visited(a);
3057 time_t time_b = gtk_recent_info_get_visited(b);
3059 return time_b - time_a;
3062 static void add_recent_file_items(struct gui *ui)
3064 const gchar *gfio = g_get_application_name();
3065 GList *items, *item;
3068 if (ui->recent_ui_id) {
3069 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3070 gtk_ui_manager_ensure_update(ui->uimanager);
3072 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3074 if (ui->actiongroup) {
3075 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3076 g_object_unref(ui->actiongroup);
3078 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3080 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3082 items = gtk_recent_manager_get_items(ui->recentmanager);
3083 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3085 for (item = items; item && item->data; item = g_list_next(item)) {
3086 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3091 if (!gtk_recent_info_has_application(info, gfio))
3095 * We only support local files for now
3097 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3100 action_name = g_strdup_printf("RecentFile%u", i++);
3101 label = gtk_recent_info_get_display_name(info);
3103 action = g_object_new(GTK_TYPE_ACTION,
3104 "name", action_name,
3105 "label", label, NULL);
3107 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3108 gtk_recent_info_ref(info),
3109 (GDestroyNotify) gtk_recent_info_unref);
3112 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3114 gtk_action_group_add_action(ui->actiongroup, action);
3115 g_object_unref(action);
3117 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3118 "/MainMenu/FileMenu/FileRecentFiles",
3120 GTK_UI_MANAGER_MENUITEM, FALSE);
3122 g_free(action_name);
3128 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3132 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3133 gint x, gint y, GtkSelectionData *data,
3134 guint info, guint time)
3136 struct gui *ui = &main_ui;
3141 source = gtk_drag_get_source_widget(ctx);
3142 if (source && widget == gtk_widget_get_toplevel(source)) {
3143 gtk_drag_finish(ctx, FALSE, FALSE, time);
3147 uris = gtk_selection_data_get_uris(data);
3149 gtk_drag_finish(ctx, FALSE, FALSE, time);
3155 if (do_file_open_with_tab(ui, uris[i]))
3160 gtk_drag_finish(ctx, TRUE, FALSE, time);
3164 static void init_ui(int *argc, char **argv[], struct gui *ui)
3166 GtkSettings *settings;
3169 /* Magical g*thread incantation, you just need this thread stuff.
3170 * Without it, the update that happens in gfio_update_thread_status
3171 * doesn't really happen in a timely fashion, you need expose events
3173 if (!g_thread_supported())
3174 g_thread_init(NULL);
3177 gtk_init(argc, argv);
3178 settings = gtk_settings_get_default();
3179 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3181 gdk_color_parse("white", &white);
3183 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3184 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3185 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3187 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3188 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3190 ui->vbox = gtk_vbox_new(FALSE, 0);
3191 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3193 ui->uimanager = gtk_ui_manager_new();
3194 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3195 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3197 ui->recentmanager = gtk_recent_manager_get_default();
3198 add_recent_file_items(ui);
3200 ui->notebook = gtk_notebook_new();
3201 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3202 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3203 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3204 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3206 vbox = new_main_page(ui);
3207 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3208 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3209 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3211 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3213 gfio_ui_setup_log(ui);
3215 gtk_widget_show_all(ui->window);
3218 int main(int argc, char *argv[], char *envp[])
3220 if (initialize_fio(envp))
3222 if (fio_init_options())
3225 memset(&main_ui, 0, sizeof(main_ui));
3226 INIT_FLIST_HEAD(&main_ui.list);
3228 init_ui(&argc, &argv, &main_ui);
3230 gdk_threads_enter();
3232 gdk_threads_leave();