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 graph *clat_graph;
176 struct graph *lat_bucket_graph;
178 struct gfio_client *client;
184 struct group_run_stats gs;
185 struct thread_stat ts;
189 struct gui_entry *ge;
190 struct fio_client *client;
191 GtkWidget *results_widget;
192 GtkWidget *disk_util_vbox;
193 GtkWidget *err_entry;
194 unsigned int job_added;
195 struct thread_options o;
197 struct end_results *results;
198 unsigned int nr_results;
201 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
202 static void gfio_update_thread_status_all(char *status_message, double perc);
203 void report_error(GError *error);
205 static struct graph *setup_iops_graph(void)
209 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
210 graph_title(g, "IOPS (IOs/sec)");
211 graph_x_title(g, "Time (secs)");
212 graph_add_label(g, "Read IOPS");
213 graph_add_label(g, "Write IOPS");
214 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
215 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
216 line_graph_set_data_count_limit(g, gfio_graph_limit);
217 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
221 static struct graph *setup_bandwidth_graph(void)
225 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
226 graph_title(g, "Bandwidth (bytes/sec)");
227 graph_x_title(g, "Time (secs)");
228 graph_add_label(g, "Read Bandwidth");
229 graph_add_label(g, "Write Bandwidth");
230 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
231 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
232 graph_set_base_offset(g, 1);
233 line_graph_set_data_count_limit(g, 100);
234 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
238 static void setup_graphs(struct gfio_graphs *g)
240 g->iops_graph = setup_iops_graph();
241 g->bandwidth_graph = setup_bandwidth_graph();
244 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
246 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
247 mt->text[mt->max_text] = strdup(text);
251 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
253 if (index >= mt->max_text)
255 if (!mt->text || !mt->text[index])
258 mt->cur_text = index;
259 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
262 static void multitext_update_entry(struct multitext_widget *mt,
263 unsigned int index, const char *text)
269 free(mt->text[index]);
271 mt->text[index] = strdup(text);
272 if (mt->cur_text == index)
273 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
276 static void multitext_free(struct multitext_widget *mt)
280 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
282 for (i = 0; i < mt->max_text; i++) {
292 static void clear_ge_ui_info(struct gui_entry *ge)
294 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
295 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
296 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
297 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
299 /* should we empty it... */
300 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
302 multitext_update_entry(&ge->eta.iotype, 0, "");
303 multitext_update_entry(&ge->eta.ioengine, 0, "");
304 multitext_update_entry(&ge->eta.iodepth, 0, "");
305 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
306 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
309 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
310 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
313 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
315 GtkWidget *entry, *frame;
317 frame = gtk_frame_new(label);
318 entry = gtk_combo_box_new_text();
319 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
320 gtk_container_add(GTK_CONTAINER(frame), entry);
325 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
327 GtkWidget *entry, *frame;
329 frame = gtk_frame_new(label);
330 entry = gtk_entry_new();
331 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
332 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
333 gtk_container_add(GTK_CONTAINER(frame), entry);
338 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
340 GtkWidget *label_widget;
343 frame = gtk_frame_new(label);
344 label_widget = gtk_label_new(NULL);
345 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
346 gtk_container_add(GTK_CONTAINER(frame), label_widget);
351 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
353 GtkWidget *button, *box;
355 box = gtk_hbox_new(FALSE, 3);
356 gtk_container_add(GTK_CONTAINER(hbox), box);
358 button = gtk_spin_button_new_with_range(min, max, 1.0);
359 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
361 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
362 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
367 static void label_set_int_value(GtkWidget *entry, unsigned int val)
371 sprintf(tmp, "%u", val);
372 gtk_label_set_text(GTK_LABEL(entry), tmp);
375 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
379 sprintf(tmp, "%u", val);
380 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
383 static void show_info_dialog(struct gui *ui, const char *title,
386 GtkWidget *dialog, *content, *label;
388 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
389 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
390 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
392 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
393 label = gtk_label_new(message);
394 gtk_container_add(GTK_CONTAINER(content), label);
395 gtk_widget_show_all(dialog);
396 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
397 gtk_dialog_run(GTK_DIALOG(dialog));
398 gtk_widget_destroy(dialog);
401 static void set_menu_entry_text(struct gui *ui, const char *path,
406 w = gtk_ui_manager_get_widget(ui->uimanager, path);
408 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
410 fprintf(stderr, "gfio: can't find path %s\n", path);
414 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
418 w = gtk_ui_manager_get_widget(ui->uimanager, path);
420 gtk_widget_set_sensitive(w, show);
422 fprintf(stderr, "gfio: can't find path %s\n", path);
425 static void set_job_menu_visible(struct gui *ui, int visible)
427 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
430 static void set_view_results_visible(struct gui *ui, int visible)
432 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
436 * Update sensitivity of job buttons and job menu items, based on the
437 * state of the client.
439 static void update_button_states(struct gui *ui, struct gui_entry *ge)
441 unsigned int connect_state, send_state, start_state, edit_state;
442 const char *connect_str = NULL;
448 sprintf(tmp, "Bad client state: %u\n", ge->state);
449 show_info_dialog(ui, "Error", tmp);
450 /* fall through to new state */
456 connect_str = "Connect";
460 case GE_STATE_CONNECTED:
463 connect_str = "Disconnect";
467 case GE_STATE_JOB_SENT:
470 connect_str = "Disconnect";
474 case GE_STATE_JOB_STARTED:
477 connect_str = "Disconnect";
481 case GE_STATE_JOB_RUNNING:
484 connect_str = "Disconnect";
488 case GE_STATE_JOB_DONE:
491 connect_str = "Connect";
497 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
498 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
499 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
500 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
502 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
503 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
505 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
506 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
507 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
509 if (ge->client && ge->client->nr_results)
510 set_view_results_visible(ui, 1);
512 set_view_results_visible(ui, 0);
515 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
518 update_button_states(ge->ui, ge);
522 #define ALIGN_RIGHT 2
526 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
528 GtkCellRenderer *renderer;
529 GtkTreeViewColumn *col;
530 double xalign = 0.0; /* left as default */
531 PangoAlignment align;
534 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
535 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
537 visible = !(flags & INVISIBLE);
539 renderer = gtk_cell_renderer_text_new();
540 col = gtk_tree_view_column_new();
542 gtk_tree_view_column_set_title(col, title);
543 if (!(flags & UNSORTABLE))
544 gtk_tree_view_column_set_sort_column_id(col, index);
545 gtk_tree_view_column_set_resizable(col, TRUE);
546 gtk_tree_view_column_pack_start(col, renderer, TRUE);
547 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
548 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
550 case PANGO_ALIGN_LEFT:
553 case PANGO_ALIGN_CENTER:
556 case PANGO_ALIGN_RIGHT:
560 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
561 gtk_tree_view_column_set_visible(col, visible);
562 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
566 static void gfio_ui_setup_log(struct gui *ui)
568 GtkTreeSelection *selection;
570 GtkWidget *tree_view;
572 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
574 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
575 gtk_widget_set_can_focus(tree_view, FALSE);
577 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
578 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
579 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
580 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
582 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
583 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
584 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
585 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
587 ui->log_model = model;
588 ui->log_tree = tree_view;
591 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
594 double xdim, double ydim)
599 g = graph_new(xdim, ydim, gfio_graph_font);
600 graph_title(g, title);
601 graph_x_title(g, "Percentile");
603 for (i = 0; i < len; i++) {
606 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
607 graph_add_label(g, fbuf);
608 graph_add_data(g, fbuf, (double) ovals[i]);
614 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
620 GType types[FIO_IO_U_LIST_MAX_LEN];
621 GtkWidget *tree_view;
622 GtkTreeSelection *selection;
627 for (i = 0; i < len; i++)
628 types[i] = G_TYPE_INT;
630 model = gtk_list_store_newv(len, types);
632 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
633 gtk_widget_set_can_focus(tree_view, FALSE);
635 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
636 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
638 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
639 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
641 for (i = 0; i < len; i++) {
644 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
645 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
648 gtk_list_store_append(model, &iter);
650 for (i = 0; i < len; i++) {
652 ovals[i] = (ovals[i] + 999) / 1000;
653 gtk_list_store_set(model, &iter, i, ovals[i], -1);
659 static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
664 cr = gdk_cairo_create(w->window);
666 if (graph_has_tooltips(g)) {
667 g_object_set(w, "has-tooltip", TRUE, NULL);
668 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
671 cairo_set_source_rgb(cr, 0, 0, 0);
672 bar_graph_draw(g, cr);
678 static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
681 struct graph *g = data;
683 graph_set_size(g, w->allocation.width, w->allocation.height);
684 graph_set_size(g, w->allocation.width, w->allocation.height);
685 graph_set_position(g, 0, 0);
689 static void gfio_show_clat_percentiles(struct gfio_client *gc,
690 GtkWidget *vbox, struct thread_stat *ts,
693 unsigned int *io_u_plat = ts->io_u_plat[ddir];
694 unsigned long nr = ts->clat_stat[ddir].samples;
695 fio_fp64_t *plist = ts->percentile_list;
696 unsigned int *ovals, len, minv, maxv, scale_down;
698 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
699 struct gui_entry *ge = gc->ge;
702 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
707 * We default to usecs, but if the value range is such that we
708 * should scale down to msecs, do that.
710 if (minv > 2000 && maxv > 99999) {
718 sprintf(tmp, "Completion percentiles (%s)", base);
719 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
720 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
722 frame = gtk_frame_new(tmp);
723 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
725 completion_vbox = gtk_vbox_new(FALSE, 3);
726 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
727 hbox = gtk_hbox_new(FALSE, 3);
728 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
729 drawing_area = gtk_drawing_area_new();
730 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
731 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
732 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
733 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
734 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
736 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
742 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
743 unsigned long max, double mean, double dev)
745 const char *base = "(usec)";
746 GtkWidget *hbox, *label, *frame;
750 if (!usec_to_msec(&min, &max, &mean, &dev))
753 minp = num2str(min, 6, 1, 0);
754 maxp = num2str(max, 6, 1, 0);
756 sprintf(tmp, "%s %s", name, base);
757 frame = gtk_frame_new(tmp);
758 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
760 hbox = gtk_hbox_new(FALSE, 3);
761 gtk_container_add(GTK_CONTAINER(frame), hbox);
763 label = new_info_label_in_frame(hbox, "Minimum");
764 gtk_label_set_text(GTK_LABEL(label), minp);
765 label = new_info_label_in_frame(hbox, "Maximum");
766 gtk_label_set_text(GTK_LABEL(label), maxp);
767 label = new_info_label_in_frame(hbox, "Average");
768 sprintf(tmp, "%5.02f", mean);
769 gtk_label_set_text(GTK_LABEL(label), tmp);
770 label = new_info_label_in_frame(hbox, "Standard deviation");
771 sprintf(tmp, "%5.02f", dev);
772 gtk_label_set_text(GTK_LABEL(label), tmp);
783 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
784 struct group_run_stats *rs,
785 struct thread_stat *ts, int ddir)
787 const char *ddir_label[2] = { "Read", "Write" };
788 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
789 unsigned long min[3], max[3], runt;
790 unsigned long long bw, iops;
791 unsigned int flags = 0;
792 double mean[3], dev[3];
793 char *io_p, *bw_p, *iops_p;
796 if (!ts->runtime[ddir])
799 i2p = is_power_of_2(rs->kb_base);
800 runt = ts->runtime[ddir];
802 bw = (1000 * ts->io_bytes[ddir]) / runt;
803 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
804 bw_p = num2str(bw, 6, 1, i2p);
806 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
807 iops_p = num2str(iops, 6, 1, 0);
809 box = gtk_hbox_new(FALSE, 3);
810 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
812 frame = gtk_frame_new(ddir_label[ddir]);
813 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
815 main_vbox = gtk_vbox_new(FALSE, 3);
816 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
818 box = gtk_hbox_new(FALSE, 3);
819 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
821 label = new_info_label_in_frame(box, "IO");
822 gtk_label_set_text(GTK_LABEL(label), io_p);
823 label = new_info_label_in_frame(box, "Bandwidth");
824 gtk_label_set_text(GTK_LABEL(label), bw_p);
825 label = new_info_label_in_frame(box, "IOPS");
826 gtk_label_set_text(GTK_LABEL(label), iops_p);
827 label = new_info_label_in_frame(box, "Runtime (msec)");
828 label_set_int_value(label, ts->runtime[ddir]);
830 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
831 double p_of_agg = 100.0;
832 const char *bw_str = "KB";
836 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
837 if (p_of_agg > 100.0)
841 if (mean[0] > 999999.9) {
849 sprintf(tmp, "Bandwidth (%s)", bw_str);
850 frame = gtk_frame_new(tmp);
851 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
853 box = gtk_hbox_new(FALSE, 3);
854 gtk_container_add(GTK_CONTAINER(frame), box);
856 label = new_info_label_in_frame(box, "Minimum");
857 label_set_int_value(label, min[0]);
858 label = new_info_label_in_frame(box, "Maximum");
859 label_set_int_value(label, max[0]);
860 label = new_info_label_in_frame(box, "Percentage of jobs");
861 sprintf(tmp, "%3.2f%%", p_of_agg);
862 gtk_label_set_text(GTK_LABEL(label), tmp);
863 label = new_info_label_in_frame(box, "Average");
864 sprintf(tmp, "%5.02f", mean[0]);
865 gtk_label_set_text(GTK_LABEL(label), tmp);
866 label = new_info_label_in_frame(box, "Standard deviation");
867 sprintf(tmp, "%5.02f", dev[0]);
868 gtk_label_set_text(GTK_LABEL(label), tmp);
871 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
873 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
875 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
879 frame = gtk_frame_new("Latency");
880 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
882 vbox = gtk_vbox_new(FALSE, 3);
883 gtk_container_add(GTK_CONTAINER(frame), vbox);
885 if (flags & GFIO_SLAT)
886 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
887 if (flags & GFIO_CLAT)
888 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
889 if (flags & GFIO_LAT)
890 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
893 if (ts->clat_percentiles)
894 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
901 static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
904 double xdim, double ydim)
909 g = graph_new(xdim, ydim, gfio_graph_font);
910 graph_title(g, title);
911 graph_x_title(g, "Buckets");
913 for (i = 0; i < len; i++) {
914 graph_add_label(g, labels[i]);
915 graph_add_data(g, labels[i], lat[i]);
921 static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
924 GtkWidget *tree_view;
925 GtkTreeSelection *selection;
931 types = malloc(num * sizeof(GType));
933 for (i = 0; i < num; i++)
934 types[i] = G_TYPE_STRING;
936 model = gtk_list_store_newv(num, 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 < num; i++)
950 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
952 gtk_list_store_append(model, &iter);
954 for (i = 0; i < num; i++) {
958 sprintf(fbuf, "0.00");
960 sprintf(fbuf, "%3.2f%%", lat[i]);
962 gtk_list_store_set(model, &iter, i, fbuf, -1);
968 static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
969 struct thread_stat *ts)
971 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
972 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
973 "250u", "500u", "750u", "1m", "2m",
974 "4m", "10m", "20m", "50m", "100m",
975 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
977 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
978 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
979 struct gui_entry *ge = gc->ge;
981 stat_calc_lat_u(ts, io_u_lat);
982 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
985 * Found out which first bucket has entries, and which last bucket
988 for (i = 0; i < total; i++) {
989 if (io_u_lat[i] == 0.00)
1003 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1004 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
1006 frame = gtk_frame_new("Latency buckets");
1007 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1009 completion_vbox = gtk_vbox_new(FALSE, 3);
1010 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1011 hbox = gtk_hbox_new(FALSE, 3);
1012 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1014 drawing_area = gtk_drawing_area_new();
1015 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1016 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1017 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1018 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1019 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1021 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
1024 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1026 GtkWidget *box, *frame, *entry;
1027 double usr_cpu, sys_cpu;
1028 unsigned long runtime;
1031 runtime = ts->total_run_time;
1033 double runt = (double) runtime;
1035 usr_cpu = (double) ts->usr_time * 100 / runt;
1036 sys_cpu = (double) ts->sys_time * 100 / runt;
1042 frame = gtk_frame_new("OS resources");
1043 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1045 box = gtk_hbox_new(FALSE, 3);
1046 gtk_container_add(GTK_CONTAINER(frame), box);
1048 entry = new_info_entry_in_frame(box, "User CPU");
1049 sprintf(tmp, "%3.2f%%", usr_cpu);
1050 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1051 entry = new_info_entry_in_frame(box, "System CPU");
1052 sprintf(tmp, "%3.2f%%", sys_cpu);
1053 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1054 entry = new_info_entry_in_frame(box, "Context switches");
1055 entry_set_int_value(entry, ts->ctx);
1056 entry = new_info_entry_in_frame(box, "Major faults");
1057 entry_set_int_value(entry, ts->majf);
1058 entry = new_info_entry_in_frame(box, "Minor faults");
1059 entry_set_int_value(entry, ts->minf);
1061 static void gfio_add_sc_depths_tree(GtkListStore *model,
1062 struct thread_stat *ts, unsigned int len,
1065 double io_u_dist[FIO_IO_U_MAP_NR];
1067 /* Bits 0, and 3-8 */
1068 const int add_mask = 0x1f9;
1072 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1074 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1076 gtk_list_store_append(model, &iter);
1078 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1080 for (i = 1, j = 0; i < len; i++) {
1083 if (!(add_mask & (1UL << (i - 1))))
1084 sprintf(fbuf, "0.0%%");
1086 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1090 gtk_list_store_set(model, &iter, i, fbuf, -1);
1095 static void gfio_add_total_depths_tree(GtkListStore *model,
1096 struct thread_stat *ts, unsigned int len)
1098 double io_u_dist[FIO_IO_U_MAP_NR];
1100 /* Bits 1-6, and 8 */
1101 const int add_mask = 0x17e;
1104 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1106 gtk_list_store_append(model, &iter);
1108 gtk_list_store_set(model, &iter, 0, "Total", -1);
1110 for (i = 1, j = 0; i < len; i++) {
1113 if (!(add_mask & (1UL << (i - 1))))
1114 sprintf(fbuf, "0.0%%");
1116 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1120 gtk_list_store_set(model, &iter, i, fbuf, -1);
1125 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1127 GtkWidget *frame, *box, *tree_view;
1128 GtkTreeSelection *selection;
1129 GtkListStore *model;
1130 GType types[FIO_IO_U_MAP_NR + 1];
1132 #define NR_LABELS 10
1133 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1135 frame = gtk_frame_new("IO depths");
1136 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1138 box = gtk_hbox_new(FALSE, 3);
1139 gtk_container_add(GTK_CONTAINER(frame), box);
1141 for (i = 0; i < NR_LABELS; i++)
1142 types[i] = G_TYPE_STRING;
1144 model = gtk_list_store_newv(NR_LABELS, types);
1146 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1147 gtk_widget_set_can_focus(tree_view, FALSE);
1149 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1150 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1152 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1153 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1155 for (i = 0; i < NR_LABELS; i++)
1156 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1158 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1159 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1160 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1162 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1165 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1167 struct gui_entry *ge = (struct gui_entry *) data;
1169 gtk_widget_destroy(w);
1170 ge->results_window = NULL;
1171 ge->results_notebook = NULL;
1175 static void results_close(GtkWidget *w, gpointer *data)
1177 struct gui_entry *ge = (struct gui_entry *) data;
1179 gtk_widget_destroy(ge->results_window);
1182 static GtkActionEntry results_menu_items[] = {
1183 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1184 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1185 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1187 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1189 static const gchar *results_ui_string = " \
1191 <menubar name=\"MainMenu\"> \
1192 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1193 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1195 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1201 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1203 GtkActionGroup *action_group;
1207 ge->results_uimanager = gtk_ui_manager_new();
1209 action_group = gtk_action_group_new("ResultsMenu");
1210 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1212 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1213 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1215 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1217 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1221 static GtkWidget *get_results_window(struct gui_entry *ge)
1223 GtkWidget *win, *notebook, *vbox;
1225 if (ge->results_window)
1226 return ge->results_notebook;
1228 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1229 gtk_window_set_title(GTK_WINDOW(win), "Results");
1230 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1231 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1232 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1234 vbox = gtk_vbox_new(FALSE, 0);
1235 gtk_container_add(GTK_CONTAINER(win), vbox);
1237 ge->results_menu = get_results_menubar(win, ge);
1238 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1240 notebook = gtk_notebook_new();
1241 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1242 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1243 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1245 ge->results_window = win;
1246 ge->results_notebook = notebook;
1247 return ge->results_notebook;
1250 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1251 struct group_run_stats *rs)
1253 unsigned int nr = gc->nr_results;
1255 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1256 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1257 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1261 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1262 struct thread_stat *ts,
1263 struct group_run_stats *rs)
1265 GtkWidget *box, *vbox, *entry, *scroll;
1267 scroll = gtk_scrolled_window_new(NULL, NULL);
1268 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1269 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1271 vbox = gtk_vbox_new(FALSE, 3);
1273 box = gtk_hbox_new(FALSE, 0);
1274 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1276 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1278 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1280 gc->results_widget = vbox;
1282 entry = new_info_entry_in_frame(box, "Name");
1283 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1284 if (strlen(ts->description)) {
1285 entry = new_info_entry_in_frame(box, "Description");
1286 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1288 entry = new_info_entry_in_frame(box, "Group ID");
1289 entry_set_int_value(entry, ts->groupid);
1290 entry = new_info_entry_in_frame(box, "Jobs");
1291 entry_set_int_value(entry, ts->members);
1292 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1293 entry_set_int_value(entry, ts->error);
1294 entry = new_info_entry_in_frame(box, "PID");
1295 entry_set_int_value(entry, ts->pid);
1297 if (ts->io_bytes[DDIR_READ])
1298 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1299 if (ts->io_bytes[DDIR_WRITE])
1300 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1302 gfio_show_latency_buckets(gc, vbox, ts);
1303 gfio_show_cpu_usage(vbox, ts);
1304 gfio_show_io_depths(vbox, ts);
1307 static void gfio_display_end_results(struct gfio_client *gc)
1309 struct gui_entry *ge = gc->ge;
1310 GtkWidget *res_notebook;
1313 res_notebook = get_results_window(ge);
1315 for (i = 0; i < gc->nr_results; i++) {
1316 struct end_results *e = &gc->results[i];
1318 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1321 gtk_widget_show_all(ge->results_window);
1324 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1325 struct group_run_stats *rs)
1327 struct gfio_client *gc = client->client_data;
1329 gfio_add_end_results(gc, ts, rs);
1331 gdk_threads_enter();
1332 gfio_display_end_results(gc);
1333 gdk_threads_leave();
1336 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1338 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1339 struct gui *ui = &main_ui;
1343 char tmp[64], timebuf[80];
1346 tm = localtime(&sec);
1347 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1348 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1350 gdk_threads_enter();
1352 gtk_list_store_append(ui->log_model, &iter);
1353 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1354 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1355 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1356 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1358 if (p->level == FIO_LOG_ERR)
1359 view_log(NULL, (gpointer) ui);
1361 gdk_threads_leave();
1364 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1366 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1367 struct gfio_client *gc = client->client_data;
1368 GtkWidget *box, *frame, *entry, *vbox;
1372 gdk_threads_enter();
1374 if (!gc->results_widget)
1377 if (!gc->disk_util_vbox) {
1378 frame = gtk_frame_new("Disk utilization");
1379 gtk_box_pack_start(GTK_BOX(gc->results_widget), frame, FALSE, FALSE, 5);
1380 vbox = gtk_vbox_new(FALSE, 3);
1381 gtk_container_add(GTK_CONTAINER(frame), vbox);
1382 gc->disk_util_vbox = vbox;
1385 vbox = gtk_vbox_new(FALSE, 3);
1386 gtk_container_add(GTK_CONTAINER(gc->disk_util_vbox), vbox);
1388 frame = gtk_frame_new((char *) p->dus.name);
1389 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1391 box = gtk_vbox_new(FALSE, 3);
1392 gtk_container_add(GTK_CONTAINER(frame), box);
1394 frame = gtk_frame_new("Read");
1395 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1396 vbox = gtk_hbox_new(TRUE, 3);
1397 gtk_container_add(GTK_CONTAINER(frame), vbox);
1398 entry = new_info_entry_in_frame(vbox, "IOs");
1399 entry_set_int_value(entry, p->dus.ios[0]);
1400 entry = new_info_entry_in_frame(vbox, "Merges");
1401 entry_set_int_value(entry, p->dus.merges[0]);
1402 entry = new_info_entry_in_frame(vbox, "Sectors");
1403 entry_set_int_value(entry, p->dus.sectors[0]);
1404 entry = new_info_entry_in_frame(vbox, "Ticks");
1405 entry_set_int_value(entry, p->dus.ticks[0]);
1407 frame = gtk_frame_new("Write");
1408 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1409 vbox = gtk_hbox_new(TRUE, 3);
1410 gtk_container_add(GTK_CONTAINER(frame), vbox);
1411 entry = new_info_entry_in_frame(vbox, "IOs");
1412 entry_set_int_value(entry, p->dus.ios[1]);
1413 entry = new_info_entry_in_frame(vbox, "Merges");
1414 entry_set_int_value(entry, p->dus.merges[1]);
1415 entry = new_info_entry_in_frame(vbox, "Sectors");
1416 entry_set_int_value(entry, p->dus.sectors[1]);
1417 entry = new_info_entry_in_frame(vbox, "Ticks");
1418 entry_set_int_value(entry, p->dus.ticks[1]);
1420 frame = gtk_frame_new("Shared");
1421 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1422 vbox = gtk_hbox_new(TRUE, 3);
1423 gtk_container_add(GTK_CONTAINER(frame), vbox);
1424 entry = new_info_entry_in_frame(vbox, "IO ticks");
1425 entry_set_int_value(entry, p->dus.io_ticks);
1426 entry = new_info_entry_in_frame(vbox, "Time in queue");
1427 entry_set_int_value(entry, p->dus.time_in_queue);
1431 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1435 sprintf(tmp, "%3.2f%%", util);
1436 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1437 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1439 gtk_widget_show_all(gc->results_widget);
1441 gdk_threads_leave();
1444 extern int sum_stat_clients;
1445 extern struct thread_stat client_ts;
1446 extern struct group_run_stats client_gs;
1448 static int sum_stat_nr;
1450 static void gfio_thread_status_op(struct fio_client *client,
1451 struct fio_net_cmd *cmd)
1453 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1455 gfio_display_ts(client, &p->ts, &p->rs);
1457 if (sum_stat_clients == 1)
1460 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1461 sum_group_stats(&client_gs, &p->rs);
1463 client_ts.members++;
1464 client_ts.groupid = p->ts.groupid;
1466 if (++sum_stat_nr == sum_stat_clients) {
1467 strcpy(client_ts.name, "All clients");
1468 gfio_display_ts(client, &client_ts, &client_gs);
1472 static void gfio_group_stats_op(struct fio_client *client,
1473 struct fio_net_cmd *cmd)
1475 /* We're ignoring group stats for now */
1478 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1481 struct gfio_graphs *g = data;
1483 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1484 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1485 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1486 graph_set_position(g->bandwidth_graph, 0, 0);
1490 static void draw_graph(struct graph *g, cairo_t *cr)
1492 line_graph_draw(g, cr);
1496 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1497 gboolean keyboard_mode, GtkTooltip *tooltip,
1500 struct gfio_graphs *g = data;
1501 const char *text = NULL;
1503 if (graph_contains_xy(g->iops_graph, x, y))
1504 text = graph_find_tooltip(g->iops_graph, x, y);
1505 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1506 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1509 gtk_tooltip_set_text(tooltip, text);
1516 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1518 struct gfio_graphs *g = p;
1521 cr = gdk_cairo_create(w->window);
1523 if (graph_has_tooltips(g->iops_graph) ||
1524 graph_has_tooltips(g->bandwidth_graph)) {
1525 g_object_set(w, "has-tooltip", TRUE, NULL);
1526 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1529 cairo_set_source_rgb(cr, 0, 0, 0);
1530 draw_graph(g->iops_graph, cr);
1531 draw_graph(g->bandwidth_graph, cr);
1538 * Client specific ETA
1540 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1542 struct gfio_client *gc = client->client_data;
1543 struct gui_entry *ge = gc->ge;
1544 static int eta_good;
1551 gdk_threads_enter();
1556 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1557 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1558 eta_to_str(eta_str, je->eta_sec);
1561 sprintf(tmp, "%u", je->nr_running);
1562 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1563 sprintf(tmp, "%u", je->files_open);
1564 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1567 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1568 if (je->m_rate || je->t_rate) {
1571 mr = num2str(je->m_rate, 4, 0, i2p);
1572 tr = num2str(je->t_rate, 4, 0, i2p);
1573 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1574 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1577 } else if (je->m_iops || je->t_iops)
1578 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1580 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1581 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1582 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1583 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1586 if (je->eta_sec != INT_MAX && je->nr_running) {
1590 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1591 strcpy(output, "-.-% done");
1595 sprintf(output, "%3.1f%% done", perc);
1598 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1599 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1601 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1602 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1604 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1605 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1606 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1607 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1609 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1610 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1611 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1612 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1621 char *dst = output + strlen(output);
1623 sprintf(dst, " - %s", eta_str);
1626 gfio_update_thread_status(ge, output, perc);
1627 gdk_threads_leave();
1631 * Update ETA in main window for all clients
1633 static void gfio_update_all_eta(struct jobs_eta *je)
1635 struct gui *ui = &main_ui;
1636 static int eta_good;
1642 gdk_threads_enter();
1647 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1648 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1649 eta_to_str(eta_str, je->eta_sec);
1653 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1654 if (je->m_rate || je->t_rate) {
1657 mr = num2str(je->m_rate, 4, 0, i2p);
1658 tr = num2str(je->t_rate, 4, 0, i2p);
1659 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1660 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1663 } else if (je->m_iops || je->t_iops)
1664 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1666 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1667 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1668 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1669 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1672 entry_set_int_value(ui->eta.jobs, je->nr_running);
1674 if (je->eta_sec != INT_MAX && je->nr_running) {
1678 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1679 strcpy(output, "-.-% done");
1683 sprintf(output, "%3.1f%% done", perc);
1686 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1687 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1689 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1690 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1692 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1693 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1694 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1695 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1697 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1698 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1699 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1700 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1709 char *dst = output + strlen(output);
1711 sprintf(dst, " - %s", eta_str);
1714 gfio_update_thread_status_all(output, perc);
1715 gdk_threads_leave();
1718 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1720 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1721 struct gfio_client *gc = client->client_data;
1722 struct gui_entry *ge = gc->ge;
1723 const char *os, *arch;
1726 os = fio_get_os_string(probe->os);
1730 arch = fio_get_arch_string(probe->arch);
1735 client->name = strdup((char *) probe->hostname);
1737 gdk_threads_enter();
1739 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1740 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1741 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1742 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1743 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1745 gfio_set_state(ge, GE_STATE_CONNECTED);
1747 gdk_threads_leave();
1750 static void gfio_update_thread_status(struct gui_entry *ge,
1751 char *status_message, double perc)
1753 static char message[100];
1754 const char *m = message;
1756 strncpy(message, status_message, sizeof(message) - 1);
1757 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1758 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1759 gtk_widget_queue_draw(main_ui.window);
1762 static void gfio_update_thread_status_all(char *status_message, double perc)
1764 struct gui *ui = &main_ui;
1765 static char message[100];
1766 const char *m = message;
1768 strncpy(message, status_message, sizeof(message) - 1);
1769 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1770 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1771 gtk_widget_queue_draw(ui->window);
1774 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1776 struct gfio_client *gc = client->client_data;
1778 gdk_threads_enter();
1779 gfio_set_state(gc->ge, GE_STATE_NEW);
1780 gdk_threads_leave();
1783 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1785 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1786 struct gfio_client *gc = client->client_data;
1787 struct thread_options *o = &gc->o;
1788 struct gui_entry *ge = gc->ge;
1791 convert_thread_options_to_cpu(o, &p->top);
1793 gdk_threads_enter();
1795 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1797 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1798 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1800 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1801 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1803 sprintf(tmp, "%u", o->iodepth);
1804 multitext_add_entry(&ge->eta.iodepth, tmp);
1806 multitext_set_entry(&ge->eta.iotype, 0);
1807 multitext_set_entry(&ge->eta.ioengine, 0);
1808 multitext_set_entry(&ge->eta.iodepth, 0);
1812 gfio_set_state(ge, GE_STATE_JOB_SENT);
1814 gdk_threads_leave();
1817 static void gfio_client_timed_out(struct fio_client *client)
1819 struct gfio_client *gc = client->client_data;
1822 gdk_threads_enter();
1824 gfio_set_state(gc->ge, GE_STATE_NEW);
1825 clear_ge_ui_info(gc->ge);
1827 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1828 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1830 gdk_threads_leave();
1833 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1835 struct gfio_client *gc = client->client_data;
1837 gdk_threads_enter();
1839 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1842 entry_set_int_value(gc->err_entry, client->error);
1844 gdk_threads_leave();
1847 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1849 struct gfio_client *gc = client->client_data;
1851 gdk_threads_enter();
1852 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1853 gdk_threads_leave();
1856 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1858 struct gfio_client *gc = client->client_data;
1860 gdk_threads_enter();
1861 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1862 gdk_threads_leave();
1865 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1867 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1871 struct client_ops gfio_client_ops = {
1872 .text = gfio_text_op,
1873 .disk_util = gfio_disk_util_op,
1874 .thread_status = gfio_thread_status_op,
1875 .group_stats = gfio_group_stats_op,
1876 .jobs_eta = gfio_update_client_eta,
1877 .eta = gfio_update_all_eta,
1878 .probe = gfio_probe_op,
1879 .quit = gfio_quit_op,
1880 .add_job = gfio_add_job_op,
1881 .timed_out = gfio_client_timed_out,
1882 .stop = gfio_client_stop,
1883 .start = gfio_client_start,
1884 .job_start = gfio_client_job_start,
1885 .iolog = gfio_client_iolog,
1886 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1887 .stay_connected = 1,
1888 .client_type = FIO_CLIENT_TYPE_GUI,
1892 * FIXME: need more handling here
1894 static void ge_destroy(struct gui_entry *ge)
1896 struct gfio_client *gc = ge->client;
1898 if (gc && gc->client) {
1899 if (ge->state >= GE_STATE_CONNECTED)
1900 fio_client_terminate(gc->client);
1902 fio_put_client(gc->client);
1905 flist_del(&ge->list);
1909 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1913 static void gfio_quit(struct gui *ui)
1915 struct gui_entry *ge;
1917 while (!flist_empty(&ui->list)) {
1918 ge = flist_entry(ui->list.next, struct gui_entry, list);
1925 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1926 __attribute__((unused)) gpointer data)
1931 static void *job_thread(void *arg)
1933 struct gui *ui = arg;
1935 ui->handler_running = 1;
1936 fio_handle_clients(&gfio_client_ops);
1937 ui->handler_running = 0;
1941 static int send_job_files(struct gui_entry *ge)
1943 struct gfio_client *gc = ge->client;
1946 for (i = 0; i < ge->nr_job_files; i++) {
1947 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1951 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1952 report_error(error);
1953 g_error_free(error);
1958 free(ge->job_files[i]);
1959 ge->job_files[i] = NULL;
1961 while (i < ge->nr_job_files) {
1962 free(ge->job_files[i]);
1963 ge->job_files[i] = NULL;
1967 free(ge->job_files);
1968 ge->job_files = NULL;
1969 ge->nr_job_files = 0;
1973 static void *server_thread(void *arg)
1976 gfio_server_running = 1;
1977 fio_start_server(NULL);
1978 gfio_server_running = 0;
1982 static void gfio_start_server(void)
1984 struct gui *ui = &main_ui;
1986 if (!gfio_server_running) {
1987 gfio_server_running = 1;
1988 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1989 pthread_detach(ui->server_t);
1993 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1996 struct gui_entry *ge = data;
1997 struct gfio_client *gc = ge->client;
2000 fio_start_client(gc->client);
2003 static void file_open(GtkWidget *w, gpointer data);
2005 static void connect_clicked(GtkWidget *widget, gpointer data)
2007 struct gui_entry *ge = data;
2008 struct gfio_client *gc = ge->client;
2010 if (ge->state == GE_STATE_NEW) {
2013 if (!ge->nr_job_files)
2014 file_open(widget, ge->ui);
2015 if (!ge->nr_job_files)
2018 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2019 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2020 ret = fio_client_connect(gc->client);
2022 if (!ge->ui->handler_running)
2023 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2024 gfio_set_state(ge, GE_STATE_CONNECTED);
2028 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2029 report_error(error);
2030 g_error_free(error);
2033 fio_client_terminate(gc->client);
2034 gfio_set_state(ge, GE_STATE_NEW);
2035 clear_ge_ui_info(ge);
2039 static void send_clicked(GtkWidget *widget, gpointer data)
2041 struct gui_entry *ge = data;
2043 if (send_job_files(ge)) {
2046 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);
2047 report_error(error);
2048 g_error_free(error);
2050 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2054 static GtkWidget *add_button(GtkWidget *buttonbox,
2055 struct button_spec *buttonspec, gpointer data)
2057 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
2059 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
2060 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
2061 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
2062 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
2067 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
2072 for (i = 0; i < nbuttons; i++)
2073 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
2076 static void on_info_bar_response(GtkWidget *widget, gint response,
2079 struct gui *ui = &main_ui;
2081 if (response == GTK_RESPONSE_OK) {
2082 gtk_widget_destroy(widget);
2083 ui->error_info_bar = NULL;
2087 void report_error(GError *error)
2089 struct gui *ui = &main_ui;
2091 if (ui->error_info_bar == NULL) {
2092 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2095 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2096 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2099 ui->error_label = gtk_label_new(error->message);
2100 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2101 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2103 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2104 gtk_widget_show_all(ui->vbox);
2107 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2108 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2112 struct connection_widgets
2119 static void hostname_cb(GtkEntry *entry, gpointer data)
2121 struct connection_widgets *cw = data;
2122 int uses_net = 0, is_localhost = 0;
2127 * Check whether to display the 'auto start backend' box
2128 * or not. Show it if we are a localhost and using network,
2129 * or using a socket.
2131 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2132 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2137 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2138 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2139 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2140 !strcmp(text, "ip6-loopback"))
2144 if (!uses_net || is_localhost) {
2145 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2146 gtk_widget_set_sensitive(cw->button, 1);
2148 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2149 gtk_widget_set_sensitive(cw->button, 0);
2153 static int get_connection_details(char **host, int *port, int *type,
2156 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2157 struct connection_widgets cw;
2160 dialog = gtk_dialog_new_with_buttons("Connection details",
2161 GTK_WINDOW(main_ui.window),
2162 GTK_DIALOG_DESTROY_WITH_PARENT,
2163 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2164 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2166 frame = gtk_frame_new("Hostname / socket name");
2167 /* gtk_dialog_get_content_area() is 2.14 and newer */
2168 vbox = GTK_DIALOG(dialog)->vbox;
2169 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2171 box = gtk_vbox_new(FALSE, 6);
2172 gtk_container_add(GTK_CONTAINER(frame), box);
2174 hbox = gtk_hbox_new(TRUE, 10);
2175 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2176 cw.hentry = gtk_entry_new();
2177 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2178 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2180 frame = gtk_frame_new("Port");
2181 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2182 box = gtk_vbox_new(FALSE, 10);
2183 gtk_container_add(GTK_CONTAINER(frame), box);
2185 hbox = gtk_hbox_new(TRUE, 4);
2186 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2187 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2189 frame = gtk_frame_new("Type");
2190 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2191 box = gtk_vbox_new(FALSE, 10);
2192 gtk_container_add(GTK_CONTAINER(frame), box);
2194 hbox = gtk_hbox_new(TRUE, 4);
2195 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2197 cw.combo = gtk_combo_box_new_text();
2198 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2199 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2200 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2201 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2203 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2205 frame = gtk_frame_new("Options");
2206 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2207 box = gtk_vbox_new(FALSE, 10);
2208 gtk_container_add(GTK_CONTAINER(frame), box);
2210 hbox = gtk_hbox_new(TRUE, 4);
2211 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2213 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2214 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2215 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.");
2216 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2219 * Connect edit signal, so we can show/not-show the auto start button
2221 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2222 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2224 gtk_widget_show_all(dialog);
2226 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2227 gtk_widget_destroy(dialog);
2231 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2232 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2234 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2235 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2236 *type = Fio_client_ipv4;
2237 else if (!strncmp(typeentry, "IPv6", 4))
2238 *type = Fio_client_ipv6;
2240 *type = Fio_client_socket;
2243 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2245 gtk_widget_destroy(dialog);
2249 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2251 struct gfio_client *gc;
2253 gc = malloc(sizeof(*gc));
2254 memset(gc, 0, sizeof(*gc));
2256 gc->client = fio_get_client(client);
2260 client->client_data = gc;
2263 static GtkWidget *new_client_page(struct gui_entry *ge);
2265 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2267 struct gui_entry *ge;
2269 ge = malloc(sizeof(*ge));
2270 memset(ge, 0, sizeof(*ge));
2271 ge->state = GE_STATE_NEW;
2272 INIT_FLIST_HEAD(&ge->list);
2273 flist_add_tail(&ge->list, &ui->list);
2278 static struct gui_entry *get_new_ge_with_tab(const char *name)
2280 struct gui_entry *ge;
2282 ge = alloc_new_gui_entry(&main_ui);
2284 ge->vbox = new_client_page(ge);
2285 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2287 ge->page_label = gtk_label_new(name);
2288 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2290 gtk_widget_show_all(main_ui.window);
2294 static void file_new(GtkWidget *w, gpointer data)
2296 struct gui *ui = (struct gui *) data;
2297 struct gui_entry *ge;
2299 ge = get_new_ge_with_tab("Untitled");
2300 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2304 * Return the 'ge' corresponding to the tab. If the active tab is the
2305 * main tab, open a new tab.
2307 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2309 struct flist_head *entry;
2310 struct gui_entry *ge;
2315 return get_new_ge_with_tab("Untitled");
2321 flist_for_each(entry, &main_ui.list) {
2322 ge = flist_entry(entry, struct gui_entry, list);
2323 if (ge->page_num == cur_page)
2330 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2335 * Main tab is tab 0, so any current page other than 0 holds
2338 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2340 return get_ge_from_page(cur_page, NULL);
2345 static void file_close(GtkWidget *w, gpointer data)
2347 struct gui *ui = (struct gui *) data;
2348 struct gui_entry *ge;
2351 * Can't close the main tab
2353 ge = get_ge_from_cur_tab(ui);
2355 gtk_widget_destroy(ge->vbox);
2359 if (!flist_empty(&ui->list)) {
2360 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2367 static void file_add_recent(struct gui *ui, const gchar *uri)
2371 memset(&grd, 0, sizeof(grd));
2372 grd.display_name = strdup("gfio");
2373 grd.description = strdup("Fio job file");
2374 grd.mime_type = strdup(GFIO_MIME);
2375 grd.app_name = strdup(g_get_application_name());
2376 grd.app_exec = strdup("gfio %f/%u");
2378 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2381 static gchar *get_filename_from_uri(const gchar *uri)
2383 if (strncmp(uri, "file://", 7))
2386 return strdup(uri + 7);
2389 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2392 struct fio_client *client;
2395 filename = get_filename_from_uri(uri);
2397 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2398 ge->job_files[ge->nr_job_files] = strdup(filename);
2401 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2405 error = g_error_new(g_quark_from_string("fio"), 1,
2406 "Failed to add client %s", host);
2407 report_error(error);
2408 g_error_free(error);
2412 gfio_client_added(ge, client);
2413 file_add_recent(ge->ui, uri);
2417 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2419 int port, type, server_start;
2420 struct gui_entry *ge;
2423 int ret, ge_is_new = 0;
2426 * Creates new tab if current tab is the main window, or the
2427 * current tab already has a client.
2429 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2430 ge = get_ge_from_page(cur_page, &ge_is_new);
2432 ge = get_new_ge_with_tab("Untitled");
2436 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2438 if (get_connection_details(&host, &port, &type, &server_start)) {
2440 gtk_widget_destroy(ge->vbox);
2445 ret = do_file_open(ge, uri, host, type, port);
2451 gfio_start_server();
2454 gtk_widget_destroy(ge->vbox);
2460 static void recent_open(GtkAction *action, gpointer data)
2462 struct gui *ui = (struct gui *) data;
2463 GtkRecentInfo *info;
2466 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2467 uri = gtk_recent_info_get_uri(info);
2469 do_file_open_with_tab(ui, uri);
2472 static void file_open(GtkWidget *w, gpointer data)
2474 struct gui *ui = data;
2476 GSList *filenames, *fn_glist;
2477 GtkFileFilter *filter;
2479 dialog = gtk_file_chooser_dialog_new("Open File",
2480 GTK_WINDOW(ui->window),
2481 GTK_FILE_CHOOSER_ACTION_OPEN,
2482 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2483 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2485 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2487 filter = gtk_file_filter_new();
2488 gtk_file_filter_add_pattern(filter, "*.fio");
2489 gtk_file_filter_add_pattern(filter, "*.job");
2490 gtk_file_filter_add_pattern(filter, "*.ini");
2491 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2492 gtk_file_filter_set_name(filter, "Fio job file");
2493 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2495 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2496 gtk_widget_destroy(dialog);
2500 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2502 gtk_widget_destroy(dialog);
2504 filenames = fn_glist;
2505 while (filenames != NULL) {
2506 if (do_file_open_with_tab(ui, filenames->data))
2508 filenames = g_slist_next(filenames);
2511 g_slist_free(fn_glist);
2514 static void file_save(GtkWidget *w, gpointer data)
2516 struct gui *ui = data;
2519 dialog = gtk_file_chooser_dialog_new("Save File",
2520 GTK_WINDOW(ui->window),
2521 GTK_FILE_CHOOSER_ACTION_SAVE,
2522 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2523 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2526 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2527 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2529 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2532 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2533 // save_job_file(filename);
2536 gtk_widget_destroy(dialog);
2539 static void view_log_destroy(GtkWidget *w, gpointer data)
2541 struct gui *ui = (struct gui *) data;
2543 gtk_widget_ref(ui->log_tree);
2544 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2545 gtk_widget_destroy(w);
2546 ui->log_view = NULL;
2549 static void view_log(GtkWidget *w, gpointer data)
2551 GtkWidget *win, *scroll, *vbox, *box;
2552 struct gui *ui = (struct gui *) data;
2557 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2558 gtk_window_set_title(GTK_WINDOW(win), "Log");
2559 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2561 scroll = gtk_scrolled_window_new(NULL, NULL);
2563 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2565 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2567 box = gtk_hbox_new(TRUE, 0);
2568 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2569 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2570 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2572 vbox = gtk_vbox_new(TRUE, 5);
2573 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2575 gtk_container_add(GTK_CONTAINER(win), vbox);
2576 gtk_widget_show_all(win);
2579 static void connect_job_entry(GtkWidget *w, gpointer data)
2581 struct gui *ui = (struct gui *) data;
2582 struct gui_entry *ge;
2584 ge = get_ge_from_cur_tab(ui);
2586 connect_clicked(w, ge);
2589 static void send_job_entry(GtkWidget *w, gpointer data)
2591 struct gui *ui = (struct gui *) data;
2592 struct gui_entry *ge;
2594 ge = get_ge_from_cur_tab(ui);
2596 send_clicked(w, ge);
2600 static void edit_job_entry(GtkWidget *w, gpointer data)
2604 static void start_job_entry(GtkWidget *w, gpointer data)
2606 struct gui *ui = (struct gui *) data;
2607 struct gui_entry *ge;
2609 ge = get_ge_from_cur_tab(ui);
2611 start_job_clicked(w, ge);
2614 static void view_results(GtkWidget *w, gpointer data)
2616 struct gui *ui = (struct gui *) data;
2617 struct gfio_client *gc;
2618 struct gui_entry *ge;
2620 ge = get_ge_from_cur_tab(ui);
2624 if (ge->results_window)
2628 if (gc && gc->nr_results)
2629 gfio_display_end_results(gc);
2633 static void __update_graph_limits(struct gfio_graphs *g)
2635 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2636 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2639 static void update_graph_limits(void)
2641 struct flist_head *entry;
2642 struct gui_entry *ge;
2644 __update_graph_limits(&main_ui.graphs);
2646 flist_for_each(entry, &main_ui.list) {
2647 ge = flist_entry(entry, struct gui_entry, list);
2648 __update_graph_limits(&ge->graphs);
2652 static void preferences(GtkWidget *w, gpointer data)
2654 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2655 GtkWidget *hbox, *spin, *entry, *spin_int;
2658 dialog = gtk_dialog_new_with_buttons("Preferences",
2659 GTK_WINDOW(main_ui.window),
2660 GTK_DIALOG_DESTROY_WITH_PARENT,
2661 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2662 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2665 frame = gtk_frame_new("Graphing");
2666 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2667 vbox = gtk_vbox_new(FALSE, 6);
2668 gtk_container_add(GTK_CONTAINER(frame), vbox);
2670 hbox = gtk_hbox_new(FALSE, 5);
2671 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2672 entry = gtk_label_new("Font face to use for graph labels");
2673 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2675 font = gtk_font_button_new();
2676 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2678 box = gtk_vbox_new(FALSE, 6);
2679 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2681 hbox = gtk_hbox_new(FALSE, 5);
2682 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2683 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2684 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2686 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2688 box = gtk_vbox_new(FALSE, 6);
2689 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2691 hbox = gtk_hbox_new(FALSE, 5);
2692 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2693 entry = gtk_label_new("Client ETA request interval (msec)");
2694 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2696 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2697 frame = gtk_frame_new("Debug logging");
2698 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2699 vbox = gtk_vbox_new(FALSE, 6);
2700 gtk_container_add(GTK_CONTAINER(frame), vbox);
2702 box = gtk_hbox_new(FALSE, 6);
2703 gtk_container_add(GTK_CONTAINER(vbox), box);
2705 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2707 for (i = 0; i < FD_DEBUG_MAX; i++) {
2709 box = gtk_hbox_new(FALSE, 6);
2710 gtk_container_add(GTK_CONTAINER(vbox), box);
2714 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2715 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2716 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2719 gtk_widget_show_all(dialog);
2721 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2722 gtk_widget_destroy(dialog);
2726 for (i = 0; i < FD_DEBUG_MAX; i++) {
2729 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2731 fio_debug |= (1UL << i);
2734 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2735 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2736 update_graph_limits();
2737 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2739 gtk_widget_destroy(dialog);
2742 static void about_dialog(GtkWidget *w, gpointer data)
2744 const char *authors[] = {
2745 "Jens Axboe <axboe@kernel.dk>",
2746 "Stephen Carmeron <stephenmcameron@gmail.com>",
2749 const char *license[] = {
2750 "Fio is free software; you can redistribute it and/or modify "
2751 "it under the terms of the GNU General Public License as published by "
2752 "the Free Software Foundation; either version 2 of the License, or "
2753 "(at your option) any later version.\n",
2754 "Fio is distributed in the hope that it will be useful, "
2755 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2756 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2757 "GNU General Public License for more details.\n",
2758 "You should have received a copy of the GNU General Public License "
2759 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2760 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2762 char *license_trans;
2764 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2765 license[2], "\n", NULL);
2767 gtk_show_about_dialog(NULL,
2768 "program-name", "gfio",
2769 "comments", "Gtk2 UI for fio",
2770 "license", license_trans,
2771 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2773 "version", fio_version_string,
2774 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2775 "logo-icon-name", "fio",
2777 "wrap-license", TRUE,
2780 g_free(license_trans);
2783 static GtkActionEntry menu_items[] = {
2784 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2785 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2786 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2787 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2788 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2789 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2790 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2791 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2792 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2793 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2794 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2795 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2796 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2797 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2798 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2799 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2800 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2802 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2804 static const gchar *ui_string = " \
2806 <menubar name=\"MainMenu\"> \
2807 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2808 <menuitem name=\"New\" action=\"NewFile\" /> \
2809 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2810 <separator name=\"Separator1\"/> \
2811 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2812 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2813 <separator name=\"Separator2\"/> \
2814 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2815 <separator name=\"Separator3\"/> \
2816 <placeholder name=\"FileRecentFiles\"/> \
2817 <separator name=\"Separator4\"/> \
2818 <menuitem name=\"Quit\" action=\"Quit\" /> \
2820 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2821 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2822 <separator name=\"Separator5\"/> \
2823 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2824 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2825 <separator name=\"Separator6\"/> \
2826 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2828 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2829 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2830 <separator name=\"Separator7\"/> \
2831 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2833 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2834 <menuitem name=\"About\" action=\"About\" /> \
2840 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2843 GtkActionGroup *action_group;
2846 action_group = gtk_action_group_new("Menu");
2847 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2849 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2850 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2852 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2854 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2857 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2858 GtkWidget *vbox, GtkUIManager *ui_manager)
2860 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2863 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2865 struct gui_entry *ge = (struct gui_entry *) data;
2868 index = gtk_combo_box_get_active(box);
2870 multitext_set_entry(&ge->eta.iotype, index);
2871 multitext_set_entry(&ge->eta.ioengine, index);
2872 multitext_set_entry(&ge->eta.iodepth, index);
2875 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2877 struct gui_entry *ge = (struct gui_entry *) data;
2879 multitext_free(&ge->eta.iotype);
2880 multitext_free(&ge->eta.ioengine);
2881 multitext_free(&ge->eta.iodepth);
2884 static GtkWidget *new_client_page(struct gui_entry *ge)
2886 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2887 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2889 main_vbox = gtk_vbox_new(FALSE, 3);
2891 top_align = gtk_alignment_new(0, 0, 1, 0);
2892 top_vbox = gtk_vbox_new(FALSE, 3);
2893 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2894 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2896 probe = gtk_frame_new("Job");
2897 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2898 probe_frame = gtk_vbox_new(FALSE, 3);
2899 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2901 probe_box = gtk_hbox_new(FALSE, 3);
2902 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2903 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2904 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2905 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2906 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2908 probe_box = gtk_hbox_new(FALSE, 3);
2909 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2911 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2912 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2913 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2914 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2915 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2916 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2917 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2918 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2920 probe_box = gtk_hbox_new(FALSE, 3);
2921 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2922 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2923 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2924 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2925 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2928 * Only add this if we have a commit rate
2931 probe_box = gtk_hbox_new(FALSE, 3);
2932 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2934 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2935 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2937 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2938 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2942 * Set up a drawing area and IOPS and bandwidth graphs
2944 ge->graphs.drawing_area = gtk_drawing_area_new();
2945 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2946 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2947 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2948 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2949 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2950 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2951 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2952 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2953 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2954 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2955 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2956 ge->graphs.drawing_area);
2957 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2959 setup_graphs(&ge->graphs);
2962 * Set up alignments for widgets at the bottom of ui,
2963 * align bottom left, expand horizontally but not vertically
2965 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2966 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2967 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2968 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2970 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2973 * Set up thread status progress bar
2975 ge->thread_status_pb = gtk_progress_bar_new();
2976 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2977 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2978 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2984 static GtkWidget *new_main_page(struct gui *ui)
2986 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2987 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2989 main_vbox = gtk_vbox_new(FALSE, 3);
2992 * Set up alignments for widgets at the top of ui,
2993 * align top left, expand horizontally but not vertically
2995 top_align = gtk_alignment_new(0, 0, 1, 0);
2996 top_vbox = gtk_vbox_new(FALSE, 0);
2997 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2998 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3000 probe = gtk_frame_new("Run statistics");
3001 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3002 probe_frame = gtk_vbox_new(FALSE, 3);
3003 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3005 probe_box = gtk_hbox_new(FALSE, 3);
3006 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3007 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3008 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3009 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3010 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3011 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3014 * Only add this if we have a commit rate
3017 probe_box = gtk_hbox_new(FALSE, 3);
3018 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3020 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3021 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3023 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3024 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3028 * Set up a drawing area and IOPS and bandwidth graphs
3030 ui->graphs.drawing_area = gtk_drawing_area_new();
3031 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3032 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3033 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3034 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3035 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3036 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3037 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3038 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3039 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3040 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3041 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3042 ui->graphs.drawing_area);
3043 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3046 setup_graphs(&ui->graphs);
3049 * Set up alignments for widgets at the bottom of ui,
3050 * align bottom left, expand horizontally but not vertically
3052 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3053 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3054 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3055 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3058 * Set up thread status progress bar
3060 ui->thread_status_pb = gtk_progress_bar_new();
3061 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3062 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3063 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3068 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3069 guint page, gpointer data)
3072 struct gui *ui = (struct gui *) data;
3073 struct gui_entry *ge;
3076 set_job_menu_visible(ui, 0);
3077 set_view_results_visible(ui, 0);
3081 set_job_menu_visible(ui, 1);
3082 ge = get_ge_from_page(page, NULL);
3084 update_button_states(ui, ge);
3089 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3091 time_t time_a = gtk_recent_info_get_visited(a);
3092 time_t time_b = gtk_recent_info_get_visited(b);
3094 return time_b - time_a;
3097 static void add_recent_file_items(struct gui *ui)
3099 const gchar *gfio = g_get_application_name();
3100 GList *items, *item;
3103 if (ui->recent_ui_id) {
3104 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3105 gtk_ui_manager_ensure_update(ui->uimanager);
3107 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3109 if (ui->actiongroup) {
3110 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3111 g_object_unref(ui->actiongroup);
3113 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3115 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3117 items = gtk_recent_manager_get_items(ui->recentmanager);
3118 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3120 for (item = items; item && item->data; item = g_list_next(item)) {
3121 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3126 if (!gtk_recent_info_has_application(info, gfio))
3130 * We only support local files for now
3132 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3135 action_name = g_strdup_printf("RecentFile%u", i++);
3136 label = gtk_recent_info_get_display_name(info);
3138 action = g_object_new(GTK_TYPE_ACTION,
3139 "name", action_name,
3140 "label", label, NULL);
3142 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3143 gtk_recent_info_ref(info),
3144 (GDestroyNotify) gtk_recent_info_unref);
3147 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3149 gtk_action_group_add_action(ui->actiongroup, action);
3150 g_object_unref(action);
3152 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3153 "/MainMenu/FileMenu/FileRecentFiles",
3155 GTK_UI_MANAGER_MENUITEM, FALSE);
3157 g_free(action_name);
3163 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3167 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3168 gint x, gint y, GtkSelectionData *data,
3169 guint info, guint time)
3171 struct gui *ui = &main_ui;
3176 source = gtk_drag_get_source_widget(ctx);
3177 if (source && widget == gtk_widget_get_toplevel(source)) {
3178 gtk_drag_finish(ctx, FALSE, FALSE, time);
3182 uris = gtk_selection_data_get_uris(data);
3184 gtk_drag_finish(ctx, FALSE, FALSE, time);
3190 if (do_file_open_with_tab(ui, uris[i]))
3195 gtk_drag_finish(ctx, TRUE, FALSE, time);
3199 static void init_ui(int *argc, char **argv[], struct gui *ui)
3201 GtkSettings *settings;
3204 /* Magical g*thread incantation, you just need this thread stuff.
3205 * Without it, the update that happens in gfio_update_thread_status
3206 * doesn't really happen in a timely fashion, you need expose events
3208 if (!g_thread_supported())
3209 g_thread_init(NULL);
3212 gtk_init(argc, argv);
3213 settings = gtk_settings_get_default();
3214 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3216 gdk_color_parse("white", &white);
3218 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3219 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3220 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3222 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3223 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3225 ui->vbox = gtk_vbox_new(FALSE, 0);
3226 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3228 ui->uimanager = gtk_ui_manager_new();
3229 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3230 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3232 ui->recentmanager = gtk_recent_manager_get_default();
3233 add_recent_file_items(ui);
3235 ui->notebook = gtk_notebook_new();
3236 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3237 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3238 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3239 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3241 vbox = new_main_page(ui);
3242 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3243 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3244 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3246 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3248 gfio_ui_setup_log(ui);
3250 gtk_widget_show_all(ui->window);
3253 int main(int argc, char *argv[], char *envp[])
3255 if (initialize_fio(envp))
3257 if (fio_init_options())
3260 memset(&main_ui, 0, sizeof(main_ui));
3261 INIT_FLIST_HEAD(&main_ui.list);
3263 init_ui(&argc, &argv, &main_ui);
3265 gdk_threads_enter();
3267 gdk_threads_leave();