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.thread_number = p->ts.thread_number;
1465 client_ts.groupid = p->ts.groupid;
1467 if (++sum_stat_nr == sum_stat_clients) {
1468 strcpy(client_ts.name, "All clients");
1469 gfio_display_ts(client, &client_ts, &client_gs);
1473 static void gfio_group_stats_op(struct fio_client *client,
1474 struct fio_net_cmd *cmd)
1476 /* We're ignoring group stats for now */
1479 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1482 struct gfio_graphs *g = data;
1484 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1485 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1486 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1487 graph_set_position(g->bandwidth_graph, 0, 0);
1491 static void draw_graph(struct graph *g, cairo_t *cr)
1493 line_graph_draw(g, cr);
1497 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1498 gboolean keyboard_mode, GtkTooltip *tooltip,
1501 struct gfio_graphs *g = data;
1502 const char *text = NULL;
1504 if (graph_contains_xy(g->iops_graph, x, y))
1505 text = graph_find_tooltip(g->iops_graph, x, y);
1506 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1507 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1510 gtk_tooltip_set_text(tooltip, text);
1517 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1519 struct gfio_graphs *g = p;
1522 cr = gdk_cairo_create(w->window);
1524 if (graph_has_tooltips(g->iops_graph) ||
1525 graph_has_tooltips(g->bandwidth_graph)) {
1526 g_object_set(w, "has-tooltip", TRUE, NULL);
1527 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1530 cairo_set_source_rgb(cr, 0, 0, 0);
1531 draw_graph(g->iops_graph, cr);
1532 draw_graph(g->bandwidth_graph, cr);
1539 * Client specific ETA
1541 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1543 struct gfio_client *gc = client->client_data;
1544 struct gui_entry *ge = gc->ge;
1545 static int eta_good;
1552 gdk_threads_enter();
1557 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1558 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1559 eta_to_str(eta_str, je->eta_sec);
1562 sprintf(tmp, "%u", je->nr_running);
1563 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1564 sprintf(tmp, "%u", je->files_open);
1565 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1568 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1569 if (je->m_rate || je->t_rate) {
1572 mr = num2str(je->m_rate, 4, 0, i2p);
1573 tr = num2str(je->t_rate, 4, 0, i2p);
1574 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1575 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1578 } else if (je->m_iops || je->t_iops)
1579 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1581 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1582 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1583 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1584 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1587 if (je->eta_sec != INT_MAX && je->nr_running) {
1591 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1592 strcpy(output, "-.-% done");
1596 sprintf(output, "%3.1f%% done", perc);
1599 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1600 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1602 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1603 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1605 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1606 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1607 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1608 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1610 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1611 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1612 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1613 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1622 char *dst = output + strlen(output);
1624 sprintf(dst, " - %s", eta_str);
1627 gfio_update_thread_status(ge, output, perc);
1628 gdk_threads_leave();
1632 * Update ETA in main window for all clients
1634 static void gfio_update_all_eta(struct jobs_eta *je)
1636 struct gui *ui = &main_ui;
1637 static int eta_good;
1643 gdk_threads_enter();
1648 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1649 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1650 eta_to_str(eta_str, je->eta_sec);
1654 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1655 if (je->m_rate || je->t_rate) {
1658 mr = num2str(je->m_rate, 4, 0, i2p);
1659 tr = num2str(je->t_rate, 4, 0, i2p);
1660 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1661 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1664 } else if (je->m_iops || je->t_iops)
1665 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1667 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1668 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1669 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1670 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1673 entry_set_int_value(ui->eta.jobs, je->nr_running);
1675 if (je->eta_sec != INT_MAX && je->nr_running) {
1679 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1680 strcpy(output, "-.-% done");
1684 sprintf(output, "%3.1f%% done", perc);
1687 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1688 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1690 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1691 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1693 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1694 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1695 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1696 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1698 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1699 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1700 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1701 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1710 char *dst = output + strlen(output);
1712 sprintf(dst, " - %s", eta_str);
1715 gfio_update_thread_status_all(output, perc);
1716 gdk_threads_leave();
1719 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1721 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1722 struct gfio_client *gc = client->client_data;
1723 struct gui_entry *ge = gc->ge;
1724 const char *os, *arch;
1727 os = fio_get_os_string(probe->os);
1731 arch = fio_get_arch_string(probe->arch);
1736 client->name = strdup((char *) probe->hostname);
1738 gdk_threads_enter();
1740 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1741 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1742 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1743 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1744 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1746 gfio_set_state(ge, GE_STATE_CONNECTED);
1748 gdk_threads_leave();
1751 static void gfio_update_thread_status(struct gui_entry *ge,
1752 char *status_message, double perc)
1754 static char message[100];
1755 const char *m = message;
1757 strncpy(message, status_message, sizeof(message) - 1);
1758 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1759 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1760 gtk_widget_queue_draw(main_ui.window);
1763 static void gfio_update_thread_status_all(char *status_message, double perc)
1765 struct gui *ui = &main_ui;
1766 static char message[100];
1767 const char *m = message;
1769 strncpy(message, status_message, sizeof(message) - 1);
1770 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1771 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1772 gtk_widget_queue_draw(ui->window);
1775 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1777 struct gfio_client *gc = client->client_data;
1779 gdk_threads_enter();
1780 gfio_set_state(gc->ge, GE_STATE_NEW);
1781 gdk_threads_leave();
1784 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1786 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1787 struct gfio_client *gc = client->client_data;
1788 struct thread_options *o = &gc->o;
1789 struct gui_entry *ge = gc->ge;
1792 p->thread_number = le32_to_cpu(p->thread_number);
1793 p->groupid = le32_to_cpu(p->groupid);
1794 convert_thread_options_to_cpu(o, &p->top);
1796 gdk_threads_enter();
1798 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1800 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1801 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1803 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1804 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1806 sprintf(tmp, "%u", o->iodepth);
1807 multitext_add_entry(&ge->eta.iodepth, tmp);
1809 multitext_set_entry(&ge->eta.iotype, 0);
1810 multitext_set_entry(&ge->eta.ioengine, 0);
1811 multitext_set_entry(&ge->eta.iodepth, 0);
1815 gfio_set_state(ge, GE_STATE_JOB_SENT);
1817 gdk_threads_leave();
1820 static void gfio_client_timed_out(struct fio_client *client)
1822 struct gfio_client *gc = client->client_data;
1825 gdk_threads_enter();
1827 gfio_set_state(gc->ge, GE_STATE_NEW);
1828 clear_ge_ui_info(gc->ge);
1830 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1831 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1833 gdk_threads_leave();
1836 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1838 struct gfio_client *gc = client->client_data;
1840 gdk_threads_enter();
1842 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1845 entry_set_int_value(gc->err_entry, client->error);
1847 gdk_threads_leave();
1850 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1852 struct gfio_client *gc = client->client_data;
1854 gdk_threads_enter();
1855 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1856 gdk_threads_leave();
1859 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1861 struct gfio_client *gc = client->client_data;
1863 gdk_threads_enter();
1864 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1865 gdk_threads_leave();
1868 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1870 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1874 struct client_ops gfio_client_ops = {
1875 .text = gfio_text_op,
1876 .disk_util = gfio_disk_util_op,
1877 .thread_status = gfio_thread_status_op,
1878 .group_stats = gfio_group_stats_op,
1879 .jobs_eta = gfio_update_client_eta,
1880 .eta = gfio_update_all_eta,
1881 .probe = gfio_probe_op,
1882 .quit = gfio_quit_op,
1883 .add_job = gfio_add_job_op,
1884 .timed_out = gfio_client_timed_out,
1885 .stop = gfio_client_stop,
1886 .start = gfio_client_start,
1887 .job_start = gfio_client_job_start,
1888 .iolog = gfio_client_iolog,
1889 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1890 .stay_connected = 1,
1891 .client_type = FIO_CLIENT_TYPE_GUI,
1895 * FIXME: need more handling here
1897 static void ge_destroy(struct gui_entry *ge)
1899 struct gfio_client *gc = ge->client;
1901 if (gc && gc->client) {
1902 if (ge->state >= GE_STATE_CONNECTED)
1903 fio_client_terminate(gc->client);
1905 fio_put_client(gc->client);
1908 flist_del(&ge->list);
1912 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1916 static void gfio_quit(struct gui *ui)
1918 struct gui_entry *ge;
1920 while (!flist_empty(&ui->list)) {
1921 ge = flist_entry(ui->list.next, struct gui_entry, list);
1928 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1929 __attribute__((unused)) gpointer data)
1934 static void *job_thread(void *arg)
1936 struct gui *ui = arg;
1938 ui->handler_running = 1;
1939 fio_handle_clients(&gfio_client_ops);
1940 ui->handler_running = 0;
1944 static int send_job_files(struct gui_entry *ge)
1946 struct gfio_client *gc = ge->client;
1949 for (i = 0; i < ge->nr_job_files; i++) {
1950 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1954 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1955 report_error(error);
1956 g_error_free(error);
1961 free(ge->job_files[i]);
1962 ge->job_files[i] = NULL;
1964 while (i < ge->nr_job_files) {
1965 free(ge->job_files[i]);
1966 ge->job_files[i] = NULL;
1970 free(ge->job_files);
1971 ge->job_files = NULL;
1972 ge->nr_job_files = 0;
1976 static void *server_thread(void *arg)
1979 gfio_server_running = 1;
1980 fio_start_server(NULL);
1981 gfio_server_running = 0;
1985 static void gfio_start_server(void)
1987 struct gui *ui = &main_ui;
1989 if (!gfio_server_running) {
1990 gfio_server_running = 1;
1991 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1992 pthread_detach(ui->server_t);
1996 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1999 struct gui_entry *ge = data;
2000 struct gfio_client *gc = ge->client;
2003 fio_start_client(gc->client);
2006 static void file_open(GtkWidget *w, gpointer data);
2008 static void connect_clicked(GtkWidget *widget, gpointer data)
2010 struct gui_entry *ge = data;
2011 struct gfio_client *gc = ge->client;
2013 if (ge->state == GE_STATE_NEW) {
2016 if (!ge->nr_job_files)
2017 file_open(widget, ge->ui);
2018 if (!ge->nr_job_files)
2021 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2022 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2023 ret = fio_client_connect(gc->client);
2025 if (!ge->ui->handler_running)
2026 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2027 gfio_set_state(ge, GE_STATE_CONNECTED);
2031 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2032 report_error(error);
2033 g_error_free(error);
2036 fio_client_terminate(gc->client);
2037 gfio_set_state(ge, GE_STATE_NEW);
2038 clear_ge_ui_info(ge);
2042 static void send_clicked(GtkWidget *widget, gpointer data)
2044 struct gui_entry *ge = data;
2046 if (send_job_files(ge)) {
2049 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);
2050 report_error(error);
2051 g_error_free(error);
2053 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2057 static GtkWidget *add_button(GtkWidget *buttonbox,
2058 struct button_spec *buttonspec, gpointer data)
2060 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
2062 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
2063 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
2064 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
2065 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
2070 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
2075 for (i = 0; i < nbuttons; i++)
2076 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
2079 static void on_info_bar_response(GtkWidget *widget, gint response,
2082 struct gui *ui = &main_ui;
2084 if (response == GTK_RESPONSE_OK) {
2085 gtk_widget_destroy(widget);
2086 ui->error_info_bar = NULL;
2090 void report_error(GError *error)
2092 struct gui *ui = &main_ui;
2094 if (ui->error_info_bar == NULL) {
2095 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2098 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2099 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2102 ui->error_label = gtk_label_new(error->message);
2103 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2104 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2106 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2107 gtk_widget_show_all(ui->vbox);
2110 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2111 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2115 struct connection_widgets
2122 static void hostname_cb(GtkEntry *entry, gpointer data)
2124 struct connection_widgets *cw = data;
2125 int uses_net = 0, is_localhost = 0;
2130 * Check whether to display the 'auto start backend' box
2131 * or not. Show it if we are a localhost and using network,
2132 * or using a socket.
2134 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2135 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2140 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2141 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2142 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2143 !strcmp(text, "ip6-loopback"))
2147 if (!uses_net || is_localhost) {
2148 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2149 gtk_widget_set_sensitive(cw->button, 1);
2151 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2152 gtk_widget_set_sensitive(cw->button, 0);
2156 static int get_connection_details(char **host, int *port, int *type,
2159 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2160 struct connection_widgets cw;
2163 dialog = gtk_dialog_new_with_buttons("Connection details",
2164 GTK_WINDOW(main_ui.window),
2165 GTK_DIALOG_DESTROY_WITH_PARENT,
2166 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2167 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2169 frame = gtk_frame_new("Hostname / socket name");
2170 /* gtk_dialog_get_content_area() is 2.14 and newer */
2171 vbox = GTK_DIALOG(dialog)->vbox;
2172 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2174 box = gtk_vbox_new(FALSE, 6);
2175 gtk_container_add(GTK_CONTAINER(frame), box);
2177 hbox = gtk_hbox_new(TRUE, 10);
2178 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2179 cw.hentry = gtk_entry_new();
2180 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2181 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2183 frame = gtk_frame_new("Port");
2184 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2185 box = gtk_vbox_new(FALSE, 10);
2186 gtk_container_add(GTK_CONTAINER(frame), box);
2188 hbox = gtk_hbox_new(TRUE, 4);
2189 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2190 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2192 frame = gtk_frame_new("Type");
2193 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2194 box = gtk_vbox_new(FALSE, 10);
2195 gtk_container_add(GTK_CONTAINER(frame), box);
2197 hbox = gtk_hbox_new(TRUE, 4);
2198 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2200 cw.combo = gtk_combo_box_new_text();
2201 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2202 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2203 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2204 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2206 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2208 frame = gtk_frame_new("Options");
2209 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2210 box = gtk_vbox_new(FALSE, 10);
2211 gtk_container_add(GTK_CONTAINER(frame), box);
2213 hbox = gtk_hbox_new(TRUE, 4);
2214 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2216 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2217 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2218 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.");
2219 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2222 * Connect edit signal, so we can show/not-show the auto start button
2224 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2225 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2227 gtk_widget_show_all(dialog);
2229 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2230 gtk_widget_destroy(dialog);
2234 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2235 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2237 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2238 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2239 *type = Fio_client_ipv4;
2240 else if (!strncmp(typeentry, "IPv6", 4))
2241 *type = Fio_client_ipv6;
2243 *type = Fio_client_socket;
2246 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2248 gtk_widget_destroy(dialog);
2252 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2254 struct gfio_client *gc;
2256 gc = malloc(sizeof(*gc));
2257 memset(gc, 0, sizeof(*gc));
2259 gc->client = fio_get_client(client);
2263 client->client_data = gc;
2266 static GtkWidget *new_client_page(struct gui_entry *ge);
2268 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2270 struct gui_entry *ge;
2272 ge = malloc(sizeof(*ge));
2273 memset(ge, 0, sizeof(*ge));
2274 ge->state = GE_STATE_NEW;
2275 INIT_FLIST_HEAD(&ge->list);
2276 flist_add_tail(&ge->list, &ui->list);
2281 static struct gui_entry *get_new_ge_with_tab(const char *name)
2283 struct gui_entry *ge;
2285 ge = alloc_new_gui_entry(&main_ui);
2287 ge->vbox = new_client_page(ge);
2288 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2290 ge->page_label = gtk_label_new(name);
2291 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2293 gtk_widget_show_all(main_ui.window);
2297 static void file_new(GtkWidget *w, gpointer data)
2299 struct gui *ui = (struct gui *) data;
2300 struct gui_entry *ge;
2302 ge = get_new_ge_with_tab("Untitled");
2303 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2307 * Return the 'ge' corresponding to the tab. If the active tab is the
2308 * main tab, open a new tab.
2310 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2312 struct flist_head *entry;
2313 struct gui_entry *ge;
2318 return get_new_ge_with_tab("Untitled");
2324 flist_for_each(entry, &main_ui.list) {
2325 ge = flist_entry(entry, struct gui_entry, list);
2326 if (ge->page_num == cur_page)
2333 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2338 * Main tab is tab 0, so any current page other than 0 holds
2341 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2343 return get_ge_from_page(cur_page, NULL);
2348 static void file_close(GtkWidget *w, gpointer data)
2350 struct gui *ui = (struct gui *) data;
2351 struct gui_entry *ge;
2354 * Can't close the main tab
2356 ge = get_ge_from_cur_tab(ui);
2358 gtk_widget_destroy(ge->vbox);
2362 if (!flist_empty(&ui->list)) {
2363 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2370 static void file_add_recent(struct gui *ui, const gchar *uri)
2374 memset(&grd, 0, sizeof(grd));
2375 grd.display_name = strdup("gfio");
2376 grd.description = strdup("Fio job file");
2377 grd.mime_type = strdup(GFIO_MIME);
2378 grd.app_name = strdup(g_get_application_name());
2379 grd.app_exec = strdup("gfio %f/%u");
2381 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2384 static gchar *get_filename_from_uri(const gchar *uri)
2386 if (strncmp(uri, "file://", 7))
2389 return strdup(uri + 7);
2392 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2395 struct fio_client *client;
2398 filename = get_filename_from_uri(uri);
2400 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2401 ge->job_files[ge->nr_job_files] = strdup(filename);
2404 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2408 error = g_error_new(g_quark_from_string("fio"), 1,
2409 "Failed to add client %s", host);
2410 report_error(error);
2411 g_error_free(error);
2415 gfio_client_added(ge, client);
2416 file_add_recent(ge->ui, uri);
2420 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2422 int port, type, server_start;
2423 struct gui_entry *ge;
2426 int ret, ge_is_new = 0;
2429 * Creates new tab if current tab is the main window, or the
2430 * current tab already has a client.
2432 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2433 ge = get_ge_from_page(cur_page, &ge_is_new);
2435 ge = get_new_ge_with_tab("Untitled");
2439 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2441 if (get_connection_details(&host, &port, &type, &server_start)) {
2443 gtk_widget_destroy(ge->vbox);
2448 ret = do_file_open(ge, uri, host, type, port);
2454 gfio_start_server();
2457 gtk_widget_destroy(ge->vbox);
2463 static void recent_open(GtkAction *action, gpointer data)
2465 struct gui *ui = (struct gui *) data;
2466 GtkRecentInfo *info;
2469 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2470 uri = gtk_recent_info_get_uri(info);
2472 do_file_open_with_tab(ui, uri);
2475 static void file_open(GtkWidget *w, gpointer data)
2477 struct gui *ui = data;
2479 GSList *filenames, *fn_glist;
2480 GtkFileFilter *filter;
2482 dialog = gtk_file_chooser_dialog_new("Open File",
2483 GTK_WINDOW(ui->window),
2484 GTK_FILE_CHOOSER_ACTION_OPEN,
2485 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2486 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2488 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2490 filter = gtk_file_filter_new();
2491 gtk_file_filter_add_pattern(filter, "*.fio");
2492 gtk_file_filter_add_pattern(filter, "*.job");
2493 gtk_file_filter_add_pattern(filter, "*.ini");
2494 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2495 gtk_file_filter_set_name(filter, "Fio job file");
2496 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2498 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2499 gtk_widget_destroy(dialog);
2503 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2505 gtk_widget_destroy(dialog);
2507 filenames = fn_glist;
2508 while (filenames != NULL) {
2509 if (do_file_open_with_tab(ui, filenames->data))
2511 filenames = g_slist_next(filenames);
2514 g_slist_free(fn_glist);
2517 static void file_save(GtkWidget *w, gpointer data)
2519 struct gui *ui = data;
2522 dialog = gtk_file_chooser_dialog_new("Save File",
2523 GTK_WINDOW(ui->window),
2524 GTK_FILE_CHOOSER_ACTION_SAVE,
2525 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2526 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2529 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2530 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2532 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2535 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2536 // save_job_file(filename);
2539 gtk_widget_destroy(dialog);
2542 static void view_log_destroy(GtkWidget *w, gpointer data)
2544 struct gui *ui = (struct gui *) data;
2546 gtk_widget_ref(ui->log_tree);
2547 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2548 gtk_widget_destroy(w);
2549 ui->log_view = NULL;
2552 static void view_log(GtkWidget *w, gpointer data)
2554 GtkWidget *win, *scroll, *vbox, *box;
2555 struct gui *ui = (struct gui *) data;
2560 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2561 gtk_window_set_title(GTK_WINDOW(win), "Log");
2562 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2564 scroll = gtk_scrolled_window_new(NULL, NULL);
2566 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2568 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2570 box = gtk_hbox_new(TRUE, 0);
2571 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2572 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2573 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2575 vbox = gtk_vbox_new(TRUE, 5);
2576 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2578 gtk_container_add(GTK_CONTAINER(win), vbox);
2579 gtk_widget_show_all(win);
2582 static void connect_job_entry(GtkWidget *w, gpointer data)
2584 struct gui *ui = (struct gui *) data;
2585 struct gui_entry *ge;
2587 ge = get_ge_from_cur_tab(ui);
2589 connect_clicked(w, ge);
2592 static void send_job_entry(GtkWidget *w, gpointer data)
2594 struct gui *ui = (struct gui *) data;
2595 struct gui_entry *ge;
2597 ge = get_ge_from_cur_tab(ui);
2599 send_clicked(w, ge);
2603 static void edit_job_entry(GtkWidget *w, gpointer data)
2607 static void start_job_entry(GtkWidget *w, gpointer data)
2609 struct gui *ui = (struct gui *) data;
2610 struct gui_entry *ge;
2612 ge = get_ge_from_cur_tab(ui);
2614 start_job_clicked(w, ge);
2617 static void view_results(GtkWidget *w, gpointer data)
2619 struct gui *ui = (struct gui *) data;
2620 struct gfio_client *gc;
2621 struct gui_entry *ge;
2623 ge = get_ge_from_cur_tab(ui);
2627 if (ge->results_window)
2631 if (gc && gc->nr_results)
2632 gfio_display_end_results(gc);
2636 static void __update_graph_limits(struct gfio_graphs *g)
2638 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2639 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2642 static void update_graph_limits(void)
2644 struct flist_head *entry;
2645 struct gui_entry *ge;
2647 __update_graph_limits(&main_ui.graphs);
2649 flist_for_each(entry, &main_ui.list) {
2650 ge = flist_entry(entry, struct gui_entry, list);
2651 __update_graph_limits(&ge->graphs);
2655 static void preferences(GtkWidget *w, gpointer data)
2657 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2658 GtkWidget *hbox, *spin, *entry, *spin_int;
2661 dialog = gtk_dialog_new_with_buttons("Preferences",
2662 GTK_WINDOW(main_ui.window),
2663 GTK_DIALOG_DESTROY_WITH_PARENT,
2664 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2665 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2668 frame = gtk_frame_new("Graphing");
2669 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2670 vbox = gtk_vbox_new(FALSE, 6);
2671 gtk_container_add(GTK_CONTAINER(frame), vbox);
2673 hbox = gtk_hbox_new(FALSE, 5);
2674 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2675 entry = gtk_label_new("Font face to use for graph labels");
2676 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2678 font = gtk_font_button_new();
2679 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2681 box = gtk_vbox_new(FALSE, 6);
2682 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2684 hbox = gtk_hbox_new(FALSE, 5);
2685 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2686 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2687 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2689 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2691 box = gtk_vbox_new(FALSE, 6);
2692 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2694 hbox = gtk_hbox_new(FALSE, 5);
2695 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2696 entry = gtk_label_new("Client ETA request interval (msec)");
2697 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2699 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2700 frame = gtk_frame_new("Debug logging");
2701 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2702 vbox = gtk_vbox_new(FALSE, 6);
2703 gtk_container_add(GTK_CONTAINER(frame), vbox);
2705 box = gtk_hbox_new(FALSE, 6);
2706 gtk_container_add(GTK_CONTAINER(vbox), box);
2708 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2710 for (i = 0; i < FD_DEBUG_MAX; i++) {
2712 box = gtk_hbox_new(FALSE, 6);
2713 gtk_container_add(GTK_CONTAINER(vbox), box);
2717 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2718 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2719 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2722 gtk_widget_show_all(dialog);
2724 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2725 gtk_widget_destroy(dialog);
2729 for (i = 0; i < FD_DEBUG_MAX; i++) {
2732 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2734 fio_debug |= (1UL << i);
2737 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2738 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2739 update_graph_limits();
2740 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2742 gtk_widget_destroy(dialog);
2745 static void about_dialog(GtkWidget *w, gpointer data)
2747 const char *authors[] = {
2748 "Jens Axboe <axboe@kernel.dk>",
2749 "Stephen Carmeron <stephenmcameron@gmail.com>",
2752 const char *license[] = {
2753 "Fio is free software; you can redistribute it and/or modify "
2754 "it under the terms of the GNU General Public License as published by "
2755 "the Free Software Foundation; either version 2 of the License, or "
2756 "(at your option) any later version.\n",
2757 "Fio is distributed in the hope that it will be useful, "
2758 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2759 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2760 "GNU General Public License for more details.\n",
2761 "You should have received a copy of the GNU General Public License "
2762 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2763 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2765 char *license_trans;
2767 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2768 license[2], "\n", NULL);
2770 gtk_show_about_dialog(NULL,
2771 "program-name", "gfio",
2772 "comments", "Gtk2 UI for fio",
2773 "license", license_trans,
2774 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2776 "version", fio_version_string,
2777 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2778 "logo-icon-name", "fio",
2780 "wrap-license", TRUE,
2783 g_free(license_trans);
2786 static GtkActionEntry menu_items[] = {
2787 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2788 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2789 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2790 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2791 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2792 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2793 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2794 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2795 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2796 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2797 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2798 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2799 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2800 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2801 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2802 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2803 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2805 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2807 static const gchar *ui_string = " \
2809 <menubar name=\"MainMenu\"> \
2810 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2811 <menuitem name=\"New\" action=\"NewFile\" /> \
2812 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2813 <separator name=\"Separator1\"/> \
2814 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2815 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2816 <separator name=\"Separator2\"/> \
2817 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2818 <separator name=\"Separator3\"/> \
2819 <placeholder name=\"FileRecentFiles\"/> \
2820 <separator name=\"Separator4\"/> \
2821 <menuitem name=\"Quit\" action=\"Quit\" /> \
2823 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2824 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2825 <separator name=\"Separator5\"/> \
2826 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2827 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2828 <separator name=\"Separator6\"/> \
2829 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2831 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2832 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2833 <separator name=\"Separator7\"/> \
2834 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2836 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2837 <menuitem name=\"About\" action=\"About\" /> \
2843 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2846 GtkActionGroup *action_group;
2849 action_group = gtk_action_group_new("Menu");
2850 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2852 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2853 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2855 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2857 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2860 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2861 GtkWidget *vbox, GtkUIManager *ui_manager)
2863 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2866 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2868 struct gui_entry *ge = (struct gui_entry *) data;
2871 index = gtk_combo_box_get_active(box);
2873 multitext_set_entry(&ge->eta.iotype, index);
2874 multitext_set_entry(&ge->eta.ioengine, index);
2875 multitext_set_entry(&ge->eta.iodepth, index);
2878 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2880 struct gui_entry *ge = (struct gui_entry *) data;
2882 multitext_free(&ge->eta.iotype);
2883 multitext_free(&ge->eta.ioengine);
2884 multitext_free(&ge->eta.iodepth);
2887 static GtkWidget *new_client_page(struct gui_entry *ge)
2889 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2890 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2892 main_vbox = gtk_vbox_new(FALSE, 3);
2894 top_align = gtk_alignment_new(0, 0, 1, 0);
2895 top_vbox = gtk_vbox_new(FALSE, 3);
2896 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2897 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2899 probe = gtk_frame_new("Job");
2900 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2901 probe_frame = gtk_vbox_new(FALSE, 3);
2902 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2904 probe_box = gtk_hbox_new(FALSE, 3);
2905 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2906 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2907 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2908 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2909 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2911 probe_box = gtk_hbox_new(FALSE, 3);
2912 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2914 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2915 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2916 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2917 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2918 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2919 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2920 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2921 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2923 probe_box = gtk_hbox_new(FALSE, 3);
2924 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2925 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2926 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2927 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2928 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2931 * Only add this if we have a commit rate
2934 probe_box = gtk_hbox_new(FALSE, 3);
2935 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2937 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2938 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2940 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2941 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2945 * Set up a drawing area and IOPS and bandwidth graphs
2947 ge->graphs.drawing_area = gtk_drawing_area_new();
2948 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2949 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2950 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2951 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2952 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2953 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2954 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2955 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2956 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2957 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2958 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2959 ge->graphs.drawing_area);
2960 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2962 setup_graphs(&ge->graphs);
2965 * Set up alignments for widgets at the bottom of ui,
2966 * align bottom left, expand horizontally but not vertically
2968 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2969 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2970 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2971 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2973 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2976 * Set up thread status progress bar
2978 ge->thread_status_pb = gtk_progress_bar_new();
2979 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2980 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2981 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2987 static GtkWidget *new_main_page(struct gui *ui)
2989 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2990 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2992 main_vbox = gtk_vbox_new(FALSE, 3);
2995 * Set up alignments for widgets at the top of ui,
2996 * align top left, expand horizontally but not vertically
2998 top_align = gtk_alignment_new(0, 0, 1, 0);
2999 top_vbox = gtk_vbox_new(FALSE, 0);
3000 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3001 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3003 probe = gtk_frame_new("Run statistics");
3004 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3005 probe_frame = gtk_vbox_new(FALSE, 3);
3006 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3008 probe_box = gtk_hbox_new(FALSE, 3);
3009 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3010 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3011 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3012 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3013 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3014 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3017 * Only add this if we have a commit rate
3020 probe_box = gtk_hbox_new(FALSE, 3);
3021 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3023 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3024 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3026 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3027 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3031 * Set up a drawing area and IOPS and bandwidth graphs
3033 ui->graphs.drawing_area = gtk_drawing_area_new();
3034 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3035 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3036 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3037 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3038 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3039 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3040 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3041 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3042 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3043 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3044 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3045 ui->graphs.drawing_area);
3046 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3049 setup_graphs(&ui->graphs);
3052 * Set up alignments for widgets at the bottom of ui,
3053 * align bottom left, expand horizontally but not vertically
3055 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3056 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3057 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3058 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3061 * Set up thread status progress bar
3063 ui->thread_status_pb = gtk_progress_bar_new();
3064 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3065 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3066 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3071 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3072 guint page, gpointer data)
3075 struct gui *ui = (struct gui *) data;
3076 struct gui_entry *ge;
3079 set_job_menu_visible(ui, 0);
3080 set_view_results_visible(ui, 0);
3084 set_job_menu_visible(ui, 1);
3085 ge = get_ge_from_page(page, NULL);
3087 update_button_states(ui, ge);
3092 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3094 time_t time_a = gtk_recent_info_get_visited(a);
3095 time_t time_b = gtk_recent_info_get_visited(b);
3097 return time_b - time_a;
3100 static void add_recent_file_items(struct gui *ui)
3102 const gchar *gfio = g_get_application_name();
3103 GList *items, *item;
3106 if (ui->recent_ui_id) {
3107 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3108 gtk_ui_manager_ensure_update(ui->uimanager);
3110 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3112 if (ui->actiongroup) {
3113 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3114 g_object_unref(ui->actiongroup);
3116 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3118 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3120 items = gtk_recent_manager_get_items(ui->recentmanager);
3121 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3123 for (item = items; item && item->data; item = g_list_next(item)) {
3124 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3129 if (!gtk_recent_info_has_application(info, gfio))
3133 * We only support local files for now
3135 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3138 action_name = g_strdup_printf("RecentFile%u", i++);
3139 label = gtk_recent_info_get_display_name(info);
3141 action = g_object_new(GTK_TYPE_ACTION,
3142 "name", action_name,
3143 "label", label, NULL);
3145 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3146 gtk_recent_info_ref(info),
3147 (GDestroyNotify) gtk_recent_info_unref);
3150 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3152 gtk_action_group_add_action(ui->actiongroup, action);
3153 g_object_unref(action);
3155 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3156 "/MainMenu/FileMenu/FileRecentFiles",
3158 GTK_UI_MANAGER_MENUITEM, FALSE);
3160 g_free(action_name);
3166 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3170 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3171 gint x, gint y, GtkSelectionData *data,
3172 guint info, guint time)
3174 struct gui *ui = &main_ui;
3179 source = gtk_drag_get_source_widget(ctx);
3180 if (source && widget == gtk_widget_get_toplevel(source)) {
3181 gtk_drag_finish(ctx, FALSE, FALSE, time);
3185 uris = gtk_selection_data_get_uris(data);
3187 gtk_drag_finish(ctx, FALSE, FALSE, time);
3193 if (do_file_open_with_tab(ui, uris[i]))
3198 gtk_drag_finish(ctx, TRUE, FALSE, time);
3202 static void init_ui(int *argc, char **argv[], struct gui *ui)
3204 GtkSettings *settings;
3207 /* Magical g*thread incantation, you just need this thread stuff.
3208 * Without it, the update that happens in gfio_update_thread_status
3209 * doesn't really happen in a timely fashion, you need expose events
3211 if (!g_thread_supported())
3212 g_thread_init(NULL);
3215 gtk_init(argc, argv);
3216 settings = gtk_settings_get_default();
3217 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3219 gdk_color_parse("white", &white);
3221 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3222 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3223 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3225 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3226 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3228 ui->vbox = gtk_vbox_new(FALSE, 0);
3229 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3231 ui->uimanager = gtk_ui_manager_new();
3232 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3233 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3235 ui->recentmanager = gtk_recent_manager_get_default();
3236 add_recent_file_items(ui);
3238 ui->notebook = gtk_notebook_new();
3239 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3240 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3241 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3242 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3244 vbox = new_main_page(ui);
3245 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3246 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3247 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3249 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3251 gfio_ui_setup_log(ui);
3253 gtk_widget_show_all(ui->window);
3256 int main(int argc, char *argv[], char *envp[])
3258 if (initialize_fio(envp))
3260 if (fio_init_options())
3263 memset(&main_ui, 0, sizeof(main_ui));
3264 INIT_FLIST_HEAD(&main_ui.list);
3266 init_ui(&argc, &argv, &main_ui);
3268 gdk_threads_enter();
3270 gdk_threads_leave();