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_frame;
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);
902 static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
905 double xdim, double ydim)
910 g = graph_new(xdim, ydim, gfio_graph_font);
911 graph_title(g, title);
912 graph_x_title(g, "Buckets");
914 for (i = 0; i < len; i++) {
915 graph_add_label(g, labels[i]);
916 graph_add_data(g, labels[i], lat[i]);
922 static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
925 GtkWidget *tree_view;
926 GtkTreeSelection *selection;
932 types = malloc(num * sizeof(GType));
934 for (i = 0; i < num; i++)
935 types[i] = G_TYPE_STRING;
937 model = gtk_list_store_newv(num, types);
941 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
942 gtk_widget_set_can_focus(tree_view, FALSE);
944 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
945 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
947 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
948 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
950 for (i = 0; i < num; i++)
951 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
953 gtk_list_store_append(model, &iter);
955 for (i = 0; i < num; i++) {
959 sprintf(fbuf, "0.00");
961 sprintf(fbuf, "%3.2f%%", lat[i]);
963 gtk_list_store_set(model, &iter, i, fbuf, -1);
969 static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
970 struct thread_stat *ts)
972 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
973 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
974 "250u", "500u", "750u", "1m", "2m",
975 "4m", "10m", "20m", "50m", "100m",
976 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
978 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
979 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
980 struct gui_entry *ge = gc->ge;
982 stat_calc_lat_u(ts, io_u_lat);
983 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
986 * Found out which first bucket has entries, and which last bucket
989 for (i = 0; i < total; i++) {
990 if (io_u_lat[i] == 0.00)
1004 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1005 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
1007 frame = gtk_frame_new("Latency buckets");
1008 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1010 completion_vbox = gtk_vbox_new(FALSE, 3);
1011 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1012 hbox = gtk_hbox_new(FALSE, 3);
1013 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1015 drawing_area = gtk_drawing_area_new();
1016 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1017 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1018 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1019 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1020 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1022 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
1025 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1027 GtkWidget *box, *frame, *entry;
1028 double usr_cpu, sys_cpu;
1029 unsigned long runtime;
1032 runtime = ts->total_run_time;
1034 double runt = (double) runtime;
1036 usr_cpu = (double) ts->usr_time * 100 / runt;
1037 sys_cpu = (double) ts->sys_time * 100 / runt;
1043 frame = gtk_frame_new("OS resources");
1044 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1046 box = gtk_hbox_new(FALSE, 3);
1047 gtk_container_add(GTK_CONTAINER(frame), box);
1049 entry = new_info_entry_in_frame(box, "User CPU");
1050 sprintf(tmp, "%3.2f%%", usr_cpu);
1051 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1052 entry = new_info_entry_in_frame(box, "System CPU");
1053 sprintf(tmp, "%3.2f%%", sys_cpu);
1054 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1055 entry = new_info_entry_in_frame(box, "Context switches");
1056 entry_set_int_value(entry, ts->ctx);
1057 entry = new_info_entry_in_frame(box, "Major faults");
1058 entry_set_int_value(entry, ts->majf);
1059 entry = new_info_entry_in_frame(box, "Minor faults");
1060 entry_set_int_value(entry, ts->minf);
1062 static void gfio_add_sc_depths_tree(GtkListStore *model,
1063 struct thread_stat *ts, unsigned int len,
1066 double io_u_dist[FIO_IO_U_MAP_NR];
1068 /* Bits 0, and 3-8 */
1069 const int add_mask = 0x1f9;
1073 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1075 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1077 gtk_list_store_append(model, &iter);
1079 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1081 for (i = 1, j = 0; i < len; i++) {
1084 if (!(add_mask & (1UL << (i - 1))))
1085 sprintf(fbuf, "0.0%%");
1087 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1091 gtk_list_store_set(model, &iter, i, fbuf, -1);
1096 static void gfio_add_total_depths_tree(GtkListStore *model,
1097 struct thread_stat *ts, unsigned int len)
1099 double io_u_dist[FIO_IO_U_MAP_NR];
1101 /* Bits 1-6, and 8 */
1102 const int add_mask = 0x17e;
1105 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1107 gtk_list_store_append(model, &iter);
1109 gtk_list_store_set(model, &iter, 0, "Total", -1);
1111 for (i = 1, j = 0; i < len; i++) {
1114 if (!(add_mask & (1UL << (i - 1))))
1115 sprintf(fbuf, "0.0%%");
1117 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1121 gtk_list_store_set(model, &iter, i, fbuf, -1);
1126 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1128 GtkWidget *frame, *box, *tree_view;
1129 GtkTreeSelection *selection;
1130 GtkListStore *model;
1131 GType types[FIO_IO_U_MAP_NR + 1];
1133 #define NR_LABELS 10
1134 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1136 frame = gtk_frame_new("IO depths");
1137 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1139 box = gtk_hbox_new(FALSE, 3);
1140 gtk_container_add(GTK_CONTAINER(frame), box);
1142 for (i = 0; i < NR_LABELS; i++)
1143 types[i] = G_TYPE_STRING;
1145 model = gtk_list_store_newv(NR_LABELS, types);
1147 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1148 gtk_widget_set_can_focus(tree_view, FALSE);
1150 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1151 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1153 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1154 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1156 for (i = 0; i < NR_LABELS; i++)
1157 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1159 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1160 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1161 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1163 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1166 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1168 struct gui_entry *ge = (struct gui_entry *) data;
1170 gtk_widget_destroy(w);
1171 ge->results_window = NULL;
1172 ge->results_notebook = NULL;
1176 static void results_close(GtkWidget *w, gpointer *data)
1178 struct gui_entry *ge = (struct gui_entry *) data;
1180 gtk_widget_destroy(ge->results_window);
1183 static GtkActionEntry results_menu_items[] = {
1184 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1185 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1186 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1188 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1190 static const gchar *results_ui_string = " \
1192 <menubar name=\"MainMenu\"> \
1193 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1194 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1196 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1202 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1204 GtkActionGroup *action_group;
1208 ge->results_uimanager = gtk_ui_manager_new();
1210 action_group = gtk_action_group_new("ResultsMenu");
1211 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1213 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1214 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1216 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1218 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1222 static GtkWidget *get_results_window(struct gui_entry *ge)
1224 GtkWidget *win, *notebook, *vbox;
1226 if (ge->results_window)
1227 return ge->results_notebook;
1229 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1230 gtk_window_set_title(GTK_WINDOW(win), "Results");
1231 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1232 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1233 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1235 vbox = gtk_vbox_new(FALSE, 0);
1236 gtk_container_add(GTK_CONTAINER(win), vbox);
1238 ge->results_menu = get_results_menubar(win, ge);
1239 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1241 notebook = gtk_notebook_new();
1242 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1243 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1244 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1246 ge->results_window = win;
1247 ge->results_notebook = notebook;
1248 return ge->results_notebook;
1251 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1252 struct group_run_stats *rs)
1254 unsigned int nr = gc->nr_results;
1256 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1257 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1258 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1262 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1263 struct thread_stat *ts,
1264 struct group_run_stats *rs)
1266 GtkWidget *box, *vbox, *entry, *scroll;
1268 scroll = gtk_scrolled_window_new(NULL, NULL);
1269 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1270 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1272 vbox = gtk_vbox_new(FALSE, 3);
1274 box = gtk_hbox_new(FALSE, 0);
1275 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1277 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1279 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1281 gc->results_widget = vbox;
1283 entry = new_info_entry_in_frame(box, "Name");
1284 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1285 if (strlen(ts->description)) {
1286 entry = new_info_entry_in_frame(box, "Description");
1287 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1289 entry = new_info_entry_in_frame(box, "Group ID");
1290 entry_set_int_value(entry, ts->groupid);
1291 entry = new_info_entry_in_frame(box, "Jobs");
1292 entry_set_int_value(entry, ts->members);
1293 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1294 entry_set_int_value(entry, ts->error);
1295 entry = new_info_entry_in_frame(box, "PID");
1296 entry_set_int_value(entry, ts->pid);
1298 if (ts->io_bytes[DDIR_READ])
1299 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1300 if (ts->io_bytes[DDIR_WRITE])
1301 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1303 gfio_show_latency_buckets(gc, vbox, ts);
1304 gfio_show_cpu_usage(vbox, ts);
1305 gfio_show_io_depths(vbox, ts);
1308 static void gfio_display_end_results(struct gfio_client *gc)
1313 res_win = get_results_window(gc->ge);
1315 for (i = 0; i < gc->nr_results; i++) {
1316 struct end_results *e = &gc->results[i];
1318 __gfio_display_end_results(res_win, gc, &e->ts, &e->gs);
1321 gtk_widget_show_all(gc->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_frame) {
1378 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1379 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1382 vbox = gtk_vbox_new(FALSE, 3);
1383 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1385 frame = gtk_frame_new((char *) p->dus.name);
1386 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1388 box = gtk_vbox_new(FALSE, 3);
1389 gtk_container_add(GTK_CONTAINER(frame), box);
1391 frame = gtk_frame_new("Read");
1392 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1393 vbox = gtk_hbox_new(TRUE, 3);
1394 gtk_container_add(GTK_CONTAINER(frame), vbox);
1395 entry = new_info_entry_in_frame(vbox, "IOs");
1396 entry_set_int_value(entry, p->dus.ios[0]);
1397 entry = new_info_entry_in_frame(vbox, "Merges");
1398 entry_set_int_value(entry, p->dus.merges[0]);
1399 entry = new_info_entry_in_frame(vbox, "Sectors");
1400 entry_set_int_value(entry, p->dus.sectors[0]);
1401 entry = new_info_entry_in_frame(vbox, "Ticks");
1402 entry_set_int_value(entry, p->dus.ticks[0]);
1404 frame = gtk_frame_new("Write");
1405 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1406 vbox = gtk_hbox_new(TRUE, 3);
1407 gtk_container_add(GTK_CONTAINER(frame), vbox);
1408 entry = new_info_entry_in_frame(vbox, "IOs");
1409 entry_set_int_value(entry, p->dus.ios[1]);
1410 entry = new_info_entry_in_frame(vbox, "Merges");
1411 entry_set_int_value(entry, p->dus.merges[1]);
1412 entry = new_info_entry_in_frame(vbox, "Sectors");
1413 entry_set_int_value(entry, p->dus.sectors[1]);
1414 entry = new_info_entry_in_frame(vbox, "Ticks");
1415 entry_set_int_value(entry, p->dus.ticks[1]);
1417 frame = gtk_frame_new("Shared");
1418 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1419 vbox = gtk_hbox_new(TRUE, 3);
1420 gtk_container_add(GTK_CONTAINER(frame), vbox);
1421 entry = new_info_entry_in_frame(vbox, "IO ticks");
1422 entry_set_int_value(entry, p->dus.io_ticks);
1423 entry = new_info_entry_in_frame(vbox, "Time in queue");
1424 entry_set_int_value(entry, p->dus.time_in_queue);
1428 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1432 sprintf(tmp, "%3.2f%%", util);
1433 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1434 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1436 gtk_widget_show_all(gc->results_widget);
1438 gdk_threads_leave();
1441 extern int sum_stat_clients;
1442 extern struct thread_stat client_ts;
1443 extern struct group_run_stats client_gs;
1445 static int sum_stat_nr;
1447 static void gfio_thread_status_op(struct fio_client *client,
1448 struct fio_net_cmd *cmd)
1450 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1452 gfio_display_ts(client, &p->ts, &p->rs);
1454 if (sum_stat_clients == 1)
1457 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1458 sum_group_stats(&client_gs, &p->rs);
1460 client_ts.members++;
1461 client_ts.groupid = p->ts.groupid;
1463 if (++sum_stat_nr == sum_stat_clients) {
1464 strcpy(client_ts.name, "All clients");
1465 gfio_display_ts(client, &client_ts, &client_gs);
1469 static void gfio_group_stats_op(struct fio_client *client,
1470 struct fio_net_cmd *cmd)
1472 /* We're ignoring group stats for now */
1475 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1478 struct gfio_graphs *g = data;
1480 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1481 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1482 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1483 graph_set_position(g->bandwidth_graph, 0, 0);
1487 static void draw_graph(struct graph *g, cairo_t *cr)
1489 line_graph_draw(g, cr);
1493 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1494 gboolean keyboard_mode, GtkTooltip *tooltip,
1497 struct gfio_graphs *g = data;
1498 const char *text = NULL;
1500 if (graph_contains_xy(g->iops_graph, x, y))
1501 text = graph_find_tooltip(g->iops_graph, x, y);
1502 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1503 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1506 gtk_tooltip_set_text(tooltip, text);
1513 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1515 struct gfio_graphs *g = p;
1518 cr = gdk_cairo_create(w->window);
1520 if (graph_has_tooltips(g->iops_graph) ||
1521 graph_has_tooltips(g->bandwidth_graph)) {
1522 g_object_set(w, "has-tooltip", TRUE, NULL);
1523 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1526 cairo_set_source_rgb(cr, 0, 0, 0);
1527 draw_graph(g->iops_graph, cr);
1528 draw_graph(g->bandwidth_graph, cr);
1535 * Client specific ETA
1537 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1539 struct gfio_client *gc = client->client_data;
1540 struct gui_entry *ge = gc->ge;
1541 static int eta_good;
1548 gdk_threads_enter();
1553 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1554 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1555 eta_to_str(eta_str, je->eta_sec);
1558 sprintf(tmp, "%u", je->nr_running);
1559 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1560 sprintf(tmp, "%u", je->files_open);
1561 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1564 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1565 if (je->m_rate || je->t_rate) {
1568 mr = num2str(je->m_rate, 4, 0, i2p);
1569 tr = num2str(je->t_rate, 4, 0, i2p);
1570 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1571 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1574 } else if (je->m_iops || je->t_iops)
1575 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1577 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1578 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1579 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1580 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1583 if (je->eta_sec != INT_MAX && je->nr_running) {
1587 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1588 strcpy(output, "-.-% done");
1592 sprintf(output, "%3.1f%% done", perc);
1595 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1596 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1598 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1599 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1601 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1602 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1603 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1604 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1606 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1607 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1608 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1609 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1618 char *dst = output + strlen(output);
1620 sprintf(dst, " - %s", eta_str);
1623 gfio_update_thread_status(ge, output, perc);
1624 gdk_threads_leave();
1628 * Update ETA in main window for all clients
1630 static void gfio_update_all_eta(struct jobs_eta *je)
1632 struct gui *ui = &main_ui;
1633 static int eta_good;
1639 gdk_threads_enter();
1644 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1645 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1646 eta_to_str(eta_str, je->eta_sec);
1650 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1651 if (je->m_rate || je->t_rate) {
1654 mr = num2str(je->m_rate, 4, 0, i2p);
1655 tr = num2str(je->t_rate, 4, 0, i2p);
1656 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1657 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1660 } else if (je->m_iops || je->t_iops)
1661 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1663 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1664 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1665 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1666 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1669 entry_set_int_value(ui->eta.jobs, je->nr_running);
1671 if (je->eta_sec != INT_MAX && je->nr_running) {
1675 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1676 strcpy(output, "-.-% done");
1680 sprintf(output, "%3.1f%% done", perc);
1683 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1684 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1686 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1687 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1689 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1690 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1691 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1692 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1694 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1695 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1696 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1697 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1706 char *dst = output + strlen(output);
1708 sprintf(dst, " - %s", eta_str);
1711 gfio_update_thread_status_all(output, perc);
1712 gdk_threads_leave();
1715 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1717 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1718 struct gfio_client *gc = client->client_data;
1719 struct gui_entry *ge = gc->ge;
1720 const char *os, *arch;
1723 os = fio_get_os_string(probe->os);
1727 arch = fio_get_arch_string(probe->arch);
1732 client->name = strdup((char *) probe->hostname);
1734 gdk_threads_enter();
1736 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1737 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1738 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1739 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1740 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1742 gfio_set_state(ge, GE_STATE_CONNECTED);
1744 gdk_threads_leave();
1747 static void gfio_update_thread_status(struct gui_entry *ge,
1748 char *status_message, double perc)
1750 static char message[100];
1751 const char *m = message;
1753 strncpy(message, status_message, sizeof(message) - 1);
1754 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1755 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1756 gtk_widget_queue_draw(main_ui.window);
1759 static void gfio_update_thread_status_all(char *status_message, double perc)
1761 struct gui *ui = &main_ui;
1762 static char message[100];
1763 const char *m = message;
1765 strncpy(message, status_message, sizeof(message) - 1);
1766 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1767 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1768 gtk_widget_queue_draw(ui->window);
1771 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1773 struct gfio_client *gc = client->client_data;
1775 gdk_threads_enter();
1776 gfio_set_state(gc->ge, GE_STATE_NEW);
1777 gdk_threads_leave();
1780 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1782 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1783 struct gfio_client *gc = client->client_data;
1784 struct thread_options *o = &gc->o;
1785 struct gui_entry *ge = gc->ge;
1788 convert_thread_options_to_cpu(o, &p->top);
1790 gdk_threads_enter();
1792 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1794 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1795 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1797 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1798 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1800 sprintf(tmp, "%u", o->iodepth);
1801 multitext_add_entry(&ge->eta.iodepth, tmp);
1803 multitext_set_entry(&ge->eta.iotype, 0);
1804 multitext_set_entry(&ge->eta.ioengine, 0);
1805 multitext_set_entry(&ge->eta.iodepth, 0);
1809 gfio_set_state(ge, GE_STATE_JOB_SENT);
1811 gdk_threads_leave();
1814 static void gfio_client_timed_out(struct fio_client *client)
1816 struct gfio_client *gc = client->client_data;
1819 gdk_threads_enter();
1821 gfio_set_state(gc->ge, GE_STATE_NEW);
1822 clear_ge_ui_info(gc->ge);
1824 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1825 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1827 gdk_threads_leave();
1830 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1832 struct gfio_client *gc = client->client_data;
1834 gdk_threads_enter();
1836 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1839 entry_set_int_value(gc->err_entry, client->error);
1841 gdk_threads_leave();
1844 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1846 struct gfio_client *gc = client->client_data;
1848 gdk_threads_enter();
1849 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1850 gdk_threads_leave();
1853 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1855 struct gfio_client *gc = client->client_data;
1857 gdk_threads_enter();
1858 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1859 gdk_threads_leave();
1862 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1864 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1868 struct client_ops gfio_client_ops = {
1869 .text = gfio_text_op,
1870 .disk_util = gfio_disk_util_op,
1871 .thread_status = gfio_thread_status_op,
1872 .group_stats = gfio_group_stats_op,
1873 .jobs_eta = gfio_update_client_eta,
1874 .eta = gfio_update_all_eta,
1875 .probe = gfio_probe_op,
1876 .quit = gfio_quit_op,
1877 .add_job = gfio_add_job_op,
1878 .timed_out = gfio_client_timed_out,
1879 .stop = gfio_client_stop,
1880 .start = gfio_client_start,
1881 .job_start = gfio_client_job_start,
1882 .iolog = gfio_client_iolog,
1883 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1884 .stay_connected = 1,
1885 .client_type = FIO_CLIENT_TYPE_GUI,
1889 * FIXME: need more handling here
1891 static void ge_destroy(struct gui_entry *ge)
1893 struct gfio_client *gc = ge->client;
1895 if (gc && gc->client) {
1896 if (ge->state >= GE_STATE_CONNECTED)
1897 fio_client_terminate(gc->client);
1899 fio_put_client(gc->client);
1902 flist_del(&ge->list);
1906 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1910 static void gfio_quit(struct gui *ui)
1912 struct gui_entry *ge;
1914 while (!flist_empty(&ui->list)) {
1915 ge = flist_entry(ui->list.next, struct gui_entry, list);
1922 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1923 __attribute__((unused)) gpointer data)
1928 static void *job_thread(void *arg)
1930 struct gui *ui = arg;
1932 ui->handler_running = 1;
1933 fio_handle_clients(&gfio_client_ops);
1934 ui->handler_running = 0;
1938 static int send_job_files(struct gui_entry *ge)
1940 struct gfio_client *gc = ge->client;
1943 for (i = 0; i < ge->nr_job_files; i++) {
1944 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1948 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1949 report_error(error);
1950 g_error_free(error);
1955 free(ge->job_files[i]);
1956 ge->job_files[i] = NULL;
1958 while (i < ge->nr_job_files) {
1959 free(ge->job_files[i]);
1960 ge->job_files[i] = NULL;
1964 free(ge->job_files);
1965 ge->job_files = NULL;
1966 ge->nr_job_files = 0;
1970 static void *server_thread(void *arg)
1973 gfio_server_running = 1;
1974 fio_start_server(NULL);
1975 gfio_server_running = 0;
1979 static void gfio_start_server(void)
1981 struct gui *ui = &main_ui;
1983 if (!gfio_server_running) {
1984 gfio_server_running = 1;
1985 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1986 pthread_detach(ui->server_t);
1990 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1993 struct gui_entry *ge = data;
1994 struct gfio_client *gc = ge->client;
1997 fio_start_client(gc->client);
2000 static void file_open(GtkWidget *w, gpointer data);
2002 static void connect_clicked(GtkWidget *widget, gpointer data)
2004 struct gui_entry *ge = data;
2005 struct gfio_client *gc = ge->client;
2007 if (ge->state == GE_STATE_NEW) {
2010 if (!ge->nr_job_files)
2011 file_open(widget, ge->ui);
2012 if (!ge->nr_job_files)
2015 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2016 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2017 ret = fio_client_connect(gc->client);
2019 if (!ge->ui->handler_running)
2020 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2021 gfio_set_state(ge, GE_STATE_CONNECTED);
2025 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2026 report_error(error);
2027 g_error_free(error);
2030 fio_client_terminate(gc->client);
2031 gfio_set_state(ge, GE_STATE_NEW);
2032 clear_ge_ui_info(ge);
2036 static void send_clicked(GtkWidget *widget, gpointer data)
2038 struct gui_entry *ge = data;
2040 if (send_job_files(ge)) {
2043 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);
2044 report_error(error);
2045 g_error_free(error);
2047 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2051 static GtkWidget *add_button(GtkWidget *buttonbox,
2052 struct button_spec *buttonspec, gpointer data)
2054 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
2056 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
2057 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
2058 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
2059 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
2064 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
2069 for (i = 0; i < nbuttons; i++)
2070 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
2073 static void on_info_bar_response(GtkWidget *widget, gint response,
2076 struct gui *ui = &main_ui;
2078 if (response == GTK_RESPONSE_OK) {
2079 gtk_widget_destroy(widget);
2080 ui->error_info_bar = NULL;
2084 void report_error(GError *error)
2086 struct gui *ui = &main_ui;
2088 if (ui->error_info_bar == NULL) {
2089 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2092 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2093 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2096 ui->error_label = gtk_label_new(error->message);
2097 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2098 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2100 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2101 gtk_widget_show_all(ui->vbox);
2104 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2105 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2109 struct connection_widgets
2116 static void hostname_cb(GtkEntry *entry, gpointer data)
2118 struct connection_widgets *cw = data;
2119 int uses_net = 0, is_localhost = 0;
2124 * Check whether to display the 'auto start backend' box
2125 * or not. Show it if we are a localhost and using network,
2126 * or using a socket.
2128 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2129 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2134 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2135 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2136 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2137 !strcmp(text, "ip6-loopback"))
2141 if (!uses_net || is_localhost) {
2142 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2143 gtk_widget_set_sensitive(cw->button, 1);
2145 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2146 gtk_widget_set_sensitive(cw->button, 0);
2150 static int get_connection_details(char **host, int *port, int *type,
2153 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2154 struct connection_widgets cw;
2157 dialog = gtk_dialog_new_with_buttons("Connection details",
2158 GTK_WINDOW(main_ui.window),
2159 GTK_DIALOG_DESTROY_WITH_PARENT,
2160 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2161 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2163 frame = gtk_frame_new("Hostname / socket name");
2164 /* gtk_dialog_get_content_area() is 2.14 and newer */
2165 vbox = GTK_DIALOG(dialog)->vbox;
2166 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2168 box = gtk_vbox_new(FALSE, 6);
2169 gtk_container_add(GTK_CONTAINER(frame), box);
2171 hbox = gtk_hbox_new(TRUE, 10);
2172 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2173 cw.hentry = gtk_entry_new();
2174 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2175 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2177 frame = gtk_frame_new("Port");
2178 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2179 box = gtk_vbox_new(FALSE, 10);
2180 gtk_container_add(GTK_CONTAINER(frame), box);
2182 hbox = gtk_hbox_new(TRUE, 4);
2183 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2184 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2186 frame = gtk_frame_new("Type");
2187 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2188 box = gtk_vbox_new(FALSE, 10);
2189 gtk_container_add(GTK_CONTAINER(frame), box);
2191 hbox = gtk_hbox_new(TRUE, 4);
2192 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2194 cw.combo = gtk_combo_box_new_text();
2195 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2196 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2197 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2198 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2200 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2202 frame = gtk_frame_new("Options");
2203 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2204 box = gtk_vbox_new(FALSE, 10);
2205 gtk_container_add(GTK_CONTAINER(frame), box);
2207 hbox = gtk_hbox_new(TRUE, 4);
2208 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2210 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2211 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2212 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.");
2213 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2216 * Connect edit signal, so we can show/not-show the auto start button
2218 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2219 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2221 gtk_widget_show_all(dialog);
2223 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2224 gtk_widget_destroy(dialog);
2228 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2229 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2231 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2232 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2233 *type = Fio_client_ipv4;
2234 else if (!strncmp(typeentry, "IPv6", 4))
2235 *type = Fio_client_ipv6;
2237 *type = Fio_client_socket;
2240 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2242 gtk_widget_destroy(dialog);
2246 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2248 struct gfio_client *gc;
2250 gc = malloc(sizeof(*gc));
2251 memset(gc, 0, sizeof(*gc));
2253 gc->client = fio_get_client(client);
2257 client->client_data = gc;
2260 static GtkWidget *new_client_page(struct gui_entry *ge);
2262 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2264 struct gui_entry *ge;
2266 ge = malloc(sizeof(*ge));
2267 memset(ge, 0, sizeof(*ge));
2268 ge->state = GE_STATE_NEW;
2269 INIT_FLIST_HEAD(&ge->list);
2270 flist_add_tail(&ge->list, &ui->list);
2275 static struct gui_entry *get_new_ge_with_tab(const char *name)
2277 struct gui_entry *ge;
2279 ge = alloc_new_gui_entry(&main_ui);
2281 ge->vbox = new_client_page(ge);
2282 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2284 ge->page_label = gtk_label_new(name);
2285 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2287 gtk_widget_show_all(main_ui.window);
2291 static void file_new(GtkWidget *w, gpointer data)
2293 struct gui *ui = (struct gui *) data;
2294 struct gui_entry *ge;
2296 ge = get_new_ge_with_tab("Untitled");
2297 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2301 * Return the 'ge' corresponding to the tab. If the active tab is the
2302 * main tab, open a new tab.
2304 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2306 struct flist_head *entry;
2307 struct gui_entry *ge;
2312 return get_new_ge_with_tab("Untitled");
2318 flist_for_each(entry, &main_ui.list) {
2319 ge = flist_entry(entry, struct gui_entry, list);
2320 if (ge->page_num == cur_page)
2327 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2332 * Main tab is tab 0, so any current page other than 0 holds
2335 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2337 return get_ge_from_page(cur_page, NULL);
2342 static void file_close(GtkWidget *w, gpointer data)
2344 struct gui *ui = (struct gui *) data;
2345 struct gui_entry *ge;
2348 * Can't close the main tab
2350 ge = get_ge_from_cur_tab(ui);
2352 gtk_widget_destroy(ge->vbox);
2356 if (!flist_empty(&ui->list)) {
2357 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2364 static void file_add_recent(struct gui *ui, const gchar *uri)
2368 memset(&grd, 0, sizeof(grd));
2369 grd.display_name = strdup("gfio");
2370 grd.description = strdup("Fio job file");
2371 grd.mime_type = strdup(GFIO_MIME);
2372 grd.app_name = strdup(g_get_application_name());
2373 grd.app_exec = strdup("gfio %f/%u");
2375 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2378 static gchar *get_filename_from_uri(const gchar *uri)
2380 if (strncmp(uri, "file://", 7))
2383 return strdup(uri + 7);
2386 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2389 struct fio_client *client;
2392 filename = get_filename_from_uri(uri);
2394 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2395 ge->job_files[ge->nr_job_files] = strdup(filename);
2398 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2402 error = g_error_new(g_quark_from_string("fio"), 1,
2403 "Failed to add client %s", host);
2404 report_error(error);
2405 g_error_free(error);
2409 gfio_client_added(ge, client);
2410 file_add_recent(ge->ui, uri);
2414 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2416 int port, type, server_start;
2417 struct gui_entry *ge;
2420 int ret, ge_is_new = 0;
2423 * Creates new tab if current tab is the main window, or the
2424 * current tab already has a client.
2426 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2427 ge = get_ge_from_page(cur_page, &ge_is_new);
2429 ge = get_new_ge_with_tab("Untitled");
2433 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2435 if (get_connection_details(&host, &port, &type, &server_start)) {
2437 gtk_widget_destroy(ge->vbox);
2442 ret = do_file_open(ge, uri, host, type, port);
2448 gfio_start_server();
2451 gtk_widget_destroy(ge->vbox);
2457 static void recent_open(GtkAction *action, gpointer data)
2459 struct gui *ui = (struct gui *) data;
2460 GtkRecentInfo *info;
2463 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2464 uri = gtk_recent_info_get_uri(info);
2466 do_file_open_with_tab(ui, uri);
2469 static void file_open(GtkWidget *w, gpointer data)
2471 struct gui *ui = data;
2473 GSList *filenames, *fn_glist;
2474 GtkFileFilter *filter;
2476 dialog = gtk_file_chooser_dialog_new("Open File",
2477 GTK_WINDOW(ui->window),
2478 GTK_FILE_CHOOSER_ACTION_OPEN,
2479 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2480 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2482 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2484 filter = gtk_file_filter_new();
2485 gtk_file_filter_add_pattern(filter, "*.fio");
2486 gtk_file_filter_add_pattern(filter, "*.job");
2487 gtk_file_filter_add_pattern(filter, "*.ini");
2488 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2489 gtk_file_filter_set_name(filter, "Fio job file");
2490 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2492 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2493 gtk_widget_destroy(dialog);
2497 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2499 gtk_widget_destroy(dialog);
2501 filenames = fn_glist;
2502 while (filenames != NULL) {
2503 if (do_file_open_with_tab(ui, filenames->data))
2505 filenames = g_slist_next(filenames);
2508 g_slist_free(fn_glist);
2511 static void file_save(GtkWidget *w, gpointer data)
2513 struct gui *ui = data;
2516 dialog = gtk_file_chooser_dialog_new("Save File",
2517 GTK_WINDOW(ui->window),
2518 GTK_FILE_CHOOSER_ACTION_SAVE,
2519 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2520 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2523 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2524 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2526 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2529 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2530 // save_job_file(filename);
2533 gtk_widget_destroy(dialog);
2536 static void view_log_destroy(GtkWidget *w, gpointer data)
2538 struct gui *ui = (struct gui *) data;
2540 gtk_widget_ref(ui->log_tree);
2541 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2542 gtk_widget_destroy(w);
2543 ui->log_view = NULL;
2546 static void view_log(GtkWidget *w, gpointer data)
2548 GtkWidget *win, *scroll, *vbox, *box;
2549 struct gui *ui = (struct gui *) data;
2554 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2555 gtk_window_set_title(GTK_WINDOW(win), "Log");
2556 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2558 scroll = gtk_scrolled_window_new(NULL, NULL);
2560 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2562 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2564 box = gtk_hbox_new(TRUE, 0);
2565 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2566 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2567 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2569 vbox = gtk_vbox_new(TRUE, 5);
2570 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2572 gtk_container_add(GTK_CONTAINER(win), vbox);
2573 gtk_widget_show_all(win);
2576 static void connect_job_entry(GtkWidget *w, gpointer data)
2578 struct gui *ui = (struct gui *) data;
2579 struct gui_entry *ge;
2581 ge = get_ge_from_cur_tab(ui);
2583 connect_clicked(w, ge);
2586 static void send_job_entry(GtkWidget *w, gpointer data)
2588 struct gui *ui = (struct gui *) data;
2589 struct gui_entry *ge;
2591 ge = get_ge_from_cur_tab(ui);
2593 send_clicked(w, ge);
2597 static void edit_job_entry(GtkWidget *w, gpointer data)
2601 static void start_job_entry(GtkWidget *w, gpointer data)
2603 struct gui *ui = (struct gui *) data;
2604 struct gui_entry *ge;
2606 ge = get_ge_from_cur_tab(ui);
2608 start_job_clicked(w, ge);
2611 static void view_results(GtkWidget *w, gpointer data)
2613 struct gui *ui = (struct gui *) data;
2614 struct gfio_client *gc;
2615 struct gui_entry *ge;
2617 ge = get_ge_from_cur_tab(ui);
2621 if (ge->results_window)
2625 if (gc && gc->nr_results)
2626 gfio_display_end_results(gc);
2630 static void __update_graph_limits(struct gfio_graphs *g)
2632 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2633 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2636 static void update_graph_limits(void)
2638 struct flist_head *entry;
2639 struct gui_entry *ge;
2641 __update_graph_limits(&main_ui.graphs);
2643 flist_for_each(entry, &main_ui.list) {
2644 ge = flist_entry(entry, struct gui_entry, list);
2645 __update_graph_limits(&ge->graphs);
2649 static void preferences(GtkWidget *w, gpointer data)
2651 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2652 GtkWidget *hbox, *spin, *entry, *spin_int;
2655 dialog = gtk_dialog_new_with_buttons("Preferences",
2656 GTK_WINDOW(main_ui.window),
2657 GTK_DIALOG_DESTROY_WITH_PARENT,
2658 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2659 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2662 frame = gtk_frame_new("Graphing");
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 hbox = gtk_hbox_new(FALSE, 5);
2668 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2669 entry = gtk_label_new("Font face to use for graph labels");
2670 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2672 font = gtk_font_button_new();
2673 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2675 box = gtk_vbox_new(FALSE, 6);
2676 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2678 hbox = gtk_hbox_new(FALSE, 5);
2679 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2680 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2681 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2683 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2685 box = gtk_vbox_new(FALSE, 6);
2686 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2688 hbox = gtk_hbox_new(FALSE, 5);
2689 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2690 entry = gtk_label_new("Client ETA request interval (msec)");
2691 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2693 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2694 frame = gtk_frame_new("Debug logging");
2695 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2696 vbox = gtk_vbox_new(FALSE, 6);
2697 gtk_container_add(GTK_CONTAINER(frame), vbox);
2699 box = gtk_hbox_new(FALSE, 6);
2700 gtk_container_add(GTK_CONTAINER(vbox), box);
2702 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2704 for (i = 0; i < FD_DEBUG_MAX; i++) {
2706 box = gtk_hbox_new(FALSE, 6);
2707 gtk_container_add(GTK_CONTAINER(vbox), box);
2711 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2712 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2713 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2716 gtk_widget_show_all(dialog);
2718 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2719 gtk_widget_destroy(dialog);
2723 for (i = 0; i < FD_DEBUG_MAX; i++) {
2726 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2728 fio_debug |= (1UL << i);
2731 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2732 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2733 update_graph_limits();
2734 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2736 gtk_widget_destroy(dialog);
2739 static void about_dialog(GtkWidget *w, gpointer data)
2741 const char *authors[] = {
2742 "Jens Axboe <axboe@kernel.dk>",
2743 "Stephen Carmeron <stephenmcameron@gmail.com>",
2746 const char *license[] = {
2747 "Fio is free software; you can redistribute it and/or modify "
2748 "it under the terms of the GNU General Public License as published by "
2749 "the Free Software Foundation; either version 2 of the License, or "
2750 "(at your option) any later version.\n",
2751 "Fio is distributed in the hope that it will be useful, "
2752 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2753 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2754 "GNU General Public License for more details.\n",
2755 "You should have received a copy of the GNU General Public License "
2756 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2757 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2759 char *license_trans;
2761 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2762 license[2], "\n", NULL);
2764 gtk_show_about_dialog(NULL,
2765 "program-name", "gfio",
2766 "comments", "Gtk2 UI for fio",
2767 "license", license_trans,
2768 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2770 "version", fio_version_string,
2771 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2772 "logo-icon-name", "fio",
2774 "wrap-license", TRUE,
2777 g_free(license_trans);
2780 static GtkActionEntry menu_items[] = {
2781 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2782 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2783 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2784 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2785 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2786 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2787 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2788 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2789 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2790 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2791 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2792 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2793 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2794 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2795 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2796 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2797 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2799 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2801 static const gchar *ui_string = " \
2803 <menubar name=\"MainMenu\"> \
2804 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2805 <menuitem name=\"New\" action=\"NewFile\" /> \
2806 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2807 <separator name=\"Separator1\"/> \
2808 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2809 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2810 <separator name=\"Separator2\"/> \
2811 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2812 <separator name=\"Separator3\"/> \
2813 <placeholder name=\"FileRecentFiles\"/> \
2814 <separator name=\"Separator4\"/> \
2815 <menuitem name=\"Quit\" action=\"Quit\" /> \
2817 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2818 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2819 <separator name=\"Separator5\"/> \
2820 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2821 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2822 <separator name=\"Separator6\"/> \
2823 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2825 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2826 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2827 <separator name=\"Separator7\"/> \
2828 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2830 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2831 <menuitem name=\"About\" action=\"About\" /> \
2837 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2840 GtkActionGroup *action_group;
2843 action_group = gtk_action_group_new("Menu");
2844 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2846 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2847 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2849 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2851 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2854 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2855 GtkWidget *vbox, GtkUIManager *ui_manager)
2857 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2860 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2862 struct gui_entry *ge = (struct gui_entry *) data;
2865 index = gtk_combo_box_get_active(box);
2867 multitext_set_entry(&ge->eta.iotype, index);
2868 multitext_set_entry(&ge->eta.ioengine, index);
2869 multitext_set_entry(&ge->eta.iodepth, index);
2872 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2874 struct gui_entry *ge = (struct gui_entry *) data;
2876 multitext_free(&ge->eta.iotype);
2877 multitext_free(&ge->eta.ioengine);
2878 multitext_free(&ge->eta.iodepth);
2881 static GtkWidget *new_client_page(struct gui_entry *ge)
2883 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2884 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2886 main_vbox = gtk_vbox_new(FALSE, 3);
2888 top_align = gtk_alignment_new(0, 0, 1, 0);
2889 top_vbox = gtk_vbox_new(FALSE, 3);
2890 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2891 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2893 probe = gtk_frame_new("Job");
2894 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2895 probe_frame = gtk_vbox_new(FALSE, 3);
2896 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2898 probe_box = gtk_hbox_new(FALSE, 3);
2899 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2900 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2901 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2902 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2903 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2905 probe_box = gtk_hbox_new(FALSE, 3);
2906 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2908 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2909 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2910 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2911 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2912 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2913 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2914 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2915 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2917 probe_box = gtk_hbox_new(FALSE, 3);
2918 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2919 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2920 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2921 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2922 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2925 * Only add this if we have a commit rate
2928 probe_box = gtk_hbox_new(FALSE, 3);
2929 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2931 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2932 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2934 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2935 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2939 * Set up a drawing area and IOPS and bandwidth graphs
2941 ge->graphs.drawing_area = gtk_drawing_area_new();
2942 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2943 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2944 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2945 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2946 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2947 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2948 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2949 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2950 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2951 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2952 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2953 ge->graphs.drawing_area);
2954 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2956 setup_graphs(&ge->graphs);
2959 * Set up alignments for widgets at the bottom of ui,
2960 * align bottom left, expand horizontally but not vertically
2962 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2963 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2964 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2965 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2967 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2970 * Set up thread status progress bar
2972 ge->thread_status_pb = gtk_progress_bar_new();
2973 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2974 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2975 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2981 static GtkWidget *new_main_page(struct gui *ui)
2983 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2984 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2986 main_vbox = gtk_vbox_new(FALSE, 3);
2989 * Set up alignments for widgets at the top of ui,
2990 * align top left, expand horizontally but not vertically
2992 top_align = gtk_alignment_new(0, 0, 1, 0);
2993 top_vbox = gtk_vbox_new(FALSE, 0);
2994 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2995 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2997 probe = gtk_frame_new("Run statistics");
2998 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2999 probe_frame = gtk_vbox_new(FALSE, 3);
3000 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3002 probe_box = gtk_hbox_new(FALSE, 3);
3003 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3004 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3005 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3006 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3007 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3008 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3011 * Only add this if we have a commit rate
3014 probe_box = gtk_hbox_new(FALSE, 3);
3015 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3017 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3018 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3020 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3021 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3025 * Set up a drawing area and IOPS and bandwidth graphs
3027 ui->graphs.drawing_area = gtk_drawing_area_new();
3028 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3029 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3030 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3031 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3032 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3033 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3034 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3035 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3036 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3037 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3038 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3039 ui->graphs.drawing_area);
3040 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3043 setup_graphs(&ui->graphs);
3046 * Set up alignments for widgets at the bottom of ui,
3047 * align bottom left, expand horizontally but not vertically
3049 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3050 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3051 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3052 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3055 * Set up thread status progress bar
3057 ui->thread_status_pb = gtk_progress_bar_new();
3058 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3059 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3060 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3065 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3066 guint page, gpointer data)
3069 struct gui *ui = (struct gui *) data;
3070 struct gui_entry *ge;
3073 set_job_menu_visible(ui, 0);
3074 set_view_results_visible(ui, 0);
3078 set_job_menu_visible(ui, 1);
3079 ge = get_ge_from_page(page, NULL);
3081 update_button_states(ui, ge);
3086 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3088 time_t time_a = gtk_recent_info_get_visited(a);
3089 time_t time_b = gtk_recent_info_get_visited(b);
3091 return time_b - time_a;
3094 static void add_recent_file_items(struct gui *ui)
3096 const gchar *gfio = g_get_application_name();
3097 GList *items, *item;
3100 if (ui->recent_ui_id) {
3101 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3102 gtk_ui_manager_ensure_update(ui->uimanager);
3104 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3106 if (ui->actiongroup) {
3107 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3108 g_object_unref(ui->actiongroup);
3110 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3112 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3114 items = gtk_recent_manager_get_items(ui->recentmanager);
3115 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3117 for (item = items; item && item->data; item = g_list_next(item)) {
3118 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3123 if (!gtk_recent_info_has_application(info, gfio))
3127 * We only support local files for now
3129 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3132 action_name = g_strdup_printf("RecentFile%u", i++);
3133 label = gtk_recent_info_get_display_name(info);
3135 action = g_object_new(GTK_TYPE_ACTION,
3136 "name", action_name,
3137 "label", label, NULL);
3139 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3140 gtk_recent_info_ref(info),
3141 (GDestroyNotify) gtk_recent_info_unref);
3144 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3146 gtk_action_group_add_action(ui->actiongroup, action);
3147 g_object_unref(action);
3149 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3150 "/MainMenu/FileMenu/FileRecentFiles",
3152 GTK_UI_MANAGER_MENUITEM, FALSE);
3154 g_free(action_name);
3160 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3164 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3165 gint x, gint y, GtkSelectionData *data,
3166 guint info, guint time)
3168 struct gui *ui = &main_ui;
3173 source = gtk_drag_get_source_widget(ctx);
3174 if (source && widget == gtk_widget_get_toplevel(source)) {
3175 gtk_drag_finish(ctx, FALSE, FALSE, time);
3179 uris = gtk_selection_data_get_uris(data);
3181 gtk_drag_finish(ctx, FALSE, FALSE, time);
3187 if (do_file_open_with_tab(ui, uris[i]))
3192 gtk_drag_finish(ctx, TRUE, FALSE, time);
3196 static void init_ui(int *argc, char **argv[], struct gui *ui)
3198 GtkSettings *settings;
3201 /* Magical g*thread incantation, you just need this thread stuff.
3202 * Without it, the update that happens in gfio_update_thread_status
3203 * doesn't really happen in a timely fashion, you need expose events
3205 if (!g_thread_supported())
3206 g_thread_init(NULL);
3209 gtk_init(argc, argv);
3210 settings = gtk_settings_get_default();
3211 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3213 gdk_color_parse("white", &white);
3215 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3216 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3217 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3219 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3220 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3222 ui->vbox = gtk_vbox_new(FALSE, 0);
3223 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3225 ui->uimanager = gtk_ui_manager_new();
3226 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3227 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3229 ui->recentmanager = gtk_recent_manager_get_default();
3230 add_recent_file_items(ui);
3232 ui->notebook = gtk_notebook_new();
3233 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3234 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3235 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3236 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3238 vbox = new_main_page(ui);
3239 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3240 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3241 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3243 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3245 gfio_ui_setup_log(ui);
3247 gtk_widget_show_all(ui->window);
3250 int main(int argc, char *argv[], char *envp[])
3252 if (initialize_fio(envp))
3254 if (fio_init_options())
3257 memset(&main_ui, 0, sizeof(main_ui));
3258 INIT_FLIST_HEAD(&main_ui.list);
3260 init_ui(&argc, &argv, &main_ui);
3262 gdk_threads_enter();
3264 gdk_threads_leave();