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_window;
161 GtkWidget *results_notebook;
162 GtkUIManager *results_uimanager;
163 GtkWidget *results_menu;
164 GtkWidget *disk_util_vbox;
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 *err_entry;
192 unsigned int job_added;
193 struct thread_options o;
195 struct end_results *results;
196 unsigned int nr_results;
198 struct cmd_du_pdu *du;
202 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
203 static void gfio_update_thread_status_all(char *status_message, double perc);
204 void report_error(GError *error);
206 static struct graph *setup_iops_graph(void)
210 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
211 graph_title(g, "IOPS (IOs/sec)");
212 graph_x_title(g, "Time (secs)");
213 graph_add_label(g, "Read IOPS");
214 graph_add_label(g, "Write IOPS");
215 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
216 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
217 line_graph_set_data_count_limit(g, gfio_graph_limit);
218 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
222 static struct graph *setup_bandwidth_graph(void)
226 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
227 graph_title(g, "Bandwidth (bytes/sec)");
228 graph_x_title(g, "Time (secs)");
229 graph_add_label(g, "Read Bandwidth");
230 graph_add_label(g, "Write Bandwidth");
231 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
232 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
233 graph_set_base_offset(g, 1);
234 line_graph_set_data_count_limit(g, 100);
235 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
239 static void setup_graphs(struct gfio_graphs *g)
241 g->iops_graph = setup_iops_graph();
242 g->bandwidth_graph = setup_bandwidth_graph();
245 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
247 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
248 mt->text[mt->max_text] = strdup(text);
252 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
254 if (index >= mt->max_text)
256 if (!mt->text || !mt->text[index])
259 mt->cur_text = index;
260 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
263 static void multitext_update_entry(struct multitext_widget *mt,
264 unsigned int index, const char *text)
270 free(mt->text[index]);
272 mt->text[index] = strdup(text);
273 if (mt->cur_text == index)
274 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
277 static void multitext_free(struct multitext_widget *mt)
281 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
283 for (i = 0; i < mt->max_text; i++) {
293 static void clear_ge_ui_info(struct gui_entry *ge)
295 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
296 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
297 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
298 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
300 /* should we empty it... */
301 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
303 multitext_update_entry(&ge->eta.iotype, 0, "");
304 multitext_update_entry(&ge->eta.ioengine, 0, "");
305 multitext_update_entry(&ge->eta.iodepth, 0, "");
306 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
309 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
310 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
311 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
314 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
316 GtkWidget *entry, *frame;
318 frame = gtk_frame_new(label);
319 entry = gtk_combo_box_new_text();
320 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
321 gtk_container_add(GTK_CONTAINER(frame), entry);
326 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
328 GtkWidget *entry, *frame;
330 frame = gtk_frame_new(label);
331 entry = gtk_entry_new();
332 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
333 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
334 gtk_container_add(GTK_CONTAINER(frame), entry);
339 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
341 GtkWidget *label_widget;
344 frame = gtk_frame_new(label);
345 label_widget = gtk_label_new(NULL);
346 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
347 gtk_container_add(GTK_CONTAINER(frame), label_widget);
352 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
354 GtkWidget *button, *box;
356 box = gtk_hbox_new(FALSE, 3);
357 gtk_container_add(GTK_CONTAINER(hbox), box);
359 button = gtk_spin_button_new_with_range(min, max, 1.0);
360 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
362 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
363 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
368 static void label_set_int_value(GtkWidget *entry, unsigned int val)
372 sprintf(tmp, "%u", val);
373 gtk_label_set_text(GTK_LABEL(entry), tmp);
376 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
380 sprintf(tmp, "%u", val);
381 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
384 static void show_info_dialog(struct gui *ui, const char *title,
387 GtkWidget *dialog, *content, *label;
389 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
390 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
391 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
393 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
394 label = gtk_label_new(message);
395 gtk_container_add(GTK_CONTAINER(content), label);
396 gtk_widget_show_all(dialog);
397 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
398 gtk_dialog_run(GTK_DIALOG(dialog));
399 gtk_widget_destroy(dialog);
402 static void set_menu_entry_text(struct gui *ui, const char *path,
407 w = gtk_ui_manager_get_widget(ui->uimanager, path);
409 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
411 fprintf(stderr, "gfio: can't find path %s\n", path);
415 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
419 w = gtk_ui_manager_get_widget(ui->uimanager, path);
421 gtk_widget_set_sensitive(w, show);
423 fprintf(stderr, "gfio: can't find path %s\n", path);
426 static void set_job_menu_visible(struct gui *ui, int visible)
428 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
431 static void set_view_results_visible(struct gui *ui, int visible)
433 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
437 * Update sensitivity of job buttons and job menu items, based on the
438 * state of the client.
440 static void update_button_states(struct gui *ui, struct gui_entry *ge)
442 unsigned int connect_state, send_state, start_state, edit_state;
443 const char *connect_str = NULL;
449 sprintf(tmp, "Bad client state: %u\n", ge->state);
450 show_info_dialog(ui, "Error", tmp);
451 /* fall through to new state */
457 connect_str = "Connect";
461 case GE_STATE_CONNECTED:
464 connect_str = "Disconnect";
468 case GE_STATE_JOB_SENT:
471 connect_str = "Disconnect";
475 case GE_STATE_JOB_STARTED:
478 connect_str = "Disconnect";
482 case GE_STATE_JOB_RUNNING:
485 connect_str = "Disconnect";
489 case GE_STATE_JOB_DONE:
492 connect_str = "Connect";
498 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
499 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
500 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
501 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
503 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
504 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
506 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
507 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
508 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
510 if (ge->client && ge->client->nr_results)
511 set_view_results_visible(ui, 1);
513 set_view_results_visible(ui, 0);
516 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
519 update_button_states(ge->ui, ge);
523 #define ALIGN_RIGHT 2
527 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
529 GtkCellRenderer *renderer;
530 GtkTreeViewColumn *col;
531 double xalign = 0.0; /* left as default */
532 PangoAlignment align;
535 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
536 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
538 visible = !(flags & INVISIBLE);
540 renderer = gtk_cell_renderer_text_new();
541 col = gtk_tree_view_column_new();
543 gtk_tree_view_column_set_title(col, title);
544 if (!(flags & UNSORTABLE))
545 gtk_tree_view_column_set_sort_column_id(col, index);
546 gtk_tree_view_column_set_resizable(col, TRUE);
547 gtk_tree_view_column_pack_start(col, renderer, TRUE);
548 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
549 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
551 case PANGO_ALIGN_LEFT:
554 case PANGO_ALIGN_CENTER:
557 case PANGO_ALIGN_RIGHT:
561 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
562 gtk_tree_view_column_set_visible(col, visible);
563 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
567 static void gfio_ui_setup_log(struct gui *ui)
569 GtkTreeSelection *selection;
571 GtkWidget *tree_view;
573 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
575 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
576 gtk_widget_set_can_focus(tree_view, FALSE);
578 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
579 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
580 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
581 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
583 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
584 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
585 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
586 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
588 ui->log_model = model;
589 ui->log_tree = tree_view;
592 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
595 double xdim, double ydim)
600 g = graph_new(xdim, ydim, gfio_graph_font);
601 graph_title(g, title);
602 graph_x_title(g, "Percentile");
604 for (i = 0; i < len; i++) {
607 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
608 graph_add_label(g, fbuf);
609 graph_add_data(g, fbuf, (double) ovals[i]);
615 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
621 GType types[FIO_IO_U_LIST_MAX_LEN];
622 GtkWidget *tree_view;
623 GtkTreeSelection *selection;
628 for (i = 0; i < len; i++)
629 types[i] = G_TYPE_INT;
631 model = gtk_list_store_newv(len, types);
633 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
634 gtk_widget_set_can_focus(tree_view, FALSE);
636 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
637 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
639 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
640 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
642 for (i = 0; i < len; i++) {
645 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
646 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
649 gtk_list_store_append(model, &iter);
651 for (i = 0; i < len; i++) {
653 ovals[i] = (ovals[i] + 999) / 1000;
654 gtk_list_store_set(model, &iter, i, ovals[i], -1);
660 static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
665 cr = gdk_cairo_create(w->window);
667 if (graph_has_tooltips(g)) {
668 g_object_set(w, "has-tooltip", TRUE, NULL);
669 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
672 cairo_set_source_rgb(cr, 0, 0, 0);
673 bar_graph_draw(g, cr);
679 static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
682 struct graph *g = data;
684 graph_set_size(g, w->allocation.width, w->allocation.height);
685 graph_set_size(g, w->allocation.width, w->allocation.height);
686 graph_set_position(g, 0, 0);
690 static void gfio_show_clat_percentiles(struct gfio_client *gc,
691 GtkWidget *vbox, struct thread_stat *ts,
694 unsigned int *io_u_plat = ts->io_u_plat[ddir];
695 unsigned long nr = ts->clat_stat[ddir].samples;
696 fio_fp64_t *plist = ts->percentile_list;
697 unsigned int *ovals, len, minv, maxv, scale_down;
699 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
700 struct gui_entry *ge = gc->ge;
703 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
708 * We default to usecs, but if the value range is such that we
709 * should scale down to msecs, do that.
711 if (minv > 2000 && maxv > 99999) {
719 sprintf(tmp, "Completion percentiles (%s)", base);
720 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
721 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
723 frame = gtk_frame_new(tmp);
724 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
726 completion_vbox = gtk_vbox_new(FALSE, 3);
727 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
728 hbox = gtk_hbox_new(FALSE, 3);
729 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
730 drawing_area = gtk_drawing_area_new();
731 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
732 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
733 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
734 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
735 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
737 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
743 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
744 unsigned long max, double mean, double dev)
746 const char *base = "(usec)";
747 GtkWidget *hbox, *label, *frame;
751 if (!usec_to_msec(&min, &max, &mean, &dev))
754 minp = num2str(min, 6, 1, 0);
755 maxp = num2str(max, 6, 1, 0);
757 sprintf(tmp, "%s %s", name, base);
758 frame = gtk_frame_new(tmp);
759 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
761 hbox = gtk_hbox_new(FALSE, 3);
762 gtk_container_add(GTK_CONTAINER(frame), hbox);
764 label = new_info_label_in_frame(hbox, "Minimum");
765 gtk_label_set_text(GTK_LABEL(label), minp);
766 label = new_info_label_in_frame(hbox, "Maximum");
767 gtk_label_set_text(GTK_LABEL(label), maxp);
768 label = new_info_label_in_frame(hbox, "Average");
769 sprintf(tmp, "%5.02f", mean);
770 gtk_label_set_text(GTK_LABEL(label), tmp);
771 label = new_info_label_in_frame(hbox, "Standard deviation");
772 sprintf(tmp, "%5.02f", dev);
773 gtk_label_set_text(GTK_LABEL(label), tmp);
784 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
785 struct group_run_stats *rs,
786 struct thread_stat *ts, int ddir)
788 const char *ddir_label[2] = { "Read", "Write" };
789 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
790 unsigned long min[3], max[3], runt;
791 unsigned long long bw, iops;
792 unsigned int flags = 0;
793 double mean[3], dev[3];
794 char *io_p, *bw_p, *iops_p;
797 if (!ts->runtime[ddir])
800 i2p = is_power_of_2(rs->kb_base);
801 runt = ts->runtime[ddir];
803 bw = (1000 * ts->io_bytes[ddir]) / runt;
804 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
805 bw_p = num2str(bw, 6, 1, i2p);
807 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
808 iops_p = num2str(iops, 6, 1, 0);
810 box = gtk_hbox_new(FALSE, 3);
811 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
813 frame = gtk_frame_new(ddir_label[ddir]);
814 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
816 main_vbox = gtk_vbox_new(FALSE, 3);
817 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
819 box = gtk_hbox_new(FALSE, 3);
820 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
822 label = new_info_label_in_frame(box, "IO");
823 gtk_label_set_text(GTK_LABEL(label), io_p);
824 label = new_info_label_in_frame(box, "Bandwidth");
825 gtk_label_set_text(GTK_LABEL(label), bw_p);
826 label = new_info_label_in_frame(box, "IOPS");
827 gtk_label_set_text(GTK_LABEL(label), iops_p);
828 label = new_info_label_in_frame(box, "Runtime (msec)");
829 label_set_int_value(label, ts->runtime[ddir]);
831 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
832 double p_of_agg = 100.0;
833 const char *bw_str = "KB";
837 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
838 if (p_of_agg > 100.0)
842 if (mean[0] > 999999.9) {
850 sprintf(tmp, "Bandwidth (%s)", bw_str);
851 frame = gtk_frame_new(tmp);
852 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
854 box = gtk_hbox_new(FALSE, 3);
855 gtk_container_add(GTK_CONTAINER(frame), box);
857 label = new_info_label_in_frame(box, "Minimum");
858 label_set_int_value(label, min[0]);
859 label = new_info_label_in_frame(box, "Maximum");
860 label_set_int_value(label, max[0]);
861 label = new_info_label_in_frame(box, "Percentage of jobs");
862 sprintf(tmp, "%3.2f%%", p_of_agg);
863 gtk_label_set_text(GTK_LABEL(label), tmp);
864 label = new_info_label_in_frame(box, "Average");
865 sprintf(tmp, "%5.02f", mean[0]);
866 gtk_label_set_text(GTK_LABEL(label), tmp);
867 label = new_info_label_in_frame(box, "Standard deviation");
868 sprintf(tmp, "%5.02f", dev[0]);
869 gtk_label_set_text(GTK_LABEL(label), tmp);
872 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
874 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
876 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
880 frame = gtk_frame_new("Latency");
881 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
883 vbox = gtk_vbox_new(FALSE, 3);
884 gtk_container_add(GTK_CONTAINER(frame), vbox);
886 if (flags & GFIO_SLAT)
887 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
888 if (flags & GFIO_CLAT)
889 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
890 if (flags & GFIO_LAT)
891 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
894 if (ts->clat_percentiles)
895 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 disk_util_destroy(GtkWidget *w, gpointer data)
1253 struct gui_entry *ge = (struct gui_entry *) data;
1255 ge->disk_util_vbox = NULL;
1256 gtk_widget_destroy(w);
1259 static int __gfio_disk_util_show(GtkWidget *res_notebook,
1260 struct gfio_client *gc, struct cmd_du_pdu *p)
1262 GtkWidget *box, *frame, *entry, *vbox;
1263 struct gui_entry *ge = gc->ge;
1267 res_notebook = get_results_window(ge);
1269 if (!ge->disk_util_vbox) {
1270 vbox = gtk_vbox_new(FALSE, 3);
1271 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), vbox, gtk_label_new("Disk utilization"));
1272 ge->disk_util_vbox = vbox;
1273 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1276 vbox = gtk_vbox_new(FALSE, 3);
1277 gtk_container_add(GTK_CONTAINER(ge->disk_util_vbox), vbox);
1279 frame = gtk_frame_new((char *) p->dus.name);
1280 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1282 box = gtk_vbox_new(FALSE, 3);
1283 gtk_container_add(GTK_CONTAINER(frame), box);
1285 frame = gtk_frame_new("Read");
1286 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1287 vbox = gtk_hbox_new(TRUE, 3);
1288 gtk_container_add(GTK_CONTAINER(frame), vbox);
1289 entry = new_info_entry_in_frame(vbox, "IOs");
1290 entry_set_int_value(entry, p->dus.ios[0]);
1291 entry = new_info_entry_in_frame(vbox, "Merges");
1292 entry_set_int_value(entry, p->dus.merges[0]);
1293 entry = new_info_entry_in_frame(vbox, "Sectors");
1294 entry_set_int_value(entry, p->dus.sectors[0]);
1295 entry = new_info_entry_in_frame(vbox, "Ticks");
1296 entry_set_int_value(entry, p->dus.ticks[0]);
1298 frame = gtk_frame_new("Write");
1299 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1300 vbox = gtk_hbox_new(TRUE, 3);
1301 gtk_container_add(GTK_CONTAINER(frame), vbox);
1302 entry = new_info_entry_in_frame(vbox, "IOs");
1303 entry_set_int_value(entry, p->dus.ios[1]);
1304 entry = new_info_entry_in_frame(vbox, "Merges");
1305 entry_set_int_value(entry, p->dus.merges[1]);
1306 entry = new_info_entry_in_frame(vbox, "Sectors");
1307 entry_set_int_value(entry, p->dus.sectors[1]);
1308 entry = new_info_entry_in_frame(vbox, "Ticks");
1309 entry_set_int_value(entry, p->dus.ticks[1]);
1311 frame = gtk_frame_new("Shared");
1312 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1313 vbox = gtk_hbox_new(TRUE, 3);
1314 gtk_container_add(GTK_CONTAINER(frame), vbox);
1315 entry = new_info_entry_in_frame(vbox, "IO ticks");
1316 entry_set_int_value(entry, p->dus.io_ticks);
1317 entry = new_info_entry_in_frame(vbox, "Time in queue");
1318 entry_set_int_value(entry, p->dus.time_in_queue);
1322 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1326 sprintf(tmp, "%3.2f%%", util);
1327 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1328 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1330 gtk_widget_show_all(ge->results_window);
1334 static int gfio_disk_util_show(struct gfio_client *gc)
1336 struct gui_entry *ge = gc->ge;
1337 GtkWidget *res_notebook;
1343 res_notebook = get_results_window(ge);
1345 for (i = 0; i < gc->nr_du; i++) {
1346 struct cmd_du_pdu *p = &gc->du[i];
1348 __gfio_disk_util_show(res_notebook, gc, p);
1351 gtk_widget_show_all(ge->results_window);
1355 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1356 struct group_run_stats *rs)
1358 unsigned int nr = gc->nr_results;
1360 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1361 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1362 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1366 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1367 struct thread_stat *ts,
1368 struct group_run_stats *rs)
1370 GtkWidget *box, *vbox, *entry, *scroll;
1372 scroll = gtk_scrolled_window_new(NULL, NULL);
1373 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1374 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1376 vbox = gtk_vbox_new(FALSE, 3);
1378 box = gtk_hbox_new(FALSE, 0);
1379 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1381 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1383 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1385 entry = new_info_entry_in_frame(box, "Name");
1386 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1387 if (strlen(ts->description)) {
1388 entry = new_info_entry_in_frame(box, "Description");
1389 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1391 entry = new_info_entry_in_frame(box, "Group ID");
1392 entry_set_int_value(entry, ts->groupid);
1393 entry = new_info_entry_in_frame(box, "Jobs");
1394 entry_set_int_value(entry, ts->members);
1395 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1396 entry_set_int_value(entry, ts->error);
1397 entry = new_info_entry_in_frame(box, "PID");
1398 entry_set_int_value(entry, ts->pid);
1400 if (ts->io_bytes[DDIR_READ])
1401 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1402 if (ts->io_bytes[DDIR_WRITE])
1403 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1405 gfio_show_latency_buckets(gc, vbox, ts);
1406 gfio_show_cpu_usage(vbox, ts);
1407 gfio_show_io_depths(vbox, ts);
1410 static void gfio_display_end_results(struct gfio_client *gc)
1412 struct gui_entry *ge = gc->ge;
1413 GtkWidget *res_notebook;
1416 res_notebook = get_results_window(ge);
1418 for (i = 0; i < gc->nr_results; i++) {
1419 struct end_results *e = &gc->results[i];
1421 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1424 if (gfio_disk_util_show(gc))
1425 gtk_widget_show_all(ge->results_window);
1428 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1429 struct group_run_stats *rs)
1431 struct gfio_client *gc = client->client_data;
1433 gfio_add_end_results(gc, ts, rs);
1435 gdk_threads_enter();
1436 gfio_display_end_results(gc);
1437 gdk_threads_leave();
1440 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1442 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1443 struct gui *ui = &main_ui;
1447 char tmp[64], timebuf[80];
1450 tm = localtime(&sec);
1451 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1452 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1454 gdk_threads_enter();
1456 gtk_list_store_append(ui->log_model, &iter);
1457 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1458 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1459 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1460 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1462 if (p->level == FIO_LOG_ERR)
1463 view_log(NULL, (gpointer) ui);
1465 gdk_threads_leave();
1468 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1470 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1471 struct gfio_client *gc = client->client_data;
1472 unsigned int nr = gc->nr_du;
1474 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1475 memcpy(&gc->du[nr], p, sizeof(*p));
1478 gdk_threads_enter();
1479 gfio_disk_util_show(gc);
1480 gdk_threads_leave();
1483 extern int sum_stat_clients;
1484 extern struct thread_stat client_ts;
1485 extern struct group_run_stats client_gs;
1487 static int sum_stat_nr;
1489 static void gfio_thread_status_op(struct fio_client *client,
1490 struct fio_net_cmd *cmd)
1492 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1494 gfio_display_ts(client, &p->ts, &p->rs);
1496 if (sum_stat_clients == 1)
1499 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1500 sum_group_stats(&client_gs, &p->rs);
1502 client_ts.members++;
1503 client_ts.thread_number = p->ts.thread_number;
1504 client_ts.groupid = p->ts.groupid;
1506 if (++sum_stat_nr == sum_stat_clients) {
1507 strcpy(client_ts.name, "All clients");
1508 gfio_display_ts(client, &client_ts, &client_gs);
1512 static void gfio_group_stats_op(struct fio_client *client,
1513 struct fio_net_cmd *cmd)
1515 /* We're ignoring group stats for now */
1518 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1521 struct gfio_graphs *g = data;
1523 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1524 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1525 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1526 graph_set_position(g->bandwidth_graph, 0, 0);
1530 static void draw_graph(struct graph *g, cairo_t *cr)
1532 line_graph_draw(g, cr);
1536 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1537 gboolean keyboard_mode, GtkTooltip *tooltip,
1540 struct gfio_graphs *g = data;
1541 const char *text = NULL;
1543 if (graph_contains_xy(g->iops_graph, x, y))
1544 text = graph_find_tooltip(g->iops_graph, x, y);
1545 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1546 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1549 gtk_tooltip_set_text(tooltip, text);
1556 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1558 struct gfio_graphs *g = p;
1561 cr = gdk_cairo_create(w->window);
1563 if (graph_has_tooltips(g->iops_graph) ||
1564 graph_has_tooltips(g->bandwidth_graph)) {
1565 g_object_set(w, "has-tooltip", TRUE, NULL);
1566 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1569 cairo_set_source_rgb(cr, 0, 0, 0);
1570 draw_graph(g->iops_graph, cr);
1571 draw_graph(g->bandwidth_graph, cr);
1578 * Client specific ETA
1580 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1582 struct gfio_client *gc = client->client_data;
1583 struct gui_entry *ge = gc->ge;
1584 static int eta_good;
1591 gdk_threads_enter();
1596 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1597 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1598 eta_to_str(eta_str, je->eta_sec);
1601 sprintf(tmp, "%u", je->nr_running);
1602 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1603 sprintf(tmp, "%u", je->files_open);
1604 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1607 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1608 if (je->m_rate || je->t_rate) {
1611 mr = num2str(je->m_rate, 4, 0, i2p);
1612 tr = num2str(je->t_rate, 4, 0, i2p);
1613 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1614 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1617 } else if (je->m_iops || je->t_iops)
1618 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1620 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1621 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1622 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1623 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1626 if (je->eta_sec != INT_MAX && je->nr_running) {
1630 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1631 strcpy(output, "-.-% done");
1635 sprintf(output, "%3.1f%% done", perc);
1638 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1639 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1641 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1642 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1644 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1645 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1646 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1647 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1649 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1650 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1651 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1652 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1661 char *dst = output + strlen(output);
1663 sprintf(dst, " - %s", eta_str);
1666 gfio_update_thread_status(ge, output, perc);
1667 gdk_threads_leave();
1671 * Update ETA in main window for all clients
1673 static void gfio_update_all_eta(struct jobs_eta *je)
1675 struct gui *ui = &main_ui;
1676 static int eta_good;
1682 gdk_threads_enter();
1687 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1688 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1689 eta_to_str(eta_str, je->eta_sec);
1693 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1694 if (je->m_rate || je->t_rate) {
1697 mr = num2str(je->m_rate, 4, 0, i2p);
1698 tr = num2str(je->t_rate, 4, 0, i2p);
1699 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1700 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1703 } else if (je->m_iops || je->t_iops)
1704 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1706 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1707 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1708 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1709 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1712 entry_set_int_value(ui->eta.jobs, je->nr_running);
1714 if (je->eta_sec != INT_MAX && je->nr_running) {
1718 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1719 strcpy(output, "-.-% done");
1723 sprintf(output, "%3.1f%% done", perc);
1726 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1727 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1729 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1730 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1732 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1733 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1734 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1735 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1737 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1738 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1739 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1740 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1749 char *dst = output + strlen(output);
1751 sprintf(dst, " - %s", eta_str);
1754 gfio_update_thread_status_all(output, perc);
1755 gdk_threads_leave();
1758 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1760 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1761 struct gfio_client *gc = client->client_data;
1762 struct gui_entry *ge = gc->ge;
1763 const char *os, *arch;
1766 os = fio_get_os_string(probe->os);
1770 arch = fio_get_arch_string(probe->arch);
1775 client->name = strdup((char *) probe->hostname);
1777 gdk_threads_enter();
1779 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1780 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1781 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1782 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1783 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1785 gfio_set_state(ge, GE_STATE_CONNECTED);
1787 gdk_threads_leave();
1790 static void gfio_update_thread_status(struct gui_entry *ge,
1791 char *status_message, double perc)
1793 static char message[100];
1794 const char *m = message;
1796 strncpy(message, status_message, sizeof(message) - 1);
1797 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1798 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1799 gtk_widget_queue_draw(main_ui.window);
1802 static void gfio_update_thread_status_all(char *status_message, double perc)
1804 struct gui *ui = &main_ui;
1805 static char message[100];
1806 const char *m = message;
1808 strncpy(message, status_message, sizeof(message) - 1);
1809 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1810 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1811 gtk_widget_queue_draw(ui->window);
1814 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1816 struct gfio_client *gc = client->client_data;
1818 gdk_threads_enter();
1819 gfio_set_state(gc->ge, GE_STATE_NEW);
1820 gdk_threads_leave();
1823 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1825 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1826 struct gfio_client *gc = client->client_data;
1827 struct thread_options *o = &gc->o;
1828 struct gui_entry *ge = gc->ge;
1831 p->thread_number = le32_to_cpu(p->thread_number);
1832 p->groupid = le32_to_cpu(p->groupid);
1833 convert_thread_options_to_cpu(o, &p->top);
1835 gdk_threads_enter();
1837 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1839 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1840 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1842 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1843 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1845 sprintf(tmp, "%u", o->iodepth);
1846 multitext_add_entry(&ge->eta.iodepth, tmp);
1848 multitext_set_entry(&ge->eta.iotype, 0);
1849 multitext_set_entry(&ge->eta.ioengine, 0);
1850 multitext_set_entry(&ge->eta.iodepth, 0);
1854 gfio_set_state(ge, GE_STATE_JOB_SENT);
1856 gdk_threads_leave();
1859 static void gfio_client_timed_out(struct fio_client *client)
1861 struct gfio_client *gc = client->client_data;
1864 gdk_threads_enter();
1866 gfio_set_state(gc->ge, GE_STATE_NEW);
1867 clear_ge_ui_info(gc->ge);
1869 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1870 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1872 gdk_threads_leave();
1875 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1877 struct gfio_client *gc = client->client_data;
1879 gdk_threads_enter();
1881 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1884 entry_set_int_value(gc->err_entry, client->error);
1886 gdk_threads_leave();
1889 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1891 struct gfio_client *gc = client->client_data;
1893 gdk_threads_enter();
1894 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1895 gdk_threads_leave();
1898 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1900 struct gfio_client *gc = client->client_data;
1902 gdk_threads_enter();
1903 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1904 gdk_threads_leave();
1907 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1909 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1913 struct client_ops gfio_client_ops = {
1914 .text = gfio_text_op,
1915 .disk_util = gfio_disk_util_op,
1916 .thread_status = gfio_thread_status_op,
1917 .group_stats = gfio_group_stats_op,
1918 .jobs_eta = gfio_update_client_eta,
1919 .eta = gfio_update_all_eta,
1920 .probe = gfio_probe_op,
1921 .quit = gfio_quit_op,
1922 .add_job = gfio_add_job_op,
1923 .timed_out = gfio_client_timed_out,
1924 .stop = gfio_client_stop,
1925 .start = gfio_client_start,
1926 .job_start = gfio_client_job_start,
1927 .iolog = gfio_client_iolog,
1928 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1929 .stay_connected = 1,
1930 .client_type = FIO_CLIENT_TYPE_GUI,
1934 * FIXME: need more handling here
1936 static void ge_destroy(struct gui_entry *ge)
1938 struct gfio_client *gc = ge->client;
1940 if (gc && gc->client) {
1941 if (ge->state >= GE_STATE_CONNECTED)
1942 fio_client_terminate(gc->client);
1944 fio_put_client(gc->client);
1947 flist_del(&ge->list);
1951 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1955 static void gfio_quit(struct gui *ui)
1957 struct gui_entry *ge;
1959 while (!flist_empty(&ui->list)) {
1960 ge = flist_entry(ui->list.next, struct gui_entry, list);
1967 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1968 __attribute__((unused)) gpointer data)
1973 static void *job_thread(void *arg)
1975 struct gui *ui = arg;
1977 ui->handler_running = 1;
1978 fio_handle_clients(&gfio_client_ops);
1979 ui->handler_running = 0;
1983 static int send_job_files(struct gui_entry *ge)
1985 struct gfio_client *gc = ge->client;
1988 for (i = 0; i < ge->nr_job_files; i++) {
1989 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1993 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1994 report_error(error);
1995 g_error_free(error);
2000 free(ge->job_files[i]);
2001 ge->job_files[i] = NULL;
2003 while (i < ge->nr_job_files) {
2004 free(ge->job_files[i]);
2005 ge->job_files[i] = NULL;
2009 free(ge->job_files);
2010 ge->job_files = NULL;
2011 ge->nr_job_files = 0;
2015 static void *server_thread(void *arg)
2018 gfio_server_running = 1;
2019 fio_start_server(NULL);
2020 gfio_server_running = 0;
2024 static void gfio_start_server(void)
2026 struct gui *ui = &main_ui;
2028 if (!gfio_server_running) {
2029 gfio_server_running = 1;
2030 pthread_create(&ui->server_t, NULL, server_thread, NULL);
2031 pthread_detach(ui->server_t);
2035 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2038 struct gui_entry *ge = data;
2039 struct gfio_client *gc = ge->client;
2042 fio_start_client(gc->client);
2045 static void file_open(GtkWidget *w, gpointer data);
2047 static void connect_clicked(GtkWidget *widget, gpointer data)
2049 struct gui_entry *ge = data;
2050 struct gfio_client *gc = ge->client;
2052 if (ge->state == GE_STATE_NEW) {
2055 if (!ge->nr_job_files)
2056 file_open(widget, ge->ui);
2057 if (!ge->nr_job_files)
2060 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2061 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2062 ret = fio_client_connect(gc->client);
2064 if (!ge->ui->handler_running)
2065 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2066 gfio_set_state(ge, GE_STATE_CONNECTED);
2070 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2071 report_error(error);
2072 g_error_free(error);
2075 fio_client_terminate(gc->client);
2076 gfio_set_state(ge, GE_STATE_NEW);
2077 clear_ge_ui_info(ge);
2081 static void send_clicked(GtkWidget *widget, gpointer data)
2083 struct gui_entry *ge = data;
2085 if (send_job_files(ge)) {
2088 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);
2089 report_error(error);
2090 g_error_free(error);
2092 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2096 static GtkWidget *add_button(GtkWidget *buttonbox,
2097 struct button_spec *buttonspec, gpointer data)
2099 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
2101 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
2102 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
2103 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
2104 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
2109 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
2114 for (i = 0; i < nbuttons; i++)
2115 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
2118 static void on_info_bar_response(GtkWidget *widget, gint response,
2121 struct gui *ui = &main_ui;
2123 if (response == GTK_RESPONSE_OK) {
2124 gtk_widget_destroy(widget);
2125 ui->error_info_bar = NULL;
2129 void report_error(GError *error)
2131 struct gui *ui = &main_ui;
2133 if (ui->error_info_bar == NULL) {
2134 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2137 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2138 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2141 ui->error_label = gtk_label_new(error->message);
2142 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2143 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2145 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2146 gtk_widget_show_all(ui->vbox);
2149 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2150 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2154 struct connection_widgets
2161 static void hostname_cb(GtkEntry *entry, gpointer data)
2163 struct connection_widgets *cw = data;
2164 int uses_net = 0, is_localhost = 0;
2169 * Check whether to display the 'auto start backend' box
2170 * or not. Show it if we are a localhost and using network,
2171 * or using a socket.
2173 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2174 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2179 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2180 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2181 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2182 !strcmp(text, "ip6-loopback"))
2186 if (!uses_net || is_localhost) {
2187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2188 gtk_widget_set_sensitive(cw->button, 1);
2190 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2191 gtk_widget_set_sensitive(cw->button, 0);
2195 static int get_connection_details(char **host, int *port, int *type,
2198 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2199 struct connection_widgets cw;
2202 dialog = gtk_dialog_new_with_buttons("Connection details",
2203 GTK_WINDOW(main_ui.window),
2204 GTK_DIALOG_DESTROY_WITH_PARENT,
2205 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2206 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2208 frame = gtk_frame_new("Hostname / socket name");
2209 /* gtk_dialog_get_content_area() is 2.14 and newer */
2210 vbox = GTK_DIALOG(dialog)->vbox;
2211 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2213 box = gtk_vbox_new(FALSE, 6);
2214 gtk_container_add(GTK_CONTAINER(frame), box);
2216 hbox = gtk_hbox_new(TRUE, 10);
2217 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2218 cw.hentry = gtk_entry_new();
2219 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2220 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2222 frame = gtk_frame_new("Port");
2223 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2224 box = gtk_vbox_new(FALSE, 10);
2225 gtk_container_add(GTK_CONTAINER(frame), box);
2227 hbox = gtk_hbox_new(TRUE, 4);
2228 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2229 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2231 frame = gtk_frame_new("Type");
2232 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2233 box = gtk_vbox_new(FALSE, 10);
2234 gtk_container_add(GTK_CONTAINER(frame), box);
2236 hbox = gtk_hbox_new(TRUE, 4);
2237 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2239 cw.combo = gtk_combo_box_new_text();
2240 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2241 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2242 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2243 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2245 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2247 frame = gtk_frame_new("Options");
2248 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2249 box = gtk_vbox_new(FALSE, 10);
2250 gtk_container_add(GTK_CONTAINER(frame), box);
2252 hbox = gtk_hbox_new(TRUE, 4);
2253 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2255 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2256 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2257 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.");
2258 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2261 * Connect edit signal, so we can show/not-show the auto start button
2263 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2264 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2266 gtk_widget_show_all(dialog);
2268 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2269 gtk_widget_destroy(dialog);
2273 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2274 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2276 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2277 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2278 *type = Fio_client_ipv4;
2279 else if (!strncmp(typeentry, "IPv6", 4))
2280 *type = Fio_client_ipv6;
2282 *type = Fio_client_socket;
2285 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2287 gtk_widget_destroy(dialog);
2291 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2293 struct gfio_client *gc;
2295 gc = malloc(sizeof(*gc));
2296 memset(gc, 0, sizeof(*gc));
2298 gc->client = fio_get_client(client);
2302 client->client_data = gc;
2305 static GtkWidget *new_client_page(struct gui_entry *ge);
2307 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2309 struct gui_entry *ge;
2311 ge = malloc(sizeof(*ge));
2312 memset(ge, 0, sizeof(*ge));
2313 ge->state = GE_STATE_NEW;
2314 INIT_FLIST_HEAD(&ge->list);
2315 flist_add_tail(&ge->list, &ui->list);
2320 static struct gui_entry *get_new_ge_with_tab(const char *name)
2322 struct gui_entry *ge;
2324 ge = alloc_new_gui_entry(&main_ui);
2326 ge->vbox = new_client_page(ge);
2327 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2329 ge->page_label = gtk_label_new(name);
2330 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2332 gtk_widget_show_all(main_ui.window);
2336 static void file_new(GtkWidget *w, gpointer data)
2338 struct gui *ui = (struct gui *) data;
2339 struct gui_entry *ge;
2341 ge = get_new_ge_with_tab("Untitled");
2342 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2346 * Return the 'ge' corresponding to the tab. If the active tab is the
2347 * main tab, open a new tab.
2349 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2351 struct flist_head *entry;
2352 struct gui_entry *ge;
2357 return get_new_ge_with_tab("Untitled");
2363 flist_for_each(entry, &main_ui.list) {
2364 ge = flist_entry(entry, struct gui_entry, list);
2365 if (ge->page_num == cur_page)
2372 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2377 * Main tab is tab 0, so any current page other than 0 holds
2380 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2382 return get_ge_from_page(cur_page, NULL);
2387 static void file_close(GtkWidget *w, gpointer data)
2389 struct gui *ui = (struct gui *) data;
2390 struct gui_entry *ge;
2393 * Can't close the main tab
2395 ge = get_ge_from_cur_tab(ui);
2397 gtk_widget_destroy(ge->vbox);
2401 if (!flist_empty(&ui->list)) {
2402 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2409 static void file_add_recent(struct gui *ui, const gchar *uri)
2413 memset(&grd, 0, sizeof(grd));
2414 grd.display_name = strdup("gfio");
2415 grd.description = strdup("Fio job file");
2416 grd.mime_type = strdup(GFIO_MIME);
2417 grd.app_name = strdup(g_get_application_name());
2418 grd.app_exec = strdup("gfio %f/%u");
2420 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2423 static gchar *get_filename_from_uri(const gchar *uri)
2425 if (strncmp(uri, "file://", 7))
2428 return strdup(uri + 7);
2431 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2434 struct fio_client *client;
2437 filename = get_filename_from_uri(uri);
2439 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2440 ge->job_files[ge->nr_job_files] = strdup(filename);
2443 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2447 error = g_error_new(g_quark_from_string("fio"), 1,
2448 "Failed to add client %s", host);
2449 report_error(error);
2450 g_error_free(error);
2454 gfio_client_added(ge, client);
2455 file_add_recent(ge->ui, uri);
2459 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2461 int port, type, server_start;
2462 struct gui_entry *ge;
2465 int ret, ge_is_new = 0;
2468 * Creates new tab if current tab is the main window, or the
2469 * current tab already has a client.
2471 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2472 ge = get_ge_from_page(cur_page, &ge_is_new);
2474 ge = get_new_ge_with_tab("Untitled");
2478 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2480 if (get_connection_details(&host, &port, &type, &server_start)) {
2482 gtk_widget_destroy(ge->vbox);
2487 ret = do_file_open(ge, uri, host, type, port);
2493 gfio_start_server();
2496 gtk_widget_destroy(ge->vbox);
2502 static void recent_open(GtkAction *action, gpointer data)
2504 struct gui *ui = (struct gui *) data;
2505 GtkRecentInfo *info;
2508 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2509 uri = gtk_recent_info_get_uri(info);
2511 do_file_open_with_tab(ui, uri);
2514 static void file_open(GtkWidget *w, gpointer data)
2516 struct gui *ui = data;
2518 GSList *filenames, *fn_glist;
2519 GtkFileFilter *filter;
2521 dialog = gtk_file_chooser_dialog_new("Open File",
2522 GTK_WINDOW(ui->window),
2523 GTK_FILE_CHOOSER_ACTION_OPEN,
2524 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2525 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2527 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2529 filter = gtk_file_filter_new();
2530 gtk_file_filter_add_pattern(filter, "*.fio");
2531 gtk_file_filter_add_pattern(filter, "*.job");
2532 gtk_file_filter_add_pattern(filter, "*.ini");
2533 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2534 gtk_file_filter_set_name(filter, "Fio job file");
2535 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2537 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2538 gtk_widget_destroy(dialog);
2542 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2544 gtk_widget_destroy(dialog);
2546 filenames = fn_glist;
2547 while (filenames != NULL) {
2548 if (do_file_open_with_tab(ui, filenames->data))
2550 filenames = g_slist_next(filenames);
2553 g_slist_free(fn_glist);
2556 static void file_save(GtkWidget *w, gpointer data)
2558 struct gui *ui = data;
2561 dialog = gtk_file_chooser_dialog_new("Save File",
2562 GTK_WINDOW(ui->window),
2563 GTK_FILE_CHOOSER_ACTION_SAVE,
2564 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2565 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2568 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2569 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2571 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2574 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2575 // save_job_file(filename);
2578 gtk_widget_destroy(dialog);
2581 static void view_log_destroy(GtkWidget *w, gpointer data)
2583 struct gui *ui = (struct gui *) data;
2585 gtk_widget_ref(ui->log_tree);
2586 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2587 gtk_widget_destroy(w);
2588 ui->log_view = NULL;
2591 static void view_log(GtkWidget *w, gpointer data)
2593 GtkWidget *win, *scroll, *vbox, *box;
2594 struct gui *ui = (struct gui *) data;
2599 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2600 gtk_window_set_title(GTK_WINDOW(win), "Log");
2601 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2603 scroll = gtk_scrolled_window_new(NULL, NULL);
2605 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2607 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2609 box = gtk_hbox_new(TRUE, 0);
2610 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2611 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2612 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2614 vbox = gtk_vbox_new(TRUE, 5);
2615 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2617 gtk_container_add(GTK_CONTAINER(win), vbox);
2618 gtk_widget_show_all(win);
2621 static void connect_job_entry(GtkWidget *w, gpointer data)
2623 struct gui *ui = (struct gui *) data;
2624 struct gui_entry *ge;
2626 ge = get_ge_from_cur_tab(ui);
2628 connect_clicked(w, ge);
2631 static void send_job_entry(GtkWidget *w, gpointer data)
2633 struct gui *ui = (struct gui *) data;
2634 struct gui_entry *ge;
2636 ge = get_ge_from_cur_tab(ui);
2638 send_clicked(w, ge);
2642 static void edit_job_entry(GtkWidget *w, gpointer data)
2646 static void start_job_entry(GtkWidget *w, gpointer data)
2648 struct gui *ui = (struct gui *) data;
2649 struct gui_entry *ge;
2651 ge = get_ge_from_cur_tab(ui);
2653 start_job_clicked(w, ge);
2656 static void view_results(GtkWidget *w, gpointer data)
2658 struct gui *ui = (struct gui *) data;
2659 struct gfio_client *gc;
2660 struct gui_entry *ge;
2662 ge = get_ge_from_cur_tab(ui);
2666 if (ge->results_window)
2670 if (gc && gc->nr_results)
2671 gfio_display_end_results(gc);
2674 static void __update_graph_limits(struct gfio_graphs *g)
2676 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2677 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2680 static void update_graph_limits(void)
2682 struct flist_head *entry;
2683 struct gui_entry *ge;
2685 __update_graph_limits(&main_ui.graphs);
2687 flist_for_each(entry, &main_ui.list) {
2688 ge = flist_entry(entry, struct gui_entry, list);
2689 __update_graph_limits(&ge->graphs);
2693 static void preferences(GtkWidget *w, gpointer data)
2695 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2696 GtkWidget *hbox, *spin, *entry, *spin_int;
2699 dialog = gtk_dialog_new_with_buttons("Preferences",
2700 GTK_WINDOW(main_ui.window),
2701 GTK_DIALOG_DESTROY_WITH_PARENT,
2702 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2703 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2706 frame = gtk_frame_new("Graphing");
2707 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2708 vbox = gtk_vbox_new(FALSE, 6);
2709 gtk_container_add(GTK_CONTAINER(frame), vbox);
2711 hbox = gtk_hbox_new(FALSE, 5);
2712 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2713 entry = gtk_label_new("Font face to use for graph labels");
2714 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2716 font = gtk_font_button_new();
2717 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2719 box = gtk_vbox_new(FALSE, 6);
2720 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2722 hbox = gtk_hbox_new(FALSE, 5);
2723 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2724 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2725 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2727 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2729 box = gtk_vbox_new(FALSE, 6);
2730 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2732 hbox = gtk_hbox_new(FALSE, 5);
2733 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2734 entry = gtk_label_new("Client ETA request interval (msec)");
2735 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2737 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2738 frame = gtk_frame_new("Debug logging");
2739 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2740 vbox = gtk_vbox_new(FALSE, 6);
2741 gtk_container_add(GTK_CONTAINER(frame), vbox);
2743 box = gtk_hbox_new(FALSE, 6);
2744 gtk_container_add(GTK_CONTAINER(vbox), box);
2746 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2748 for (i = 0; i < FD_DEBUG_MAX; i++) {
2750 box = gtk_hbox_new(FALSE, 6);
2751 gtk_container_add(GTK_CONTAINER(vbox), box);
2755 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2756 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2757 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2760 gtk_widget_show_all(dialog);
2762 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2763 gtk_widget_destroy(dialog);
2767 for (i = 0; i < FD_DEBUG_MAX; i++) {
2770 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2772 fio_debug |= (1UL << i);
2775 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2776 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2777 update_graph_limits();
2778 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2780 gtk_widget_destroy(dialog);
2783 static void about_dialog(GtkWidget *w, gpointer data)
2785 const char *authors[] = {
2786 "Jens Axboe <axboe@kernel.dk>",
2787 "Stephen Carmeron <stephenmcameron@gmail.com>",
2790 const char *license[] = {
2791 "Fio is free software; you can redistribute it and/or modify "
2792 "it under the terms of the GNU General Public License as published by "
2793 "the Free Software Foundation; either version 2 of the License, or "
2794 "(at your option) any later version.\n",
2795 "Fio is distributed in the hope that it will be useful, "
2796 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2797 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2798 "GNU General Public License for more details.\n",
2799 "You should have received a copy of the GNU General Public License "
2800 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2801 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2803 char *license_trans;
2805 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2806 license[2], "\n", NULL);
2808 gtk_show_about_dialog(NULL,
2809 "program-name", "gfio",
2810 "comments", "Gtk2 UI for fio",
2811 "license", license_trans,
2812 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2814 "version", fio_version_string,
2815 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2816 "logo-icon-name", "fio",
2818 "wrap-license", TRUE,
2821 g_free(license_trans);
2824 static GtkActionEntry menu_items[] = {
2825 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2826 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2827 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2828 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2829 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2830 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2831 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2832 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2833 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2834 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2835 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2836 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2837 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2838 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2839 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2840 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2841 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2843 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2845 static const gchar *ui_string = " \
2847 <menubar name=\"MainMenu\"> \
2848 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2849 <menuitem name=\"New\" action=\"NewFile\" /> \
2850 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2851 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2852 <separator name=\"Separator1\"/> \
2853 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2854 <separator name=\"Separator2\"/> \
2855 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2856 <separator name=\"Separator3\"/> \
2857 <placeholder name=\"FileRecentFiles\"/> \
2858 <separator name=\"Separator4\"/> \
2859 <menuitem name=\"Quit\" action=\"Quit\" /> \
2861 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2862 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2863 <separator name=\"Separator5\"/> \
2864 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2865 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2866 <separator name=\"Separator6\"/> \
2867 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2869 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2870 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2871 <separator name=\"Separator7\"/> \
2872 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2874 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2875 <menuitem name=\"About\" action=\"About\" /> \
2881 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2884 GtkActionGroup *action_group;
2887 action_group = gtk_action_group_new("Menu");
2888 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2890 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2891 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2893 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2895 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2898 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2899 GtkWidget *vbox, GtkUIManager *ui_manager)
2901 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2904 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2906 struct gui_entry *ge = (struct gui_entry *) data;
2909 index = gtk_combo_box_get_active(box);
2911 multitext_set_entry(&ge->eta.iotype, index);
2912 multitext_set_entry(&ge->eta.ioengine, index);
2913 multitext_set_entry(&ge->eta.iodepth, index);
2916 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2918 struct gui_entry *ge = (struct gui_entry *) data;
2920 multitext_free(&ge->eta.iotype);
2921 multitext_free(&ge->eta.ioengine);
2922 multitext_free(&ge->eta.iodepth);
2925 static GtkWidget *new_client_page(struct gui_entry *ge)
2927 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2928 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2930 main_vbox = gtk_vbox_new(FALSE, 3);
2932 top_align = gtk_alignment_new(0, 0, 1, 0);
2933 top_vbox = gtk_vbox_new(FALSE, 3);
2934 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2935 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2937 probe = gtk_frame_new("Job");
2938 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2939 probe_frame = gtk_vbox_new(FALSE, 3);
2940 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2942 probe_box = gtk_hbox_new(FALSE, 3);
2943 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2944 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2945 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2946 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2947 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2949 probe_box = gtk_hbox_new(FALSE, 3);
2950 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2952 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2953 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2954 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2955 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2956 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2957 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2958 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2959 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2961 probe_box = gtk_hbox_new(FALSE, 3);
2962 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2963 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2964 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2965 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2966 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2969 * Only add this if we have a commit rate
2972 probe_box = gtk_hbox_new(FALSE, 3);
2973 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2975 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2976 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2978 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2979 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2983 * Set up a drawing area and IOPS and bandwidth graphs
2985 ge->graphs.drawing_area = gtk_drawing_area_new();
2986 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2987 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2988 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2989 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2990 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2991 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2992 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2993 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2994 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2995 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2996 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2997 ge->graphs.drawing_area);
2998 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
3000 setup_graphs(&ge->graphs);
3003 * Set up alignments for widgets at the bottom of ui,
3004 * align bottom left, expand horizontally but not vertically
3006 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3007 ge->buttonbox = gtk_hbox_new(FALSE, 0);
3008 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3009 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3011 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3014 * Set up thread status progress bar
3016 ge->thread_status_pb = gtk_progress_bar_new();
3017 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3018 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3019 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3025 static GtkWidget *new_main_page(struct gui *ui)
3027 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
3028 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
3030 main_vbox = gtk_vbox_new(FALSE, 3);
3033 * Set up alignments for widgets at the top of ui,
3034 * align top left, expand horizontally but not vertically
3036 top_align = gtk_alignment_new(0, 0, 1, 0);
3037 top_vbox = gtk_vbox_new(FALSE, 0);
3038 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3039 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3041 probe = gtk_frame_new("Run statistics");
3042 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3043 probe_frame = gtk_vbox_new(FALSE, 3);
3044 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3046 probe_box = gtk_hbox_new(FALSE, 3);
3047 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3048 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3049 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3050 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3051 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3052 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3055 * Only add this if we have a commit rate
3058 probe_box = gtk_hbox_new(FALSE, 3);
3059 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3061 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3062 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3064 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3065 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3069 * Set up a drawing area and IOPS and bandwidth graphs
3071 ui->graphs.drawing_area = gtk_drawing_area_new();
3072 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3073 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3074 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3075 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3076 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3077 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3078 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3079 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3080 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3081 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3082 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3083 ui->graphs.drawing_area);
3084 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3087 setup_graphs(&ui->graphs);
3090 * Set up alignments for widgets at the bottom of ui,
3091 * align bottom left, expand horizontally but not vertically
3093 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3094 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3095 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3096 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3099 * Set up thread status progress bar
3101 ui->thread_status_pb = gtk_progress_bar_new();
3102 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3103 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3104 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3109 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3110 guint page, gpointer data)
3113 struct gui *ui = (struct gui *) data;
3114 struct gui_entry *ge;
3117 set_job_menu_visible(ui, 0);
3118 set_view_results_visible(ui, 0);
3122 set_job_menu_visible(ui, 1);
3123 ge = get_ge_from_page(page, NULL);
3125 update_button_states(ui, ge);
3130 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3132 time_t time_a = gtk_recent_info_get_visited(a);
3133 time_t time_b = gtk_recent_info_get_visited(b);
3135 return time_b - time_a;
3138 static void add_recent_file_items(struct gui *ui)
3140 const gchar *gfio = g_get_application_name();
3141 GList *items, *item;
3144 if (ui->recent_ui_id) {
3145 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3146 gtk_ui_manager_ensure_update(ui->uimanager);
3148 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3150 if (ui->actiongroup) {
3151 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3152 g_object_unref(ui->actiongroup);
3154 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3156 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3158 items = gtk_recent_manager_get_items(ui->recentmanager);
3159 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3161 for (item = items; item && item->data; item = g_list_next(item)) {
3162 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3167 if (!gtk_recent_info_has_application(info, gfio))
3171 * We only support local files for now
3173 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3176 action_name = g_strdup_printf("RecentFile%u", i++);
3177 label = gtk_recent_info_get_display_name(info);
3179 action = g_object_new(GTK_TYPE_ACTION,
3180 "name", action_name,
3181 "label", label, NULL);
3183 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3184 gtk_recent_info_ref(info),
3185 (GDestroyNotify) gtk_recent_info_unref);
3188 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3190 gtk_action_group_add_action(ui->actiongroup, action);
3191 g_object_unref(action);
3193 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3194 "/MainMenu/FileMenu/FileRecentFiles",
3196 GTK_UI_MANAGER_MENUITEM, FALSE);
3198 g_free(action_name);
3204 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3208 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3209 gint x, gint y, GtkSelectionData *data,
3210 guint info, guint time)
3212 struct gui *ui = &main_ui;
3217 source = gtk_drag_get_source_widget(ctx);
3218 if (source && widget == gtk_widget_get_toplevel(source)) {
3219 gtk_drag_finish(ctx, FALSE, FALSE, time);
3223 uris = gtk_selection_data_get_uris(data);
3225 gtk_drag_finish(ctx, FALSE, FALSE, time);
3231 if (do_file_open_with_tab(ui, uris[i]))
3236 gtk_drag_finish(ctx, TRUE, FALSE, time);
3240 static void init_ui(int *argc, char **argv[], struct gui *ui)
3242 GtkSettings *settings;
3245 /* Magical g*thread incantation, you just need this thread stuff.
3246 * Without it, the update that happens in gfio_update_thread_status
3247 * doesn't really happen in a timely fashion, you need expose events
3249 if (!g_thread_supported())
3250 g_thread_init(NULL);
3253 gtk_init(argc, argv);
3254 settings = gtk_settings_get_default();
3255 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3257 gdk_color_parse("white", &white);
3259 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3260 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3261 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3263 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3264 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3266 ui->vbox = gtk_vbox_new(FALSE, 0);
3267 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3269 ui->uimanager = gtk_ui_manager_new();
3270 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3271 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3273 ui->recentmanager = gtk_recent_manager_get_default();
3274 add_recent_file_items(ui);
3276 ui->notebook = gtk_notebook_new();
3277 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3278 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3279 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3280 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3282 vbox = new_main_page(ui);
3283 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3284 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3285 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3287 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3289 gfio_ui_setup_log(ui);
3291 gtk_widget_show_all(ui->window);
3294 int main(int argc, char *argv[], char *envp[])
3296 if (initialize_fio(envp))
3298 if (fio_init_options())
3301 memset(&main_ui, 0, sizeof(main_ui));
3302 INIT_FLIST_HEAD(&main_ui.list);
3304 init_ui(&argc, &argv, &main_ui);
3306 gdk_threads_enter();
3308 gdk_threads_leave();