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[2];
56 const int start_sensitive;
57 } buttonspeclist[] = {
58 #define CONNECT_BUTTON 0
60 #define START_JOB_BUTTON 2
61 { "Connect", connect_clicked, { "Disconnect from host", "Connect to host" }, 1 },
62 { "Send", send_clicked, { "Send job description to host", NULL }, 0 },
63 { "Start Job", start_job_clicked,
64 { "Start the current job on the server", NULL }, 0 },
74 struct multitext_widget {
77 unsigned int cur_text;
78 unsigned int max_text;
83 struct multitext_widget iotype;
84 struct multitext_widget bs;
85 struct multitext_widget ioengine;
86 struct multitext_widget iodepth;
94 GtkWidget *write_iops;
100 #define DRAWING_AREA_XDIM 1000
101 #define DRAWING_AREA_YDIM 400
102 GtkWidget *drawing_area;
103 struct graph *iops_graph;
104 struct graph *bandwidth_graph;
108 * Main window widgets and data
111 GtkUIManager *uimanager;
112 GtkRecentManager *recentmanager;
113 GtkActionGroup *actiongroup;
118 GtkWidget *thread_status_pb;
119 GtkWidget *buttonbox;
121 GtkWidget *error_info_bar;
122 GtkWidget *error_label;
123 GtkListStore *log_model;
126 struct gfio_graphs graphs;
127 struct probe_widget probe;
128 struct eta_widget eta;
134 struct flist_head list;
141 GE_STATE_JOB_STARTED,
142 GE_STATE_JOB_RUNNING,
150 struct flist_head list;
154 GtkWidget *job_notebook;
155 GtkWidget *thread_status_pb;
156 GtkWidget *buttonbox;
157 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
159 GtkWidget *error_info_bar;
160 GtkWidget *error_label;
161 GtkWidget *results_window;
162 GtkWidget *results_notebook;
163 GtkUIManager *results_uimanager;
164 GtkWidget *results_menu;
165 GtkWidget *disk_util_vbox;
166 GtkListStore *log_model;
169 struct gfio_graphs graphs;
170 struct probe_widget probe;
171 struct eta_widget eta;
172 GtkWidget *page_label;
176 struct graph *clat_graph;
177 struct graph *lat_bucket_graph;
179 struct gfio_client *client;
185 struct group_run_stats gs;
186 struct thread_stat ts;
190 struct gui_entry *ge;
191 struct fio_client *client;
192 GtkWidget *err_entry;
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.bs, 0, "");
305 multitext_update_entry(&ge->eta.ioengine, 0, "");
306 multitext_update_entry(&ge->eta.iodepth, 0, "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
309 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
310 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
311 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
312 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
315 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
317 GtkWidget *entry, *frame;
319 frame = gtk_frame_new(label);
320 entry = gtk_combo_box_new_text();
321 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
322 gtk_container_add(GTK_CONTAINER(frame), entry);
327 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
329 GtkWidget *entry, *frame;
331 frame = gtk_frame_new(label);
332 entry = gtk_entry_new();
333 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
334 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
335 gtk_container_add(GTK_CONTAINER(frame), entry);
340 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
342 GtkWidget *label_widget;
345 frame = gtk_frame_new(label);
346 label_widget = gtk_label_new(NULL);
347 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
348 gtk_container_add(GTK_CONTAINER(frame), label_widget);
353 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
355 GtkWidget *button, *box;
357 box = gtk_hbox_new(FALSE, 3);
358 gtk_container_add(GTK_CONTAINER(hbox), box);
360 button = gtk_spin_button_new_with_range(min, max, 1.0);
361 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
363 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
364 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
369 static void label_set_int_value(GtkWidget *entry, unsigned int val)
373 sprintf(tmp, "%u", val);
374 gtk_label_set_text(GTK_LABEL(entry), tmp);
377 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
381 sprintf(tmp, "%u", val);
382 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
385 static void show_info_dialog(struct gui *ui, const char *title,
388 GtkWidget *dialog, *content, *label;
390 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
391 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
392 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
394 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
395 label = gtk_label_new(message);
396 gtk_container_add(GTK_CONTAINER(content), label);
397 gtk_widget_show_all(dialog);
398 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
399 gtk_dialog_run(GTK_DIALOG(dialog));
400 gtk_widget_destroy(dialog);
403 static void set_menu_entry_text(struct gui *ui, const char *path,
408 w = gtk_ui_manager_get_widget(ui->uimanager, path);
410 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
412 fprintf(stderr, "gfio: can't find path %s\n", path);
416 static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
420 w = gtk_ui_manager_get_widget(ui->uimanager, path);
422 gtk_widget_set_sensitive(w, show);
424 fprintf(stderr, "gfio: can't find path %s\n", path);
427 static void set_job_menu_visible(struct gui *ui, int visible)
429 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
432 static void set_view_results_visible(struct gui *ui, int visible)
434 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
437 static const char *get_button_tooltip(struct button_spec *s, int sensitive)
439 if (s->tooltiptext[sensitive])
440 return s->tooltiptext[sensitive];
442 return s->tooltiptext[0];
445 static GtkWidget *add_button(GtkWidget *buttonbox,
446 struct button_spec *buttonspec, gpointer data)
448 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
449 gboolean sens = buttonspec->start_sensitive;
451 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
452 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
454 sens = buttonspec->start_sensitive;
455 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
456 gtk_widget_set_sensitive(button, sens);
461 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
466 for (i = 0; i < nbuttons; i++)
467 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
471 * Update sensitivity of job buttons and job menu items, based on the
472 * state of the client.
474 static void update_button_states(struct gui *ui, struct gui_entry *ge)
476 unsigned int connect_state, send_state, start_state, edit_state;
477 const char *connect_str = NULL;
483 sprintf(tmp, "Bad client state: %u\n", ge->state);
484 show_info_dialog(ui, "Error", tmp);
485 /* fall through to new state */
491 connect_str = "Connect";
495 case GE_STATE_CONNECTED:
498 connect_str = "Disconnect";
502 case GE_STATE_JOB_SENT:
505 connect_str = "Disconnect";
509 case GE_STATE_JOB_STARTED:
512 connect_str = "Disconnect";
516 case GE_STATE_JOB_RUNNING:
519 connect_str = "Disconnect";
523 case GE_STATE_JOB_DONE:
526 connect_str = "Connect";
532 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
533 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
534 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
535 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
536 gtk_widget_set_tooltip_text(ge->button[CONNECT_BUTTON], get_button_tooltip(&buttonspeclist[CONNECT_BUTTON], connect_state));
538 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
539 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
541 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
542 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
543 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
545 if (ge->client && ge->client->nr_results)
546 set_view_results_visible(ui, 1);
548 set_view_results_visible(ui, 0);
551 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
554 update_button_states(ge->ui, ge);
558 #define ALIGN_RIGHT 2
562 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
564 GtkCellRenderer *renderer;
565 GtkTreeViewColumn *col;
566 double xalign = 0.0; /* left as default */
567 PangoAlignment align;
570 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
571 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
573 visible = !(flags & INVISIBLE);
575 renderer = gtk_cell_renderer_text_new();
576 col = gtk_tree_view_column_new();
578 gtk_tree_view_column_set_title(col, title);
579 if (!(flags & UNSORTABLE))
580 gtk_tree_view_column_set_sort_column_id(col, index);
581 gtk_tree_view_column_set_resizable(col, TRUE);
582 gtk_tree_view_column_pack_start(col, renderer, TRUE);
583 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
584 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
586 case PANGO_ALIGN_LEFT:
589 case PANGO_ALIGN_CENTER:
592 case PANGO_ALIGN_RIGHT:
596 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
597 gtk_tree_view_column_set_visible(col, visible);
598 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
602 static void gfio_ui_setup_log(struct gui *ui)
604 GtkTreeSelection *selection;
606 GtkWidget *tree_view;
608 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
610 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
611 gtk_widget_set_can_focus(tree_view, FALSE);
613 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
614 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
615 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
616 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
618 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
619 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
620 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
621 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
623 ui->log_model = model;
624 ui->log_tree = tree_view;
627 static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
630 double xdim, double ydim)
635 g = graph_new(xdim, ydim, gfio_graph_font);
636 graph_title(g, title);
637 graph_x_title(g, "Percentile");
639 for (i = 0; i < len; i++) {
642 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
643 graph_add_label(g, fbuf);
644 graph_add_data(g, fbuf, (double) ovals[i]);
650 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
656 GType types[FIO_IO_U_LIST_MAX_LEN];
657 GtkWidget *tree_view;
658 GtkTreeSelection *selection;
663 for (i = 0; i < len; i++)
664 types[i] = G_TYPE_INT;
666 model = gtk_list_store_newv(len, types);
668 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
669 gtk_widget_set_can_focus(tree_view, FALSE);
671 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
672 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
674 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
675 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
677 for (i = 0; i < len; i++) {
680 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
681 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
684 gtk_list_store_append(model, &iter);
686 for (i = 0; i < len; i++) {
688 ovals[i] = (ovals[i] + 999) / 1000;
689 gtk_list_store_set(model, &iter, i, ovals[i], -1);
695 static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
700 cr = gdk_cairo_create(w->window);
702 if (graph_has_tooltips(g)) {
703 g_object_set(w, "has-tooltip", TRUE, NULL);
704 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
707 cairo_set_source_rgb(cr, 0, 0, 0);
708 bar_graph_draw(g, cr);
714 static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
717 struct graph *g = data;
719 graph_set_size(g, w->allocation.width, w->allocation.height);
720 graph_set_size(g, w->allocation.width, w->allocation.height);
721 graph_set_position(g, 0, 0);
725 static void gfio_show_clat_percentiles(struct gfio_client *gc,
726 GtkWidget *vbox, struct thread_stat *ts,
729 unsigned int *io_u_plat = ts->io_u_plat[ddir];
730 unsigned long nr = ts->clat_stat[ddir].samples;
731 fio_fp64_t *plist = ts->percentile_list;
732 unsigned int *ovals, len, minv, maxv, scale_down;
734 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
735 struct gui_entry *ge = gc->ge;
738 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
743 * We default to usecs, but if the value range is such that we
744 * should scale down to msecs, do that.
746 if (minv > 2000 && maxv > 99999) {
754 sprintf(tmp, "Completion percentiles (%s)", base);
755 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
756 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
758 frame = gtk_frame_new(tmp);
759 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
761 completion_vbox = gtk_vbox_new(FALSE, 3);
762 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
763 hbox = gtk_hbox_new(FALSE, 3);
764 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
765 drawing_area = gtk_drawing_area_new();
766 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
767 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
768 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
769 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
770 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
772 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
778 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
779 unsigned long max, double mean, double dev)
781 const char *base = "(usec)";
782 GtkWidget *hbox, *label, *frame;
786 if (!usec_to_msec(&min, &max, &mean, &dev))
789 minp = num2str(min, 6, 1, 0);
790 maxp = num2str(max, 6, 1, 0);
792 sprintf(tmp, "%s %s", name, base);
793 frame = gtk_frame_new(tmp);
794 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
796 hbox = gtk_hbox_new(FALSE, 3);
797 gtk_container_add(GTK_CONTAINER(frame), hbox);
799 label = new_info_label_in_frame(hbox, "Minimum");
800 gtk_label_set_text(GTK_LABEL(label), minp);
801 label = new_info_label_in_frame(hbox, "Maximum");
802 gtk_label_set_text(GTK_LABEL(label), maxp);
803 label = new_info_label_in_frame(hbox, "Average");
804 sprintf(tmp, "%5.02f", mean);
805 gtk_label_set_text(GTK_LABEL(label), tmp);
806 label = new_info_label_in_frame(hbox, "Standard deviation");
807 sprintf(tmp, "%5.02f", dev);
808 gtk_label_set_text(GTK_LABEL(label), tmp);
819 static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
820 struct group_run_stats *rs,
821 struct thread_stat *ts, int ddir)
823 const char *ddir_label[2] = { "Read", "Write" };
824 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
825 unsigned long min[3], max[3], runt;
826 unsigned long long bw, iops;
827 unsigned int flags = 0;
828 double mean[3], dev[3];
829 char *io_p, *bw_p, *iops_p;
832 if (!ts->runtime[ddir])
835 i2p = is_power_of_2(rs->kb_base);
836 runt = ts->runtime[ddir];
838 bw = (1000 * ts->io_bytes[ddir]) / runt;
839 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
840 bw_p = num2str(bw, 6, 1, i2p);
842 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
843 iops_p = num2str(iops, 6, 1, 0);
845 box = gtk_hbox_new(FALSE, 3);
846 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
848 frame = gtk_frame_new(ddir_label[ddir]);
849 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
851 main_vbox = gtk_vbox_new(FALSE, 3);
852 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
854 box = gtk_hbox_new(FALSE, 3);
855 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
857 label = new_info_label_in_frame(box, "IO");
858 gtk_label_set_text(GTK_LABEL(label), io_p);
859 label = new_info_label_in_frame(box, "Bandwidth");
860 gtk_label_set_text(GTK_LABEL(label), bw_p);
861 label = new_info_label_in_frame(box, "IOPS");
862 gtk_label_set_text(GTK_LABEL(label), iops_p);
863 label = new_info_label_in_frame(box, "Runtime (msec)");
864 label_set_int_value(label, ts->runtime[ddir]);
866 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
867 double p_of_agg = 100.0;
868 const char *bw_str = "KB";
872 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
873 if (p_of_agg > 100.0)
877 if (mean[0] > 999999.9) {
885 sprintf(tmp, "Bandwidth (%s)", bw_str);
886 frame = gtk_frame_new(tmp);
887 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
889 box = gtk_hbox_new(FALSE, 3);
890 gtk_container_add(GTK_CONTAINER(frame), box);
892 label = new_info_label_in_frame(box, "Minimum");
893 label_set_int_value(label, min[0]);
894 label = new_info_label_in_frame(box, "Maximum");
895 label_set_int_value(label, max[0]);
896 label = new_info_label_in_frame(box, "Percentage of jobs");
897 sprintf(tmp, "%3.2f%%", p_of_agg);
898 gtk_label_set_text(GTK_LABEL(label), tmp);
899 label = new_info_label_in_frame(box, "Average");
900 sprintf(tmp, "%5.02f", mean[0]);
901 gtk_label_set_text(GTK_LABEL(label), tmp);
902 label = new_info_label_in_frame(box, "Standard deviation");
903 sprintf(tmp, "%5.02f", dev[0]);
904 gtk_label_set_text(GTK_LABEL(label), tmp);
907 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
909 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
911 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
915 frame = gtk_frame_new("Latency");
916 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
918 vbox = gtk_vbox_new(FALSE, 3);
919 gtk_container_add(GTK_CONTAINER(frame), vbox);
921 if (flags & GFIO_SLAT)
922 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
923 if (flags & GFIO_CLAT)
924 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
925 if (flags & GFIO_LAT)
926 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
929 if (ts->clat_percentiles)
930 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
937 static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
940 double xdim, double ydim)
945 g = graph_new(xdim, ydim, gfio_graph_font);
946 graph_title(g, title);
947 graph_x_title(g, "Buckets");
949 for (i = 0; i < len; i++) {
950 graph_add_label(g, labels[i]);
951 graph_add_data(g, labels[i], lat[i]);
957 static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
960 GtkWidget *tree_view;
961 GtkTreeSelection *selection;
967 types = malloc(num * sizeof(GType));
969 for (i = 0; i < num; i++)
970 types[i] = G_TYPE_STRING;
972 model = gtk_list_store_newv(num, types);
976 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
977 gtk_widget_set_can_focus(tree_view, FALSE);
979 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
980 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
982 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
983 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
985 for (i = 0; i < num; i++)
986 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
988 gtk_list_store_append(model, &iter);
990 for (i = 0; i < num; i++) {
994 sprintf(fbuf, "0.00");
996 sprintf(fbuf, "%3.2f%%", lat[i]);
998 gtk_list_store_set(model, &iter, i, fbuf, -1);
1004 static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
1005 struct thread_stat *ts)
1007 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
1008 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
1009 "250u", "500u", "750u", "1m", "2m",
1010 "4m", "10m", "20m", "50m", "100m",
1011 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
1013 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
1014 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
1015 struct gui_entry *ge = gc->ge;
1017 stat_calc_lat_u(ts, io_u_lat);
1018 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
1021 * Found out which first bucket has entries, and which last bucket
1024 for (i = 0; i < total; i++) {
1025 if (io_u_lat[i] == 0.00)
1039 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1040 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
1042 frame = gtk_frame_new("Latency buckets");
1043 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1045 completion_vbox = gtk_vbox_new(FALSE, 3);
1046 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1047 hbox = gtk_hbox_new(FALSE, 3);
1048 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1050 drawing_area = gtk_drawing_area_new();
1051 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1052 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1053 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1054 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1055 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1057 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
1060 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1062 GtkWidget *box, *frame, *entry;
1063 double usr_cpu, sys_cpu;
1064 unsigned long runtime;
1067 runtime = ts->total_run_time;
1069 double runt = (double) runtime;
1071 usr_cpu = (double) ts->usr_time * 100 / runt;
1072 sys_cpu = (double) ts->sys_time * 100 / runt;
1078 frame = gtk_frame_new("OS resources");
1079 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1081 box = gtk_hbox_new(FALSE, 3);
1082 gtk_container_add(GTK_CONTAINER(frame), box);
1084 entry = new_info_entry_in_frame(box, "User CPU");
1085 sprintf(tmp, "%3.2f%%", usr_cpu);
1086 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1087 entry = new_info_entry_in_frame(box, "System CPU");
1088 sprintf(tmp, "%3.2f%%", sys_cpu);
1089 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1090 entry = new_info_entry_in_frame(box, "Context switches");
1091 entry_set_int_value(entry, ts->ctx);
1092 entry = new_info_entry_in_frame(box, "Major faults");
1093 entry_set_int_value(entry, ts->majf);
1094 entry = new_info_entry_in_frame(box, "Minor faults");
1095 entry_set_int_value(entry, ts->minf);
1097 static void gfio_add_sc_depths_tree(GtkListStore *model,
1098 struct thread_stat *ts, unsigned int len,
1101 double io_u_dist[FIO_IO_U_MAP_NR];
1103 /* Bits 0, and 3-8 */
1104 const int add_mask = 0x1f9;
1108 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1110 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1112 gtk_list_store_append(model, &iter);
1114 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1116 for (i = 1, j = 0; i < len; i++) {
1119 if (!(add_mask & (1UL << (i - 1))))
1120 sprintf(fbuf, "0.0%%");
1122 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1126 gtk_list_store_set(model, &iter, i, fbuf, -1);
1131 static void gfio_add_total_depths_tree(GtkListStore *model,
1132 struct thread_stat *ts, unsigned int len)
1134 double io_u_dist[FIO_IO_U_MAP_NR];
1136 /* Bits 1-6, and 8 */
1137 const int add_mask = 0x17e;
1140 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1142 gtk_list_store_append(model, &iter);
1144 gtk_list_store_set(model, &iter, 0, "Total", -1);
1146 for (i = 1, j = 0; i < len; i++) {
1149 if (!(add_mask & (1UL << (i - 1))))
1150 sprintf(fbuf, "0.0%%");
1152 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1156 gtk_list_store_set(model, &iter, i, fbuf, -1);
1161 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1163 GtkWidget *frame, *box, *tree_view;
1164 GtkTreeSelection *selection;
1165 GtkListStore *model;
1166 GType types[FIO_IO_U_MAP_NR + 1];
1168 #define NR_LABELS 10
1169 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1171 frame = gtk_frame_new("IO depths");
1172 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1174 box = gtk_hbox_new(FALSE, 3);
1175 gtk_container_add(GTK_CONTAINER(frame), box);
1177 for (i = 0; i < NR_LABELS; i++)
1178 types[i] = G_TYPE_STRING;
1180 model = gtk_list_store_newv(NR_LABELS, types);
1182 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1183 gtk_widget_set_can_focus(tree_view, FALSE);
1185 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1186 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1188 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1189 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1191 for (i = 0; i < NR_LABELS; i++)
1192 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1194 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1195 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1196 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1198 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1201 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1203 struct gui_entry *ge = (struct gui_entry *) data;
1205 gtk_widget_destroy(w);
1206 ge->results_window = NULL;
1207 ge->results_notebook = NULL;
1211 static void results_close(GtkWidget *w, gpointer *data)
1213 struct gui_entry *ge = (struct gui_entry *) data;
1215 gtk_widget_destroy(ge->results_window);
1218 static GtkActionEntry results_menu_items[] = {
1219 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1220 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1221 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1223 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1225 static const gchar *results_ui_string = " \
1227 <menubar name=\"MainMenu\"> \
1228 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1229 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1231 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1237 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1239 GtkActionGroup *action_group;
1243 ge->results_uimanager = gtk_ui_manager_new();
1245 action_group = gtk_action_group_new("ResultsMenu");
1246 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1248 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1249 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1251 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1253 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1257 static GtkWidget *get_results_window(struct gui_entry *ge)
1259 GtkWidget *win, *notebook, *vbox;
1261 if (ge->results_window)
1262 return ge->results_notebook;
1264 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1265 gtk_window_set_title(GTK_WINDOW(win), "Results");
1266 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1267 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1268 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1270 vbox = gtk_vbox_new(FALSE, 0);
1271 gtk_container_add(GTK_CONTAINER(win), vbox);
1273 ge->results_menu = get_results_menubar(win, ge);
1274 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1276 notebook = gtk_notebook_new();
1277 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1278 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1279 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1281 ge->results_window = win;
1282 ge->results_notebook = notebook;
1283 return ge->results_notebook;
1286 static void disk_util_destroy(GtkWidget *w, gpointer data)
1288 struct gui_entry *ge = (struct gui_entry *) data;
1290 ge->disk_util_vbox = NULL;
1291 gtk_widget_destroy(w);
1294 static GtkWidget *get_scrolled_window(gint border_width)
1298 scroll = gtk_scrolled_window_new(NULL, NULL);
1299 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1300 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1305 static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1307 GtkWidget *vbox, *box, *scroll, *res_notebook;
1309 if (ge->disk_util_vbox)
1310 return ge->disk_util_vbox;
1312 scroll = get_scrolled_window(5);
1313 vbox = gtk_vbox_new(FALSE, 3);
1314 box = gtk_hbox_new(FALSE, 0);
1315 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1317 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1318 res_notebook = get_results_window(ge);
1320 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1321 ge->disk_util_vbox = box;
1322 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1324 return ge->disk_util_vbox;
1327 static int __gfio_disk_util_show(GtkWidget *res_notebook,
1328 struct gfio_client *gc, struct cmd_du_pdu *p)
1330 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
1331 struct gui_entry *ge = gc->ge;
1335 util_vbox = gfio_disk_util_get_vbox(ge);
1337 vbox = gtk_vbox_new(FALSE, 3);
1338 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
1340 frame = gtk_frame_new((char *) p->dus.name);
1341 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1343 box = gtk_vbox_new(FALSE, 3);
1344 gtk_container_add(GTK_CONTAINER(frame), box);
1346 frame = gtk_frame_new("Read");
1347 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1348 vbox = gtk_hbox_new(TRUE, 3);
1349 gtk_container_add(GTK_CONTAINER(frame), vbox);
1350 entry = new_info_entry_in_frame(vbox, "IOs");
1351 entry_set_int_value(entry, p->dus.ios[0]);
1352 entry = new_info_entry_in_frame(vbox, "Merges");
1353 entry_set_int_value(entry, p->dus.merges[0]);
1354 entry = new_info_entry_in_frame(vbox, "Sectors");
1355 entry_set_int_value(entry, p->dus.sectors[0]);
1356 entry = new_info_entry_in_frame(vbox, "Ticks");
1357 entry_set_int_value(entry, p->dus.ticks[0]);
1359 frame = gtk_frame_new("Write");
1360 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1361 vbox = gtk_hbox_new(TRUE, 3);
1362 gtk_container_add(GTK_CONTAINER(frame), vbox);
1363 entry = new_info_entry_in_frame(vbox, "IOs");
1364 entry_set_int_value(entry, p->dus.ios[1]);
1365 entry = new_info_entry_in_frame(vbox, "Merges");
1366 entry_set_int_value(entry, p->dus.merges[1]);
1367 entry = new_info_entry_in_frame(vbox, "Sectors");
1368 entry_set_int_value(entry, p->dus.sectors[1]);
1369 entry = new_info_entry_in_frame(vbox, "Ticks");
1370 entry_set_int_value(entry, p->dus.ticks[1]);
1372 frame = gtk_frame_new("Shared");
1373 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1374 vbox = gtk_hbox_new(TRUE, 3);
1375 gtk_container_add(GTK_CONTAINER(frame), vbox);
1376 entry = new_info_entry_in_frame(vbox, "IO ticks");
1377 entry_set_int_value(entry, p->dus.io_ticks);
1378 entry = new_info_entry_in_frame(vbox, "Time in queue");
1379 entry_set_int_value(entry, p->dus.time_in_queue);
1383 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1387 sprintf(tmp, "%3.2f%%", util);
1388 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1389 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1391 gtk_widget_show_all(ge->results_window);
1395 static int gfio_disk_util_show(struct gfio_client *gc)
1397 struct gui_entry *ge = gc->ge;
1398 GtkWidget *res_notebook;
1404 res_notebook = get_results_window(ge);
1406 for (i = 0; i < gc->nr_du; i++) {
1407 struct cmd_du_pdu *p = &gc->du[i];
1409 __gfio_disk_util_show(res_notebook, gc, p);
1412 gtk_widget_show_all(ge->results_window);
1416 static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1417 struct group_run_stats *rs)
1419 unsigned int nr = gc->nr_results;
1421 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1422 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1423 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1427 static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1428 struct thread_stat *ts,
1429 struct group_run_stats *rs)
1431 GtkWidget *box, *vbox, *entry, *scroll;
1433 scroll = gtk_scrolled_window_new(NULL, NULL);
1434 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1435 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1437 vbox = gtk_vbox_new(FALSE, 3);
1439 box = gtk_hbox_new(FALSE, 0);
1440 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1442 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1444 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1446 entry = new_info_entry_in_frame(box, "Name");
1447 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1448 if (strlen(ts->description)) {
1449 entry = new_info_entry_in_frame(box, "Description");
1450 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1452 entry = new_info_entry_in_frame(box, "Group ID");
1453 entry_set_int_value(entry, ts->groupid);
1454 entry = new_info_entry_in_frame(box, "Jobs");
1455 entry_set_int_value(entry, ts->members);
1456 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1457 entry_set_int_value(entry, ts->error);
1458 entry = new_info_entry_in_frame(box, "PID");
1459 entry_set_int_value(entry, ts->pid);
1461 if (ts->io_bytes[DDIR_READ])
1462 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1463 if (ts->io_bytes[DDIR_WRITE])
1464 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1466 gfio_show_latency_buckets(gc, vbox, ts);
1467 gfio_show_cpu_usage(vbox, ts);
1468 gfio_show_io_depths(vbox, ts);
1471 static void gfio_display_end_results(struct gfio_client *gc)
1473 struct gui_entry *ge = gc->ge;
1474 GtkWidget *res_notebook;
1477 res_notebook = get_results_window(ge);
1479 for (i = 0; i < gc->nr_results; i++) {
1480 struct end_results *e = &gc->results[i];
1482 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1485 if (gfio_disk_util_show(gc))
1486 gtk_widget_show_all(ge->results_window);
1489 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1490 struct group_run_stats *rs)
1492 struct gfio_client *gc = client->client_data;
1493 struct gui_entry *ge = gc->ge;
1495 gfio_add_end_results(gc, ts, rs);
1497 gdk_threads_enter();
1498 if (ge->results_window)
1499 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1501 gfio_display_end_results(gc);
1502 gdk_threads_leave();
1505 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1507 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1508 struct gui *ui = &main_ui;
1512 char tmp[64], timebuf[80];
1515 tm = localtime(&sec);
1516 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1517 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1519 gdk_threads_enter();
1521 gtk_list_store_append(ui->log_model, &iter);
1522 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1523 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1524 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1525 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1527 if (p->level == FIO_LOG_ERR)
1528 view_log(NULL, (gpointer) ui);
1530 gdk_threads_leave();
1533 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1535 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1536 struct gfio_client *gc = client->client_data;
1537 struct gui_entry *ge = gc->ge;
1538 unsigned int nr = gc->nr_du;
1540 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1541 memcpy(&gc->du[nr], p, sizeof(*p));
1544 gdk_threads_enter();
1545 if (ge->results_window)
1546 __gfio_disk_util_show(ge->results_notebook, gc, p);
1548 gfio_disk_util_show(gc);
1549 gdk_threads_leave();
1552 extern int sum_stat_clients;
1553 extern struct thread_stat client_ts;
1554 extern struct group_run_stats client_gs;
1556 static int sum_stat_nr;
1558 static void gfio_thread_status_op(struct fio_client *client,
1559 struct fio_net_cmd *cmd)
1561 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1563 gfio_display_ts(client, &p->ts, &p->rs);
1565 if (sum_stat_clients == 1)
1568 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1569 sum_group_stats(&client_gs, &p->rs);
1571 client_ts.members++;
1572 client_ts.thread_number = p->ts.thread_number;
1573 client_ts.groupid = p->ts.groupid;
1575 if (++sum_stat_nr == sum_stat_clients) {
1576 strcpy(client_ts.name, "All clients");
1577 gfio_display_ts(client, &client_ts, &client_gs);
1581 static void gfio_group_stats_op(struct fio_client *client,
1582 struct fio_net_cmd *cmd)
1584 /* We're ignoring group stats for now */
1587 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1590 struct gfio_graphs *g = data;
1592 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1593 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1594 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1595 graph_set_position(g->bandwidth_graph, 0, 0);
1599 static void draw_graph(struct graph *g, cairo_t *cr)
1601 line_graph_draw(g, cr);
1605 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1606 gboolean keyboard_mode, GtkTooltip *tooltip,
1609 struct gfio_graphs *g = data;
1610 const char *text = NULL;
1612 if (graph_contains_xy(g->iops_graph, x, y))
1613 text = graph_find_tooltip(g->iops_graph, x, y);
1614 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1615 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1618 gtk_tooltip_set_text(tooltip, text);
1625 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1627 struct gfio_graphs *g = p;
1630 cr = gdk_cairo_create(w->window);
1632 if (graph_has_tooltips(g->iops_graph) ||
1633 graph_has_tooltips(g->bandwidth_graph)) {
1634 g_object_set(w, "has-tooltip", TRUE, NULL);
1635 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1638 cairo_set_source_rgb(cr, 0, 0, 0);
1639 draw_graph(g->iops_graph, cr);
1640 draw_graph(g->bandwidth_graph, cr);
1647 * Client specific ETA
1649 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1651 struct gfio_client *gc = client->client_data;
1652 struct gui_entry *ge = gc->ge;
1653 static int eta_good;
1660 gdk_threads_enter();
1665 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1666 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1667 eta_to_str(eta_str, je->eta_sec);
1670 sprintf(tmp, "%u", je->nr_running);
1671 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1672 sprintf(tmp, "%u", je->files_open);
1673 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1676 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1677 if (je->m_rate || je->t_rate) {
1680 mr = num2str(je->m_rate, 4, 0, i2p);
1681 tr = num2str(je->t_rate, 4, 0, i2p);
1682 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1683 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1686 } else if (je->m_iops || je->t_iops)
1687 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1689 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1690 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1691 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1692 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1695 if (je->eta_sec != INT_MAX && je->nr_running) {
1699 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1700 strcpy(output, "-.-% done");
1704 sprintf(output, "%3.1f%% done", perc);
1707 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1708 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1710 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1711 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1713 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1714 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1715 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1716 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1718 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1719 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1720 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1721 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1730 char *dst = output + strlen(output);
1732 sprintf(dst, " - %s", eta_str);
1735 gfio_update_thread_status(ge, output, perc);
1736 gdk_threads_leave();
1740 * Update ETA in main window for all clients
1742 static void gfio_update_all_eta(struct jobs_eta *je)
1744 struct gui *ui = &main_ui;
1745 static int eta_good;
1751 gdk_threads_enter();
1756 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1757 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1758 eta_to_str(eta_str, je->eta_sec);
1762 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1763 if (je->m_rate || je->t_rate) {
1766 mr = num2str(je->m_rate, 4, 0, i2p);
1767 tr = num2str(je->t_rate, 4, 0, i2p);
1768 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1769 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1772 } else if (je->m_iops || je->t_iops)
1773 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1775 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1776 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1777 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1778 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1781 entry_set_int_value(ui->eta.jobs, je->nr_running);
1783 if (je->eta_sec != INT_MAX && je->nr_running) {
1787 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1788 strcpy(output, "-.-% done");
1792 sprintf(output, "%3.1f%% done", perc);
1795 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1796 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1798 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1799 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1801 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1802 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1803 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1804 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1806 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1807 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1808 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1809 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1818 char *dst = output + strlen(output);
1820 sprintf(dst, " - %s", eta_str);
1823 gfio_update_thread_status_all(output, perc);
1824 gdk_threads_leave();
1827 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1829 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1830 struct gfio_client *gc = client->client_data;
1831 struct gui_entry *ge = gc->ge;
1832 const char *os, *arch;
1835 os = fio_get_os_string(probe->os);
1839 arch = fio_get_arch_string(probe->arch);
1844 client->name = strdup((char *) probe->hostname);
1846 gdk_threads_enter();
1848 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1849 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1850 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1851 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1852 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1854 gfio_set_state(ge, GE_STATE_CONNECTED);
1856 gdk_threads_leave();
1859 static void gfio_update_thread_status(struct gui_entry *ge,
1860 char *status_message, double perc)
1862 static char message[100];
1863 const char *m = message;
1865 strncpy(message, status_message, sizeof(message) - 1);
1866 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1867 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1868 gtk_widget_queue_draw(main_ui.window);
1871 static void gfio_update_thread_status_all(char *status_message, double perc)
1873 struct gui *ui = &main_ui;
1874 static char message[100];
1875 const char *m = message;
1877 strncpy(message, status_message, sizeof(message) - 1);
1878 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1879 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1880 gtk_widget_queue_draw(ui->window);
1883 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1885 struct gfio_client *gc = client->client_data;
1887 gdk_threads_enter();
1888 gfio_set_state(gc->ge, GE_STATE_NEW);
1889 gdk_threads_leave();
1892 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1894 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1895 struct gfio_client *gc = client->client_data;
1896 struct thread_options *o = &gc->o;
1897 struct gui_entry *ge = gc->ge;
1898 char *c1, *c2, *c3, *c4;
1901 p->thread_number = le32_to_cpu(p->thread_number);
1902 p->groupid = le32_to_cpu(p->groupid);
1903 convert_thread_options_to_cpu(o, &p->top);
1905 gdk_threads_enter();
1907 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1909 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1910 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1912 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1913 multitext_add_entry(&ge->eta.iotype, tmp);
1915 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1916 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1917 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1918 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1919 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1924 multitext_add_entry(&ge->eta.bs, tmp);
1926 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1928 sprintf(tmp, "%u", o->iodepth);
1929 multitext_add_entry(&ge->eta.iodepth, tmp);
1931 multitext_set_entry(&ge->eta.iotype, 0);
1932 multitext_set_entry(&ge->eta.bs, 0);
1933 multitext_set_entry(&ge->eta.ioengine, 0);
1934 multitext_set_entry(&ge->eta.iodepth, 0);
1936 gfio_set_state(ge, GE_STATE_JOB_SENT);
1938 gdk_threads_leave();
1941 static void gfio_client_timed_out(struct fio_client *client)
1943 struct gfio_client *gc = client->client_data;
1946 gdk_threads_enter();
1948 gfio_set_state(gc->ge, GE_STATE_NEW);
1949 clear_ge_ui_info(gc->ge);
1951 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1952 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1954 gdk_threads_leave();
1957 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1959 struct gfio_client *gc = client->client_data;
1961 gdk_threads_enter();
1963 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1966 entry_set_int_value(gc->err_entry, client->error);
1968 gdk_threads_leave();
1971 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1973 struct gfio_client *gc = client->client_data;
1975 gdk_threads_enter();
1976 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1977 gdk_threads_leave();
1980 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1982 struct gfio_client *gc = client->client_data;
1984 gdk_threads_enter();
1985 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1986 gdk_threads_leave();
1989 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1991 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
1995 struct client_ops gfio_client_ops = {
1996 .text = gfio_text_op,
1997 .disk_util = gfio_disk_util_op,
1998 .thread_status = gfio_thread_status_op,
1999 .group_stats = gfio_group_stats_op,
2000 .jobs_eta = gfio_update_client_eta,
2001 .eta = gfio_update_all_eta,
2002 .probe = gfio_probe_op,
2003 .quit = gfio_quit_op,
2004 .add_job = gfio_add_job_op,
2005 .timed_out = gfio_client_timed_out,
2006 .stop = gfio_client_stop,
2007 .start = gfio_client_start,
2008 .job_start = gfio_client_job_start,
2009 .iolog = gfio_client_iolog,
2010 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
2011 .stay_connected = 1,
2012 .client_type = FIO_CLIENT_TYPE_GUI,
2016 * FIXME: need more handling here
2018 static void ge_destroy(struct gui_entry *ge)
2020 struct gfio_client *gc = ge->client;
2022 if (gc && gc->client) {
2023 if (ge->state >= GE_STATE_CONNECTED)
2024 fio_client_terminate(gc->client);
2026 fio_put_client(gc->client);
2029 flist_del(&ge->list);
2033 static void ge_widget_destroy(GtkWidget *w, gpointer data)
2037 static void gfio_quit(struct gui *ui)
2039 struct gui_entry *ge;
2041 while (!flist_empty(&ui->list)) {
2042 ge = flist_entry(ui->list.next, struct gui_entry, list);
2049 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
2050 __attribute__((unused)) gpointer data)
2055 static void *job_thread(void *arg)
2057 struct gui *ui = arg;
2059 ui->handler_running = 1;
2060 fio_handle_clients(&gfio_client_ops);
2061 ui->handler_running = 0;
2065 static int send_job_files(struct gui_entry *ge)
2067 struct gfio_client *gc = ge->client;
2070 for (i = 0; i < ge->nr_job_files; i++) {
2071 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
2075 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
2076 report_error(error);
2077 g_error_free(error);
2082 free(ge->job_files[i]);
2083 ge->job_files[i] = NULL;
2085 while (i < ge->nr_job_files) {
2086 free(ge->job_files[i]);
2087 ge->job_files[i] = NULL;
2091 free(ge->job_files);
2092 ge->job_files = NULL;
2093 ge->nr_job_files = 0;
2097 static void *server_thread(void *arg)
2100 gfio_server_running = 1;
2101 fio_start_server(NULL);
2102 gfio_server_running = 0;
2106 static void gfio_start_server(void)
2108 struct gui *ui = &main_ui;
2110 if (!gfio_server_running) {
2111 gfio_server_running = 1;
2112 pthread_create(&ui->server_t, NULL, server_thread, NULL);
2113 pthread_detach(ui->server_t);
2117 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2120 struct gui_entry *ge = data;
2121 struct gfio_client *gc = ge->client;
2124 fio_start_client(gc->client);
2127 static void file_open(GtkWidget *w, gpointer data);
2129 static void connect_clicked(GtkWidget *widget, gpointer data)
2131 struct gui_entry *ge = data;
2132 struct gfio_client *gc = ge->client;
2134 if (ge->state == GE_STATE_NEW) {
2137 if (!ge->nr_job_files)
2138 file_open(widget, ge->ui);
2139 if (!ge->nr_job_files)
2144 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2145 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2146 ret = fio_client_connect(gc->client);
2148 if (!ge->ui->handler_running)
2149 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2150 gfio_set_state(ge, GE_STATE_CONNECTED);
2154 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2155 report_error(error);
2156 g_error_free(error);
2159 fio_client_terminate(gc->client);
2160 gfio_set_state(ge, GE_STATE_NEW);
2161 clear_ge_ui_info(ge);
2165 static void send_clicked(GtkWidget *widget, gpointer data)
2167 struct gui_entry *ge = data;
2169 if (send_job_files(ge)) {
2172 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);
2173 report_error(error);
2174 g_error_free(error);
2176 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
2180 static void on_info_bar_response(GtkWidget *widget, gint response,
2183 struct gui *ui = &main_ui;
2185 if (response == GTK_RESPONSE_OK) {
2186 gtk_widget_destroy(widget);
2187 ui->error_info_bar = NULL;
2191 void report_error(GError *error)
2193 struct gui *ui = &main_ui;
2195 if (ui->error_info_bar == NULL) {
2196 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
2199 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2200 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
2203 ui->error_label = gtk_label_new(error->message);
2204 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2205 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
2207 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2208 gtk_widget_show_all(ui->vbox);
2211 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2212 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
2216 struct connection_widgets
2223 static void hostname_cb(GtkEntry *entry, gpointer data)
2225 struct connection_widgets *cw = data;
2226 int uses_net = 0, is_localhost = 0;
2231 * Check whether to display the 'auto start backend' box
2232 * or not. Show it if we are a localhost and using network,
2233 * or using a socket.
2235 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2236 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2241 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2242 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2243 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2244 !strcmp(text, "ip6-loopback"))
2248 if (!uses_net || is_localhost) {
2249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2250 gtk_widget_set_sensitive(cw->button, 1);
2252 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2253 gtk_widget_set_sensitive(cw->button, 0);
2257 static int get_connection_details(char **host, int *port, int *type,
2260 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2261 struct connection_widgets cw;
2264 dialog = gtk_dialog_new_with_buttons("Connection details",
2265 GTK_WINDOW(main_ui.window),
2266 GTK_DIALOG_DESTROY_WITH_PARENT,
2267 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2268 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2270 frame = gtk_frame_new("Hostname / socket name");
2271 /* gtk_dialog_get_content_area() is 2.14 and newer */
2272 vbox = GTK_DIALOG(dialog)->vbox;
2273 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2275 box = gtk_vbox_new(FALSE, 6);
2276 gtk_container_add(GTK_CONTAINER(frame), box);
2278 hbox = gtk_hbox_new(TRUE, 10);
2279 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2280 cw.hentry = gtk_entry_new();
2281 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2282 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2284 frame = gtk_frame_new("Port");
2285 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2286 box = gtk_vbox_new(FALSE, 10);
2287 gtk_container_add(GTK_CONTAINER(frame), box);
2289 hbox = gtk_hbox_new(TRUE, 4);
2290 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2291 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2293 frame = gtk_frame_new("Type");
2294 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2295 box = gtk_vbox_new(FALSE, 10);
2296 gtk_container_add(GTK_CONTAINER(frame), box);
2298 hbox = gtk_hbox_new(TRUE, 4);
2299 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2301 cw.combo = gtk_combo_box_new_text();
2302 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2303 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2304 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2305 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2307 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2309 frame = gtk_frame_new("Options");
2310 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2311 box = gtk_vbox_new(FALSE, 10);
2312 gtk_container_add(GTK_CONTAINER(frame), box);
2314 hbox = gtk_hbox_new(TRUE, 4);
2315 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2317 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2318 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2319 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.");
2320 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2323 * Connect edit signal, so we can show/not-show the auto start button
2325 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2326 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2328 gtk_widget_show_all(dialog);
2330 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2331 gtk_widget_destroy(dialog);
2335 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2336 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2338 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2339 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2340 *type = Fio_client_ipv4;
2341 else if (!strncmp(typeentry, "IPv6", 4))
2342 *type = Fio_client_ipv6;
2344 *type = Fio_client_socket;
2347 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2349 gtk_widget_destroy(dialog);
2353 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2355 struct gfio_client *gc;
2357 gc = malloc(sizeof(*gc));
2358 memset(gc, 0, sizeof(*gc));
2360 gc->client = fio_get_client(client);
2364 client->client_data = gc;
2367 static GtkWidget *new_client_page(struct gui_entry *ge);
2369 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2371 struct gui_entry *ge;
2373 ge = malloc(sizeof(*ge));
2374 memset(ge, 0, sizeof(*ge));
2375 ge->state = GE_STATE_NEW;
2376 INIT_FLIST_HEAD(&ge->list);
2377 flist_add_tail(&ge->list, &ui->list);
2382 static struct gui_entry *get_new_ge_with_tab(const char *name)
2384 struct gui_entry *ge;
2386 ge = alloc_new_gui_entry(&main_ui);
2388 ge->vbox = new_client_page(ge);
2389 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2391 ge->page_label = gtk_label_new(name);
2392 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2394 gtk_widget_show_all(main_ui.window);
2398 static void file_new(GtkWidget *w, gpointer data)
2400 struct gui *ui = (struct gui *) data;
2401 struct gui_entry *ge;
2403 ge = get_new_ge_with_tab("Untitled");
2404 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2408 * Return the 'ge' corresponding to the tab. If the active tab is the
2409 * main tab, open a new tab.
2411 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2413 struct flist_head *entry;
2414 struct gui_entry *ge;
2419 return get_new_ge_with_tab("Untitled");
2425 flist_for_each(entry, &main_ui.list) {
2426 ge = flist_entry(entry, struct gui_entry, list);
2427 if (ge->page_num == cur_page)
2434 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2439 * Main tab is tab 0, so any current page other than 0 holds
2442 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2444 return get_ge_from_page(cur_page, NULL);
2449 static void file_close(GtkWidget *w, gpointer data)
2451 struct gui *ui = (struct gui *) data;
2452 struct gui_entry *ge;
2455 * Can't close the main tab
2457 ge = get_ge_from_cur_tab(ui);
2459 gtk_widget_destroy(ge->vbox);
2463 if (!flist_empty(&ui->list)) {
2464 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2471 static void file_add_recent(struct gui *ui, const gchar *uri)
2475 memset(&grd, 0, sizeof(grd));
2476 grd.display_name = strdup("gfio");
2477 grd.description = strdup("Fio job file");
2478 grd.mime_type = strdup(GFIO_MIME);
2479 grd.app_name = strdup(g_get_application_name());
2480 grd.app_exec = strdup("gfio %f/%u");
2482 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2485 static gchar *get_filename_from_uri(const gchar *uri)
2487 if (strncmp(uri, "file://", 7))
2490 return strdup(uri + 7);
2493 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2496 struct fio_client *client;
2499 filename = get_filename_from_uri(uri);
2501 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2502 ge->job_files[ge->nr_job_files] = strdup(filename);
2505 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2509 error = g_error_new(g_quark_from_string("fio"), 1,
2510 "Failed to add client %s", host);
2511 report_error(error);
2512 g_error_free(error);
2516 gfio_client_added(ge, client);
2517 file_add_recent(ge->ui, uri);
2521 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2523 int port, type, server_start;
2524 struct gui_entry *ge;
2527 int ret, ge_is_new = 0;
2530 * Creates new tab if current tab is the main window, or the
2531 * current tab already has a client.
2533 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2534 ge = get_ge_from_page(cur_page, &ge_is_new);
2536 ge = get_new_ge_with_tab("Untitled");
2540 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2542 if (get_connection_details(&host, &port, &type, &server_start)) {
2544 gtk_widget_destroy(ge->vbox);
2549 ret = do_file_open(ge, uri, host, type, port);
2555 gfio_start_server();
2558 gtk_widget_destroy(ge->vbox);
2564 static void recent_open(GtkAction *action, gpointer data)
2566 struct gui *ui = (struct gui *) data;
2567 GtkRecentInfo *info;
2570 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2571 uri = gtk_recent_info_get_uri(info);
2573 do_file_open_with_tab(ui, uri);
2576 static void file_open(GtkWidget *w, gpointer data)
2578 struct gui *ui = data;
2580 GSList *filenames, *fn_glist;
2581 GtkFileFilter *filter;
2583 dialog = gtk_file_chooser_dialog_new("Open File",
2584 GTK_WINDOW(ui->window),
2585 GTK_FILE_CHOOSER_ACTION_OPEN,
2586 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2587 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2589 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2591 filter = gtk_file_filter_new();
2592 gtk_file_filter_add_pattern(filter, "*.fio");
2593 gtk_file_filter_add_pattern(filter, "*.job");
2594 gtk_file_filter_add_pattern(filter, "*.ini");
2595 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2596 gtk_file_filter_set_name(filter, "Fio job file");
2597 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2599 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2600 gtk_widget_destroy(dialog);
2604 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2606 gtk_widget_destroy(dialog);
2608 filenames = fn_glist;
2609 while (filenames != NULL) {
2610 if (do_file_open_with_tab(ui, filenames->data))
2612 filenames = g_slist_next(filenames);
2615 g_slist_free(fn_glist);
2618 static void file_save(GtkWidget *w, gpointer data)
2620 struct gui *ui = data;
2623 dialog = gtk_file_chooser_dialog_new("Save File",
2624 GTK_WINDOW(ui->window),
2625 GTK_FILE_CHOOSER_ACTION_SAVE,
2626 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2627 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2630 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2631 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2633 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2636 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2637 // save_job_file(filename);
2640 gtk_widget_destroy(dialog);
2643 static void view_log_destroy(GtkWidget *w, gpointer data)
2645 struct gui *ui = (struct gui *) data;
2647 gtk_widget_ref(ui->log_tree);
2648 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2649 gtk_widget_destroy(w);
2650 ui->log_view = NULL;
2653 static void view_log(GtkWidget *w, gpointer data)
2655 GtkWidget *win, *scroll, *vbox, *box;
2656 struct gui *ui = (struct gui *) data;
2661 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2662 gtk_window_set_title(GTK_WINDOW(win), "Log");
2663 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2665 scroll = gtk_scrolled_window_new(NULL, NULL);
2667 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2669 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2671 box = gtk_hbox_new(TRUE, 0);
2672 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2673 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2674 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2676 vbox = gtk_vbox_new(TRUE, 5);
2677 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2679 gtk_container_add(GTK_CONTAINER(win), vbox);
2680 gtk_widget_show_all(win);
2683 static void connect_job_entry(GtkWidget *w, gpointer data)
2685 struct gui *ui = (struct gui *) data;
2686 struct gui_entry *ge;
2688 ge = get_ge_from_cur_tab(ui);
2690 connect_clicked(w, ge);
2693 static void send_job_entry(GtkWidget *w, gpointer data)
2695 struct gui *ui = (struct gui *) data;
2696 struct gui_entry *ge;
2698 ge = get_ge_from_cur_tab(ui);
2700 send_clicked(w, ge);
2704 static void edit_job_entry(GtkWidget *w, gpointer data)
2708 static void start_job_entry(GtkWidget *w, gpointer data)
2710 struct gui *ui = (struct gui *) data;
2711 struct gui_entry *ge;
2713 ge = get_ge_from_cur_tab(ui);
2715 start_job_clicked(w, ge);
2718 static void view_results(GtkWidget *w, gpointer data)
2720 struct gui *ui = (struct gui *) data;
2721 struct gfio_client *gc;
2722 struct gui_entry *ge;
2724 ge = get_ge_from_cur_tab(ui);
2728 if (ge->results_window)
2732 if (gc && gc->nr_results)
2733 gfio_display_end_results(gc);
2736 static void __update_graph_limits(struct gfio_graphs *g)
2738 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2739 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2742 static void update_graph_limits(void)
2744 struct flist_head *entry;
2745 struct gui_entry *ge;
2747 __update_graph_limits(&main_ui.graphs);
2749 flist_for_each(entry, &main_ui.list) {
2750 ge = flist_entry(entry, struct gui_entry, list);
2751 __update_graph_limits(&ge->graphs);
2755 static void preferences(GtkWidget *w, gpointer data)
2757 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2758 GtkWidget *hbox, *spin, *entry, *spin_int;
2761 dialog = gtk_dialog_new_with_buttons("Preferences",
2762 GTK_WINDOW(main_ui.window),
2763 GTK_DIALOG_DESTROY_WITH_PARENT,
2764 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2765 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2768 frame = gtk_frame_new("Graphing");
2769 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2770 vbox = gtk_vbox_new(FALSE, 6);
2771 gtk_container_add(GTK_CONTAINER(frame), vbox);
2773 hbox = gtk_hbox_new(FALSE, 5);
2774 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2775 entry = gtk_label_new("Font face to use for graph labels");
2776 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2778 font = gtk_font_button_new();
2779 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2781 box = gtk_vbox_new(FALSE, 6);
2782 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2784 hbox = gtk_hbox_new(FALSE, 5);
2785 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2786 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2787 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2789 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2791 box = gtk_vbox_new(FALSE, 6);
2792 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2794 hbox = gtk_hbox_new(FALSE, 5);
2795 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2796 entry = gtk_label_new("Client ETA request interval (msec)");
2797 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2799 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2800 frame = gtk_frame_new("Debug logging");
2801 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2802 vbox = gtk_vbox_new(FALSE, 6);
2803 gtk_container_add(GTK_CONTAINER(frame), vbox);
2805 box = gtk_hbox_new(FALSE, 6);
2806 gtk_container_add(GTK_CONTAINER(vbox), box);
2808 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2810 for (i = 0; i < FD_DEBUG_MAX; i++) {
2812 box = gtk_hbox_new(FALSE, 6);
2813 gtk_container_add(GTK_CONTAINER(vbox), box);
2817 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2818 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2819 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2822 gtk_widget_show_all(dialog);
2824 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2825 gtk_widget_destroy(dialog);
2829 for (i = 0; i < FD_DEBUG_MAX; i++) {
2832 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2834 fio_debug |= (1UL << i);
2837 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2838 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2839 update_graph_limits();
2840 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2842 gtk_widget_destroy(dialog);
2845 static void about_dialog(GtkWidget *w, gpointer data)
2847 const char *authors[] = {
2848 "Jens Axboe <axboe@kernel.dk>",
2849 "Stephen Carmeron <stephenmcameron@gmail.com>",
2852 const char *license[] = {
2853 "Fio is free software; you can redistribute it and/or modify "
2854 "it under the terms of the GNU General Public License as published by "
2855 "the Free Software Foundation; either version 2 of the License, or "
2856 "(at your option) any later version.\n",
2857 "Fio is distributed in the hope that it will be useful, "
2858 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2859 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2860 "GNU General Public License for more details.\n",
2861 "You should have received a copy of the GNU General Public License "
2862 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2863 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2865 char *license_trans;
2867 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2868 license[2], "\n", NULL);
2870 gtk_show_about_dialog(NULL,
2871 "program-name", "gfio",
2872 "comments", "Gtk2 UI for fio",
2873 "license", license_trans,
2874 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2876 "version", fio_version_string,
2877 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2878 "logo-icon-name", "fio",
2880 "wrap-license", TRUE,
2883 g_free(license_trans);
2886 static GtkActionEntry menu_items[] = {
2887 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2888 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2889 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2890 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2891 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2892 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2893 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2894 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2895 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2896 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2897 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
2898 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2899 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2900 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2901 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2902 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2903 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2905 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2907 static const gchar *ui_string = " \
2909 <menubar name=\"MainMenu\"> \
2910 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2911 <menuitem name=\"New\" action=\"NewFile\" /> \
2912 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2913 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2914 <separator name=\"Separator1\"/> \
2915 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2916 <separator name=\"Separator2\"/> \
2917 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2918 <separator name=\"Separator3\"/> \
2919 <placeholder name=\"FileRecentFiles\"/> \
2920 <separator name=\"Separator4\"/> \
2921 <menuitem name=\"Quit\" action=\"Quit\" /> \
2923 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2924 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2925 <separator name=\"Separator5\"/> \
2926 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2927 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2928 <separator name=\"Separator6\"/> \
2929 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2931 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2932 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2933 <separator name=\"Separator7\"/> \
2934 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2936 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2937 <menuitem name=\"About\" action=\"About\" /> \
2943 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2946 GtkActionGroup *action_group;
2949 action_group = gtk_action_group_new("Menu");
2950 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2952 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2953 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2955 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2957 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2960 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2961 GtkWidget *vbox, GtkUIManager *ui_manager)
2963 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2966 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2968 struct gui_entry *ge = (struct gui_entry *) data;
2971 index = gtk_combo_box_get_active(box);
2973 multitext_set_entry(&ge->eta.iotype, index);
2974 multitext_set_entry(&ge->eta.bs, index);
2975 multitext_set_entry(&ge->eta.ioengine, index);
2976 multitext_set_entry(&ge->eta.iodepth, index);
2979 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2981 struct gui_entry *ge = (struct gui_entry *) data;
2983 multitext_free(&ge->eta.iotype);
2984 multitext_free(&ge->eta.bs);
2985 multitext_free(&ge->eta.ioengine);
2986 multitext_free(&ge->eta.iodepth);
2989 static GtkWidget *new_client_page(struct gui_entry *ge)
2991 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2992 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2994 main_vbox = gtk_vbox_new(FALSE, 3);
2996 top_align = gtk_alignment_new(0, 0, 1, 0);
2997 top_vbox = gtk_vbox_new(FALSE, 3);
2998 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2999 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3001 probe = gtk_frame_new("Job");
3002 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3003 probe_frame = gtk_vbox_new(FALSE, 3);
3004 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3006 probe_box = gtk_hbox_new(FALSE, 3);
3007 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3008 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
3009 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
3010 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
3011 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
3013 probe_box = gtk_hbox_new(FALSE, 3);
3014 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3016 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
3017 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
3018 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
3019 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
3020 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
3021 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
3022 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
3023 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
3024 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
3026 probe_box = gtk_hbox_new(FALSE, 3);
3027 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3028 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3029 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3030 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3031 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3034 * Only add this if we have a commit rate
3037 probe_box = gtk_hbox_new(FALSE, 3);
3038 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3040 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3041 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3043 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3044 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3048 * Set up a drawing area and IOPS and bandwidth graphs
3050 ge->graphs.drawing_area = gtk_drawing_area_new();
3051 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
3052 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3053 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3054 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3055 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3056 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3057 G_CALLBACK(on_config_drawing_area), &ge->graphs);
3058 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3059 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3060 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3061 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3062 ge->graphs.drawing_area);
3063 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
3065 setup_graphs(&ge->graphs);
3068 * Set up alignments for widgets at the bottom of ui,
3069 * align bottom left, expand horizontally but not vertically
3071 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3072 ge->buttonbox = gtk_hbox_new(FALSE, 0);
3073 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3074 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3076 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3079 * Set up thread status progress bar
3081 ge->thread_status_pb = gtk_progress_bar_new();
3082 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3083 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3084 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3090 static GtkWidget *new_main_page(struct gui *ui)
3092 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
3093 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
3095 main_vbox = gtk_vbox_new(FALSE, 3);
3098 * Set up alignments for widgets at the top of ui,
3099 * align top left, expand horizontally but not vertically
3101 top_align = gtk_alignment_new(0, 0, 1, 0);
3102 top_vbox = gtk_vbox_new(FALSE, 0);
3103 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3104 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
3106 probe = gtk_frame_new("Run statistics");
3107 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3108 probe_frame = gtk_vbox_new(FALSE, 3);
3109 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3111 probe_box = gtk_hbox_new(FALSE, 3);
3112 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3113 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
3114 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3115 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3116 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3117 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3120 * Only add this if we have a commit rate
3123 probe_box = gtk_hbox_new(FALSE, 3);
3124 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3126 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3127 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3129 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3130 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3134 * Set up a drawing area and IOPS and bandwidth graphs
3136 ui->graphs.drawing_area = gtk_drawing_area_new();
3137 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
3138 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
3139 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3140 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3141 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3142 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3143 G_CALLBACK(on_config_drawing_area), &ui->graphs);
3144 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3145 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
3146 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
3147 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
3148 ui->graphs.drawing_area);
3149 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
3152 setup_graphs(&ui->graphs);
3155 * Set up alignments for widgets at the bottom of ui,
3156 * align bottom left, expand horizontally but not vertically
3158 bottom_align = gtk_alignment_new(0, 1, 1, 0);
3159 ui->buttonbox = gtk_hbox_new(FALSE, 0);
3160 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3161 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
3164 * Set up thread status progress bar
3166 ui->thread_status_pb = gtk_progress_bar_new();
3167 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
3168 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3169 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3174 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3175 guint page, gpointer data)
3178 struct gui *ui = (struct gui *) data;
3179 struct gui_entry *ge;
3182 set_job_menu_visible(ui, 0);
3183 set_view_results_visible(ui, 0);
3187 set_job_menu_visible(ui, 1);
3188 ge = get_ge_from_page(page, NULL);
3190 update_button_states(ui, ge);
3195 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3197 time_t time_a = gtk_recent_info_get_visited(a);
3198 time_t time_b = gtk_recent_info_get_visited(b);
3200 return time_b - time_a;
3203 static void add_recent_file_items(struct gui *ui)
3205 const gchar *gfio = g_get_application_name();
3206 GList *items, *item;
3209 if (ui->recent_ui_id) {
3210 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3211 gtk_ui_manager_ensure_update(ui->uimanager);
3213 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3215 if (ui->actiongroup) {
3216 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3217 g_object_unref(ui->actiongroup);
3219 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3221 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3223 items = gtk_recent_manager_get_items(ui->recentmanager);
3224 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3226 for (item = items; item && item->data; item = g_list_next(item)) {
3227 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3232 if (!gtk_recent_info_has_application(info, gfio))
3236 * We only support local files for now
3238 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3241 action_name = g_strdup_printf("RecentFile%u", i++);
3242 label = gtk_recent_info_get_display_name(info);
3244 action = g_object_new(GTK_TYPE_ACTION,
3245 "name", action_name,
3246 "label", label, NULL);
3248 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3249 gtk_recent_info_ref(info),
3250 (GDestroyNotify) gtk_recent_info_unref);
3253 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3255 gtk_action_group_add_action(ui->actiongroup, action);
3256 g_object_unref(action);
3258 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3259 "/MainMenu/FileMenu/FileRecentFiles",
3261 GTK_UI_MANAGER_MENUITEM, FALSE);
3263 g_free(action_name);
3269 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3273 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3274 gint x, gint y, GtkSelectionData *data,
3275 guint info, guint time)
3277 struct gui *ui = &main_ui;
3282 source = gtk_drag_get_source_widget(ctx);
3283 if (source && widget == gtk_widget_get_toplevel(source)) {
3284 gtk_drag_finish(ctx, FALSE, FALSE, time);
3288 uris = gtk_selection_data_get_uris(data);
3290 gtk_drag_finish(ctx, FALSE, FALSE, time);
3296 if (do_file_open_with_tab(ui, uris[i]))
3301 gtk_drag_finish(ctx, TRUE, FALSE, time);
3305 static void init_ui(int *argc, char **argv[], struct gui *ui)
3307 GtkSettings *settings;
3310 /* Magical g*thread incantation, you just need this thread stuff.
3311 * Without it, the update that happens in gfio_update_thread_status
3312 * doesn't really happen in a timely fashion, you need expose events
3314 if (!g_thread_supported())
3315 g_thread_init(NULL);
3318 gtk_init(argc, argv);
3319 settings = gtk_settings_get_default();
3320 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3322 gdk_color_parse("white", &white);
3324 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3325 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3326 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3328 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3329 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3331 ui->vbox = gtk_vbox_new(FALSE, 0);
3332 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3334 ui->uimanager = gtk_ui_manager_new();
3335 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3336 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3338 ui->recentmanager = gtk_recent_manager_get_default();
3339 add_recent_file_items(ui);
3341 ui->notebook = gtk_notebook_new();
3342 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3343 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3344 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3345 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3347 vbox = new_main_page(ui);
3348 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3349 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3350 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3352 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3354 gfio_ui_setup_log(ui);
3356 gtk_widget_show_all(ui->window);
3359 int main(int argc, char *argv[], char *envp[])
3361 if (initialize_fio(envp))
3363 if (fio_init_options())
3366 memset(&main_ui, 0, sizeof(main_ui));
3367 INIT_FLIST_HEAD(&main_ui.list);
3369 init_ui(&argc, &argv, &main_ui);
3371 gdk_threads_enter();
3373 gdk_threads_leave();