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;
41 static void view_log(GtkWidget *w, gpointer data);
43 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
45 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
47 static void connect_clicked(GtkWidget *widget, gpointer data);
48 static void start_job_clicked(GtkWidget *widget, gpointer data);
49 static void send_clicked(GtkWidget *widget, gpointer data);
51 static struct button_spec {
52 const char *buttontext;
54 const char *tooltiptext;
55 const int start_insensitive;
56 } buttonspeclist[] = {
57 #define CONNECT_BUTTON 0
59 #define START_JOB_BUTTON 2
60 { "Connect", connect_clicked, "Connect to host", 0 },
61 { "Send", send_clicked, "Send job description to host", 1 },
62 { "Start Job", start_job_clicked,
63 "Start the current job on the server", 1 },
73 struct multitext_widget {
76 unsigned int cur_text;
77 unsigned int max_text;
82 struct multitext_widget iotype;
83 struct multitext_widget ioengine;
84 struct multitext_widget iodepth;
92 GtkWidget *write_iops;
98 #define DRAWING_AREA_XDIM 1000
99 #define DRAWING_AREA_YDIM 400
100 GtkWidget *drawing_area;
101 struct graph *iops_graph;
102 struct graph *bandwidth_graph;
106 * Main window widgets and data
109 GtkUIManager *uimanager;
110 GtkRecentManager *recentmanager;
111 GtkActionGroup *actiongroup;
116 GtkWidget *thread_status_pb;
117 GtkWidget *buttonbox;
119 GtkWidget *error_info_bar;
120 GtkWidget *error_label;
121 GtkListStore *log_model;
124 struct gfio_graphs graphs;
125 struct probe_widget probe;
126 struct eta_widget eta;
132 struct flist_head list;
139 GE_STATE_JOB_STARTED,
140 GE_STATE_JOB_RUNNING,
148 struct flist_head list;
152 GtkWidget *job_notebook;
153 GtkWidget *thread_status_pb;
154 GtkWidget *buttonbox;
155 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
157 GtkWidget *error_info_bar;
158 GtkWidget *error_label;
159 GtkWidget *results_notebook;
160 GtkWidget *results_window;
161 GtkUIManager *results_uimanager;
162 GtkWidget *results_vbox;
163 GtkWidget *results_menu;
164 GtkListStore *log_model;
167 struct gfio_graphs graphs;
168 struct probe_widget probe;
169 struct eta_widget eta;
170 GtkWidget *page_label;
174 struct gfio_client *client;
180 struct gui_entry *ge;
181 struct fio_client *client;
182 GtkWidget *results_widget;
183 GtkWidget *disk_util_frame;
184 GtkWidget *err_entry;
185 unsigned int job_added;
186 struct thread_options o;
189 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
190 static void gfio_update_thread_status_all(char *status_message, double perc);
191 void report_error(GError *error);
193 static struct graph *setup_iops_graph(void)
197 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
198 graph_title(g, "IOPS (IOs/sec)");
199 graph_x_title(g, "Time (secs)");
200 graph_add_label(g, "Read IOPS");
201 graph_add_label(g, "Write IOPS");
202 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
203 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
204 line_graph_set_data_count_limit(g, gfio_graph_limit);
205 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
209 static struct graph *setup_bandwidth_graph(void)
213 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
214 graph_title(g, "Bandwidth (bytes/sec)");
215 graph_x_title(g, "Time (secs)");
216 graph_add_label(g, "Read Bandwidth");
217 graph_add_label(g, "Write Bandwidth");
218 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
219 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
220 graph_set_base_offset(g, 1);
221 line_graph_set_data_count_limit(g, 100);
222 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
226 static void setup_graphs(struct gfio_graphs *g)
228 g->iops_graph = setup_iops_graph();
229 g->bandwidth_graph = setup_bandwidth_graph();
232 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
234 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
235 mt->text[mt->max_text] = strdup(text);
239 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
241 if (index >= mt->max_text)
243 if (!mt->text || !mt->text[index])
246 mt->cur_text = index;
247 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
250 static void multitext_update_entry(struct multitext_widget *mt,
251 unsigned int index, const char *text)
257 free(mt->text[index]);
259 mt->text[index] = strdup(text);
260 if (mt->cur_text == index)
261 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
264 static void multitext_free(struct multitext_widget *mt)
268 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
270 for (i = 0; i < mt->max_text; i++) {
280 static void clear_ge_ui_info(struct gui_entry *ge)
282 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
283 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
284 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
285 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
287 /* should we empty it... */
288 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
290 multitext_update_entry(&ge->eta.iotype, 0, "");
291 multitext_update_entry(&ge->eta.ioengine, 0, "");
292 multitext_update_entry(&ge->eta.iodepth, 0, "");
293 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
294 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
295 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
296 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
297 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
298 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
301 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
303 GtkWidget *entry, *frame;
305 frame = gtk_frame_new(label);
306 entry = gtk_combo_box_new_text();
307 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
308 gtk_container_add(GTK_CONTAINER(frame), entry);
313 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
315 GtkWidget *entry, *frame;
317 frame = gtk_frame_new(label);
318 entry = gtk_entry_new();
319 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
320 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
321 gtk_container_add(GTK_CONTAINER(frame), entry);
326 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
328 GtkWidget *label_widget;
331 frame = gtk_frame_new(label);
332 label_widget = gtk_label_new(NULL);
333 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
334 gtk_container_add(GTK_CONTAINER(frame), label_widget);
339 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
341 GtkWidget *button, *box;
343 box = gtk_hbox_new(FALSE, 3);
344 gtk_container_add(GTK_CONTAINER(hbox), box);
346 button = gtk_spin_button_new_with_range(min, max, 1.0);
347 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
349 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
350 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
355 static void label_set_int_value(GtkWidget *entry, unsigned int val)
359 sprintf(tmp, "%u", val);
360 gtk_label_set_text(GTK_LABEL(entry), tmp);
363 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
367 sprintf(tmp, "%u", val);
368 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
371 static void show_info_dialog(struct gui *ui, const char *title,
374 GtkWidget *dialog, *content, *label;
376 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
377 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
378 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
380 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
381 label = gtk_label_new(message);
382 gtk_container_add(GTK_CONTAINER(content), label);
383 gtk_widget_show_all(dialog);
384 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
385 gtk_dialog_run(GTK_DIALOG(dialog));
386 gtk_widget_destroy(dialog);
390 * Update sensitivity of job buttons and job menu items, based on the
391 * state of the client.
393 static void update_button_states(struct gui *ui, struct gui_entry *ge)
395 unsigned int connect_state, send_state, start_state, edit_state;
396 const char *connect_str = NULL;
403 sprintf(tmp, "Bad client state: %u\n", ge->state);
404 show_info_dialog(ui, "Error", tmp);
405 /* fall through to new state */
411 connect_str = "Connect";
415 case GE_STATE_CONNECTED:
418 connect_str = "Disconnect";
422 case GE_STATE_JOB_SENT:
425 connect_str = "Disconnect";
429 case GE_STATE_JOB_STARTED:
432 connect_str = "Disconnect";
436 case GE_STATE_JOB_RUNNING:
439 connect_str = "Disconnect";
443 case GE_STATE_JOB_DONE:
446 connect_str = "Connect";
452 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
453 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
454 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
455 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
458 * So the below doesn't work at all, how to set those menu items
461 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Connect");
462 gtk_widget_set_sensitive(w, connect_state);
463 gtk_menu_item_set_label(GTK_MENU_ITEM(w), connect_str);
465 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Edit job");
466 gtk_widget_set_sensitive(w, edit_state);
468 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Send job");
469 gtk_widget_set_sensitive(w, send_state);
471 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Start job");
472 gtk_widget_set_sensitive(w, start_state);
475 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
478 update_button_states(ge->ui, ge);
482 #define ALIGN_RIGHT 2
486 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
488 GtkCellRenderer *renderer;
489 GtkTreeViewColumn *col;
490 double xalign = 0.0; /* left as default */
491 PangoAlignment align;
494 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
495 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
497 visible = !(flags & INVISIBLE);
499 renderer = gtk_cell_renderer_text_new();
500 col = gtk_tree_view_column_new();
502 gtk_tree_view_column_set_title(col, title);
503 if (!(flags & UNSORTABLE))
504 gtk_tree_view_column_set_sort_column_id(col, index);
505 gtk_tree_view_column_set_resizable(col, TRUE);
506 gtk_tree_view_column_pack_start(col, renderer, TRUE);
507 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
508 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
510 case PANGO_ALIGN_LEFT:
513 case PANGO_ALIGN_CENTER:
516 case PANGO_ALIGN_RIGHT:
520 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
521 gtk_tree_view_column_set_visible(col, visible);
522 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
526 static void gfio_ui_setup_log(struct gui *ui)
528 GtkTreeSelection *selection;
530 GtkWidget *tree_view;
532 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
534 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
535 gtk_widget_set_can_focus(tree_view, FALSE);
537 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
538 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
539 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
540 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
542 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
543 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
544 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
545 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
547 ui->log_model = model;
548 ui->log_tree = tree_view;
551 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
557 GType types[FIO_IO_U_LIST_MAX_LEN];
558 GtkWidget *tree_view;
559 GtkTreeSelection *selection;
564 for (i = 0; i < len; i++)
565 types[i] = G_TYPE_INT;
567 model = gtk_list_store_newv(len, types);
569 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
570 gtk_widget_set_can_focus(tree_view, FALSE);
572 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
573 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
575 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
576 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
578 for (i = 0; i < len; i++) {
581 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
582 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
585 gtk_list_store_append(model, &iter);
587 for (i = 0; i < len; i++) {
589 ovals[i] = (ovals[i] + 999) / 1000;
590 gtk_list_store_set(model, &iter, i, ovals[i], -1);
596 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
599 unsigned int *io_u_plat = ts->io_u_plat[ddir];
600 unsigned long nr = ts->clat_stat[ddir].samples;
601 fio_fp64_t *plist = ts->percentile_list;
602 unsigned int *ovals, len, minv, maxv, scale_down;
604 GtkWidget *tree_view, *frame, *hbox;
607 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
612 * We default to usecs, but if the value range is such that we
613 * should scale down to msecs, do that.
615 if (minv > 2000 && maxv > 99999) {
623 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
625 sprintf(tmp, "Completion percentiles (%s)", base);
626 frame = gtk_frame_new(tmp);
627 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
629 hbox = gtk_hbox_new(FALSE, 3);
630 gtk_container_add(GTK_CONTAINER(frame), hbox);
632 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
638 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
639 unsigned long max, double mean, double dev)
641 const char *base = "(usec)";
642 GtkWidget *hbox, *label, *frame;
646 if (!usec_to_msec(&min, &max, &mean, &dev))
649 minp = num2str(min, 6, 1, 0);
650 maxp = num2str(max, 6, 1, 0);
652 sprintf(tmp, "%s %s", name, base);
653 frame = gtk_frame_new(tmp);
654 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
656 hbox = gtk_hbox_new(FALSE, 3);
657 gtk_container_add(GTK_CONTAINER(frame), hbox);
659 label = new_info_label_in_frame(hbox, "Minimum");
660 gtk_label_set_text(GTK_LABEL(label), minp);
661 label = new_info_label_in_frame(hbox, "Maximum");
662 gtk_label_set_text(GTK_LABEL(label), maxp);
663 label = new_info_label_in_frame(hbox, "Average");
664 sprintf(tmp, "%5.02f", mean);
665 gtk_label_set_text(GTK_LABEL(label), tmp);
666 label = new_info_label_in_frame(hbox, "Standard deviation");
667 sprintf(tmp, "%5.02f", dev);
668 gtk_label_set_text(GTK_LABEL(label), tmp);
679 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
680 struct thread_stat *ts, int ddir)
682 const char *ddir_label[2] = { "Read", "Write" };
683 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
684 unsigned long min[3], max[3], runt;
685 unsigned long long bw, iops;
686 unsigned int flags = 0;
687 double mean[3], dev[3];
688 char *io_p, *bw_p, *iops_p;
691 if (!ts->runtime[ddir])
694 i2p = is_power_of_2(rs->kb_base);
695 runt = ts->runtime[ddir];
697 bw = (1000 * ts->io_bytes[ddir]) / runt;
698 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
699 bw_p = num2str(bw, 6, 1, i2p);
701 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
702 iops_p = num2str(iops, 6, 1, 0);
704 box = gtk_hbox_new(FALSE, 3);
705 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
707 frame = gtk_frame_new(ddir_label[ddir]);
708 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
710 main_vbox = gtk_vbox_new(FALSE, 3);
711 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
713 box = gtk_hbox_new(FALSE, 3);
714 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
716 label = new_info_label_in_frame(box, "IO");
717 gtk_label_set_text(GTK_LABEL(label), io_p);
718 label = new_info_label_in_frame(box, "Bandwidth");
719 gtk_label_set_text(GTK_LABEL(label), bw_p);
720 label = new_info_label_in_frame(box, "IOPS");
721 gtk_label_set_text(GTK_LABEL(label), iops_p);
722 label = new_info_label_in_frame(box, "Runtime (msec)");
723 label_set_int_value(label, ts->runtime[ddir]);
725 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
726 double p_of_agg = 100.0;
727 const char *bw_str = "KB";
731 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
732 if (p_of_agg > 100.0)
736 if (mean[0] > 999999.9) {
744 sprintf(tmp, "Bandwidth (%s)", bw_str);
745 frame = gtk_frame_new(tmp);
746 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
748 box = gtk_hbox_new(FALSE, 3);
749 gtk_container_add(GTK_CONTAINER(frame), box);
751 label = new_info_label_in_frame(box, "Minimum");
752 label_set_int_value(label, min[0]);
753 label = new_info_label_in_frame(box, "Maximum");
754 label_set_int_value(label, max[0]);
755 label = new_info_label_in_frame(box, "Percentage of jobs");
756 sprintf(tmp, "%3.2f%%", p_of_agg);
757 gtk_label_set_text(GTK_LABEL(label), tmp);
758 label = new_info_label_in_frame(box, "Average");
759 sprintf(tmp, "%5.02f", mean[0]);
760 gtk_label_set_text(GTK_LABEL(label), tmp);
761 label = new_info_label_in_frame(box, "Standard deviation");
762 sprintf(tmp, "%5.02f", dev[0]);
763 gtk_label_set_text(GTK_LABEL(label), tmp);
766 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
768 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
770 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
774 frame = gtk_frame_new("Latency");
775 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
777 vbox = gtk_vbox_new(FALSE, 3);
778 gtk_container_add(GTK_CONTAINER(frame), vbox);
780 if (flags & GFIO_SLAT)
781 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
782 if (flags & GFIO_CLAT)
783 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
784 if (flags & GFIO_LAT)
785 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
788 if (ts->clat_percentiles)
789 gfio_show_clat_percentiles(main_vbox, ts, ddir);
797 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
800 GtkWidget *tree_view;
801 GtkTreeSelection *selection;
808 * Check if all are empty, in which case don't bother
810 for (i = 0, skipped = 0; i < num; i++)
817 types = malloc(num * sizeof(GType));
819 for (i = 0; i < num; i++)
820 types[i] = G_TYPE_STRING;
822 model = gtk_list_store_newv(num, types);
826 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
827 gtk_widget_set_can_focus(tree_view, FALSE);
829 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
830 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
832 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
833 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
835 for (i = 0; i < num; i++)
836 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
838 gtk_list_store_append(model, &iter);
840 for (i = 0; i < num; i++) {
844 sprintf(fbuf, "0.00");
846 sprintf(fbuf, "%3.2f%%", lat[i]);
848 gtk_list_store_set(model, &iter, i, fbuf, -1);
854 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
856 GtkWidget *box, *frame, *tree_view;
857 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
858 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
859 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
860 "250", "500", "750", "1000", };
861 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
862 "250", "500", "750", "1000", "2000",
865 stat_calc_lat_u(ts, io_u_lat_u);
866 stat_calc_lat_m(ts, io_u_lat_m);
868 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
870 frame = gtk_frame_new("Latency buckets (usec)");
871 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
873 box = gtk_hbox_new(FALSE, 3);
874 gtk_container_add(GTK_CONTAINER(frame), box);
875 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
878 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
880 frame = gtk_frame_new("Latency buckets (msec)");
881 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
883 box = gtk_hbox_new(FALSE, 3);
884 gtk_container_add(GTK_CONTAINER(frame), box);
885 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
889 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
891 GtkWidget *box, *frame, *entry;
892 double usr_cpu, sys_cpu;
893 unsigned long runtime;
896 runtime = ts->total_run_time;
898 double runt = (double) runtime;
900 usr_cpu = (double) ts->usr_time * 100 / runt;
901 sys_cpu = (double) ts->sys_time * 100 / runt;
907 frame = gtk_frame_new("OS resources");
908 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
910 box = gtk_hbox_new(FALSE, 3);
911 gtk_container_add(GTK_CONTAINER(frame), box);
913 entry = new_info_entry_in_frame(box, "User CPU");
914 sprintf(tmp, "%3.2f%%", usr_cpu);
915 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
916 entry = new_info_entry_in_frame(box, "System CPU");
917 sprintf(tmp, "%3.2f%%", sys_cpu);
918 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
919 entry = new_info_entry_in_frame(box, "Context switches");
920 entry_set_int_value(entry, ts->ctx);
921 entry = new_info_entry_in_frame(box, "Major faults");
922 entry_set_int_value(entry, ts->majf);
923 entry = new_info_entry_in_frame(box, "Minor faults");
924 entry_set_int_value(entry, ts->minf);
926 static void gfio_add_sc_depths_tree(GtkListStore *model,
927 struct thread_stat *ts, unsigned int len,
930 double io_u_dist[FIO_IO_U_MAP_NR];
932 /* Bits 0, and 3-8 */
933 const int add_mask = 0x1f9;
937 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
939 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
941 gtk_list_store_append(model, &iter);
943 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
945 for (i = 1, j = 0; i < len; i++) {
948 if (!(add_mask & (1UL << (i - 1))))
949 sprintf(fbuf, "0.0%%");
951 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
955 gtk_list_store_set(model, &iter, i, fbuf, -1);
960 static void gfio_add_total_depths_tree(GtkListStore *model,
961 struct thread_stat *ts, unsigned int len)
963 double io_u_dist[FIO_IO_U_MAP_NR];
965 /* Bits 1-6, and 8 */
966 const int add_mask = 0x17e;
969 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
971 gtk_list_store_append(model, &iter);
973 gtk_list_store_set(model, &iter, 0, "Total", -1);
975 for (i = 1, j = 0; i < len; i++) {
978 if (!(add_mask & (1UL << (i - 1))))
979 sprintf(fbuf, "0.0%%");
981 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
985 gtk_list_store_set(model, &iter, i, fbuf, -1);
990 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
992 GtkWidget *frame, *box, *tree_view;
993 GtkTreeSelection *selection;
995 GType types[FIO_IO_U_MAP_NR + 1];
998 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1000 frame = gtk_frame_new("IO depths");
1001 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1003 box = gtk_hbox_new(FALSE, 3);
1004 gtk_container_add(GTK_CONTAINER(frame), box);
1006 for (i = 0; i < NR_LABELS; i++)
1007 types[i] = G_TYPE_STRING;
1009 model = gtk_list_store_newv(NR_LABELS, types);
1011 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1012 gtk_widget_set_can_focus(tree_view, FALSE);
1014 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1015 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1017 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1018 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1020 for (i = 0; i < NR_LABELS; i++)
1021 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1023 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1024 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1025 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1027 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1030 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1032 struct gui_entry *ge = (struct gui_entry *) data;
1034 gtk_widget_destroy(w);
1035 ge->results_window = NULL;
1036 ge->results_notebook = NULL;
1040 static void results_close(GtkWidget *w, gpointer *data)
1042 struct gui_entry *ge = (struct gui_entry *) data;
1044 gtk_widget_destroy(ge->results_window);
1047 static GtkActionEntry results_menu_items[] = {
1048 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1049 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1050 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1052 static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1054 static const gchar *results_ui_string = " \
1056 <menubar name=\"MainMenu\"> \
1057 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1058 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1060 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1066 static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1068 GtkActionGroup *action_group;
1072 ge->results_uimanager = gtk_ui_manager_new();
1074 action_group = gtk_action_group_new("ResultsMenu");
1075 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1077 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1078 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1080 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1082 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1086 static GtkWidget *get_results_window(struct gui_entry *ge)
1088 GtkWidget *win, *notebook, *vbox;
1090 if (ge->results_window)
1091 return ge->results_notebook;
1093 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1094 gtk_window_set_title(GTK_WINDOW(win), "Results");
1095 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1096 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1097 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1099 vbox = gtk_vbox_new(FALSE, 0);
1100 gtk_container_add(GTK_CONTAINER(win), vbox);
1102 ge->results_menu = get_results_menubar(win, ge);
1103 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1105 notebook = gtk_notebook_new();
1106 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1107 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1108 gtk_container_add(GTK_CONTAINER(vbox), notebook);
1110 ge->results_window = win;
1111 ge->results_notebook = notebook;
1112 return ge->results_notebook;
1115 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1116 struct group_run_stats *rs)
1118 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
1119 struct gfio_client *gc = client->client_data;
1121 gdk_threads_enter();
1123 res_win = get_results_window(gc->ge);
1125 scroll = gtk_scrolled_window_new(NULL, NULL);
1126 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1127 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1129 vbox = gtk_vbox_new(FALSE, 3);
1131 box = gtk_hbox_new(FALSE, 0);
1132 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1134 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1136 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1138 gc->results_widget = vbox;
1140 entry = new_info_entry_in_frame(box, "Name");
1141 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1142 if (strlen(ts->description)) {
1143 entry = new_info_entry_in_frame(box, "Description");
1144 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1146 entry = new_info_entry_in_frame(box, "Group ID");
1147 entry_set_int_value(entry, ts->groupid);
1148 entry = new_info_entry_in_frame(box, "Jobs");
1149 entry_set_int_value(entry, ts->members);
1150 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1151 entry_set_int_value(entry, ts->error);
1152 entry = new_info_entry_in_frame(box, "PID");
1153 entry_set_int_value(entry, ts->pid);
1155 if (ts->io_bytes[DDIR_READ])
1156 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1157 if (ts->io_bytes[DDIR_WRITE])
1158 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1160 gfio_show_latency_buckets(vbox, ts);
1161 gfio_show_cpu_usage(vbox, ts);
1162 gfio_show_io_depths(vbox, ts);
1164 gtk_widget_show_all(gc->ge->results_window);
1165 gdk_threads_leave();
1168 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1170 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1171 struct gui *ui = &main_ui;
1175 char tmp[64], timebuf[80];
1178 tm = localtime(&sec);
1179 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1180 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1182 gdk_threads_enter();
1184 gtk_list_store_append(ui->log_model, &iter);
1185 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1186 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1187 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1188 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1190 if (p->level == FIO_LOG_ERR)
1191 view_log(NULL, (gpointer) ui);
1193 gdk_threads_leave();
1196 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1198 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1199 struct gfio_client *gc = client->client_data;
1200 GtkWidget *box, *frame, *entry, *vbox;
1204 gdk_threads_enter();
1206 if (!gc->results_widget)
1209 if (!gc->disk_util_frame) {
1210 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1211 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1214 vbox = gtk_vbox_new(FALSE, 3);
1215 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1217 frame = gtk_frame_new((char *) p->dus.name);
1218 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1220 box = gtk_vbox_new(FALSE, 3);
1221 gtk_container_add(GTK_CONTAINER(frame), box);
1223 frame = gtk_frame_new("Read");
1224 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1225 vbox = gtk_hbox_new(TRUE, 3);
1226 gtk_container_add(GTK_CONTAINER(frame), vbox);
1227 entry = new_info_entry_in_frame(vbox, "IOs");
1228 entry_set_int_value(entry, p->dus.ios[0]);
1229 entry = new_info_entry_in_frame(vbox, "Merges");
1230 entry_set_int_value(entry, p->dus.merges[0]);
1231 entry = new_info_entry_in_frame(vbox, "Sectors");
1232 entry_set_int_value(entry, p->dus.sectors[0]);
1233 entry = new_info_entry_in_frame(vbox, "Ticks");
1234 entry_set_int_value(entry, p->dus.ticks[0]);
1236 frame = gtk_frame_new("Write");
1237 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1238 vbox = gtk_hbox_new(TRUE, 3);
1239 gtk_container_add(GTK_CONTAINER(frame), vbox);
1240 entry = new_info_entry_in_frame(vbox, "IOs");
1241 entry_set_int_value(entry, p->dus.ios[1]);
1242 entry = new_info_entry_in_frame(vbox, "Merges");
1243 entry_set_int_value(entry, p->dus.merges[1]);
1244 entry = new_info_entry_in_frame(vbox, "Sectors");
1245 entry_set_int_value(entry, p->dus.sectors[1]);
1246 entry = new_info_entry_in_frame(vbox, "Ticks");
1247 entry_set_int_value(entry, p->dus.ticks[1]);
1249 frame = gtk_frame_new("Shared");
1250 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1251 vbox = gtk_hbox_new(TRUE, 3);
1252 gtk_container_add(GTK_CONTAINER(frame), vbox);
1253 entry = new_info_entry_in_frame(vbox, "IO ticks");
1254 entry_set_int_value(entry, p->dus.io_ticks);
1255 entry = new_info_entry_in_frame(vbox, "Time in queue");
1256 entry_set_int_value(entry, p->dus.time_in_queue);
1260 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1264 sprintf(tmp, "%3.2f%%", util);
1265 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1266 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1268 gtk_widget_show_all(gc->results_widget);
1270 gdk_threads_leave();
1273 extern int sum_stat_clients;
1274 extern struct thread_stat client_ts;
1275 extern struct group_run_stats client_gs;
1277 static int sum_stat_nr;
1279 static void gfio_thread_status_op(struct fio_client *client,
1280 struct fio_net_cmd *cmd)
1282 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1284 gfio_display_ts(client, &p->ts, &p->rs);
1286 if (sum_stat_clients == 1)
1289 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1290 sum_group_stats(&client_gs, &p->rs);
1292 client_ts.members++;
1293 client_ts.groupid = p->ts.groupid;
1295 if (++sum_stat_nr == sum_stat_clients) {
1296 strcpy(client_ts.name, "All clients");
1297 gfio_display_ts(client, &client_ts, &client_gs);
1301 static void gfio_group_stats_op(struct fio_client *client,
1302 struct fio_net_cmd *cmd)
1304 /* We're ignoring group stats for now */
1307 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1310 struct gfio_graphs *g = data;
1312 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1313 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1314 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1315 graph_set_position(g->bandwidth_graph, 0, 0);
1319 static void draw_graph(struct graph *g, cairo_t *cr)
1321 line_graph_draw(g, cr);
1325 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1326 gboolean keyboard_mode, GtkTooltip *tooltip,
1329 struct gfio_graphs *g = data;
1330 const char *text = NULL;
1332 if (graph_contains_xy(g->iops_graph, x, y))
1333 text = graph_find_tooltip(g->iops_graph, x, y);
1334 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1335 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1338 gtk_tooltip_set_text(tooltip, text);
1345 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1347 struct gfio_graphs *g = p;
1350 cr = gdk_cairo_create(w->window);
1352 if (graph_has_tooltips(g->iops_graph) ||
1353 graph_has_tooltips(g->bandwidth_graph)) {
1354 g_object_set(w, "has-tooltip", TRUE, NULL);
1355 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1358 cairo_set_source_rgb(cr, 0, 0, 0);
1359 draw_graph(g->iops_graph, cr);
1360 draw_graph(g->bandwidth_graph, cr);
1367 * Client specific ETA
1369 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1371 struct gfio_client *gc = client->client_data;
1372 struct gui_entry *ge = gc->ge;
1373 static int eta_good;
1380 gdk_threads_enter();
1385 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1386 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1387 eta_to_str(eta_str, je->eta_sec);
1390 sprintf(tmp, "%u", je->nr_running);
1391 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1392 sprintf(tmp, "%u", je->files_open);
1393 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1396 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1397 if (je->m_rate || je->t_rate) {
1400 mr = num2str(je->m_rate, 4, 0, i2p);
1401 tr = num2str(je->t_rate, 4, 0, i2p);
1402 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1403 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1406 } else if (je->m_iops || je->t_iops)
1407 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1409 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1410 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1411 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1412 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1415 if (je->eta_sec != INT_MAX && je->nr_running) {
1419 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1420 strcpy(output, "-.-% done");
1424 sprintf(output, "%3.1f%% done", perc);
1427 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1428 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1430 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1431 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1433 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1434 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1435 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1436 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1438 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1439 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1440 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1441 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1450 char *dst = output + strlen(output);
1452 sprintf(dst, " - %s", eta_str);
1455 gfio_update_thread_status(ge, output, perc);
1456 gdk_threads_leave();
1460 * Update ETA in main window for all clients
1462 static void gfio_update_all_eta(struct jobs_eta *je)
1464 struct gui *ui = &main_ui;
1465 static int eta_good;
1471 gdk_threads_enter();
1476 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1477 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1478 eta_to_str(eta_str, je->eta_sec);
1482 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1483 if (je->m_rate || je->t_rate) {
1486 mr = num2str(je->m_rate, 4, 0, i2p);
1487 tr = num2str(je->t_rate, 4, 0, i2p);
1488 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1489 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1492 } else if (je->m_iops || je->t_iops)
1493 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1495 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1496 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1497 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1498 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1501 entry_set_int_value(ui->eta.jobs, je->nr_running);
1503 if (je->eta_sec != INT_MAX && je->nr_running) {
1507 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1508 strcpy(output, "-.-% done");
1512 sprintf(output, "%3.1f%% done", perc);
1515 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1516 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1518 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1519 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1521 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1522 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1523 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1524 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1526 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1527 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1528 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1529 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1538 char *dst = output + strlen(output);
1540 sprintf(dst, " - %s", eta_str);
1543 gfio_update_thread_status_all(output, perc);
1544 gdk_threads_leave();
1547 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1549 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1550 struct gfio_client *gc = client->client_data;
1551 struct gui_entry *ge = gc->ge;
1552 const char *os, *arch;
1555 os = fio_get_os_string(probe->os);
1559 arch = fio_get_arch_string(probe->arch);
1564 client->name = strdup((char *) probe->hostname);
1566 gdk_threads_enter();
1568 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1569 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1570 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1571 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1572 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1574 gfio_set_state(ge, GE_STATE_CONNECTED);
1576 gdk_threads_leave();
1579 static void gfio_update_thread_status(struct gui_entry *ge,
1580 char *status_message, double perc)
1582 static char message[100];
1583 const char *m = message;
1585 strncpy(message, status_message, sizeof(message) - 1);
1586 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1587 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1588 gtk_widget_queue_draw(main_ui.window);
1591 static void gfio_update_thread_status_all(char *status_message, double perc)
1593 struct gui *ui = &main_ui;
1594 static char message[100];
1595 const char *m = message;
1597 strncpy(message, status_message, sizeof(message) - 1);
1598 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1599 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1600 gtk_widget_queue_draw(ui->window);
1603 static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
1605 struct gfio_client *gc = client->client_data;
1607 gdk_threads_enter();
1608 gfio_set_state(gc->ge, GE_STATE_NEW);
1609 gdk_threads_leave();
1612 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1614 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1615 struct gfio_client *gc = client->client_data;
1616 struct thread_options *o = &gc->o;
1617 struct gui_entry *ge = gc->ge;
1620 convert_thread_options_to_cpu(o, &p->top);
1622 gdk_threads_enter();
1624 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1626 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1627 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1629 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1630 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1632 sprintf(tmp, "%u", o->iodepth);
1633 multitext_add_entry(&ge->eta.iodepth, tmp);
1635 multitext_set_entry(&ge->eta.iotype, 0);
1636 multitext_set_entry(&ge->eta.ioengine, 0);
1637 multitext_set_entry(&ge->eta.iodepth, 0);
1641 gfio_set_state(ge, GE_STATE_JOB_SENT);
1643 gdk_threads_leave();
1646 static void gfio_client_timed_out(struct fio_client *client)
1648 struct gfio_client *gc = client->client_data;
1651 gdk_threads_enter();
1653 gfio_set_state(gc->ge, GE_STATE_NEW);
1654 clear_ge_ui_info(gc->ge);
1656 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1657 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1659 gdk_threads_leave();
1662 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1664 struct gfio_client *gc = client->client_data;
1666 gdk_threads_enter();
1668 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1671 entry_set_int_value(gc->err_entry, client->error);
1673 gdk_threads_leave();
1676 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1678 struct gfio_client *gc = client->client_data;
1680 gdk_threads_enter();
1681 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1682 gdk_threads_leave();
1685 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1687 struct gfio_client *gc = client->client_data;
1689 gdk_threads_enter();
1690 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1691 gdk_threads_leave();
1694 static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1699 struct client_ops gfio_client_ops = {
1700 .text = gfio_text_op,
1701 .disk_util = gfio_disk_util_op,
1702 .thread_status = gfio_thread_status_op,
1703 .group_stats = gfio_group_stats_op,
1704 .jobs_eta = gfio_update_client_eta,
1705 .eta = gfio_update_all_eta,
1706 .probe = gfio_probe_op,
1707 .quit = gfio_quit_op,
1708 .add_job = gfio_add_job_op,
1709 .timed_out = gfio_client_timed_out,
1710 .stop = gfio_client_stop,
1711 .start = gfio_client_start,
1712 .job_start = gfio_client_job_start,
1713 .iolog = gfio_client_iolog,
1714 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1715 .stay_connected = 1,
1716 .client_type = FIO_CLIENT_TYPE_GUI,
1720 * FIXME: need more handling here
1722 static void ge_destroy(struct gui_entry *ge)
1724 struct gfio_client *gc = ge->client;
1726 if (gc && gc->client) {
1727 if (ge->state >= GE_STATE_CONNECTED)
1728 fio_client_terminate(gc->client);
1730 fio_put_client(gc->client);
1733 flist_del(&ge->list);
1737 static void ge_widget_destroy(GtkWidget *w, gpointer data)
1741 static void gfio_quit(struct gui *ui)
1743 struct gui_entry *ge;
1745 while (!flist_empty(&ui->list)) {
1746 ge = flist_entry(ui->list.next, struct gui_entry, list);
1753 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1754 __attribute__((unused)) gpointer data)
1759 static void *job_thread(void *arg)
1761 struct gui *ui = arg;
1763 ui->handler_running = 1;
1764 fio_handle_clients(&gfio_client_ops);
1765 ui->handler_running = 0;
1769 static int send_job_files(struct gui_entry *ge)
1771 struct gfio_client *gc = ge->client;
1774 for (i = 0; i < ge->nr_job_files; i++) {
1775 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1779 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1780 report_error(error);
1781 g_error_free(error);
1786 free(ge->job_files[i]);
1787 ge->job_files[i] = NULL;
1789 while (i < ge->nr_job_files) {
1790 free(ge->job_files[i]);
1791 ge->job_files[i] = NULL;
1795 free(ge->job_files);
1796 ge->job_files = NULL;
1797 ge->nr_job_files = 0;
1801 static void *server_thread(void *arg)
1804 gfio_server_running = 1;
1805 fio_start_server(NULL);
1806 gfio_server_running = 0;
1810 static void gfio_start_server(void)
1812 struct gui *ui = &main_ui;
1814 if (!gfio_server_running) {
1815 gfio_server_running = 1;
1816 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1817 pthread_detach(ui->server_t);
1821 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1824 struct gui_entry *ge = data;
1825 struct gfio_client *gc = ge->client;
1828 fio_start_client(gc->client);
1831 static void file_open(GtkWidget *w, gpointer data);
1833 static void connect_clicked(GtkWidget *widget, gpointer data)
1835 struct gui_entry *ge = data;
1836 struct gfio_client *gc = ge->client;
1838 if (ge->state == GE_STATE_NEW) {
1841 if (!ge->nr_job_files)
1842 file_open(widget, ge->ui);
1843 if (!ge->nr_job_files)
1846 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1847 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1848 ret = fio_client_connect(gc->client);
1850 if (!ge->ui->handler_running)
1851 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1852 gfio_set_state(ge, GE_STATE_CONNECTED);
1856 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1857 report_error(error);
1858 g_error_free(error);
1861 fio_client_terminate(gc->client);
1862 gfio_set_state(ge, GE_STATE_NEW);
1863 clear_ge_ui_info(ge);
1867 static void send_clicked(GtkWidget *widget, gpointer data)
1869 struct gui_entry *ge = data;
1871 if (send_job_files(ge)) {
1874 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);
1875 report_error(error);
1876 g_error_free(error);
1878 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1882 static GtkWidget *add_button(GtkWidget *buttonbox,
1883 struct button_spec *buttonspec, gpointer data)
1885 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1887 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1888 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1889 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1890 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1895 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1900 for (i = 0; i < nbuttons; i++)
1901 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1904 static void on_info_bar_response(GtkWidget *widget, gint response,
1907 struct gui *ui = &main_ui;
1909 if (response == GTK_RESPONSE_OK) {
1910 gtk_widget_destroy(widget);
1911 ui->error_info_bar = NULL;
1915 void report_error(GError *error)
1917 struct gui *ui = &main_ui;
1919 if (ui->error_info_bar == NULL) {
1920 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1923 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1924 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1927 ui->error_label = gtk_label_new(error->message);
1928 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1929 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1931 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1932 gtk_widget_show_all(ui->vbox);
1935 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1936 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1940 struct connection_widgets
1947 static void hostname_cb(GtkEntry *entry, gpointer data)
1949 struct connection_widgets *cw = data;
1950 int uses_net = 0, is_localhost = 0;
1955 * Check whether to display the 'auto start backend' box
1956 * or not. Show it if we are a localhost and using network,
1957 * or using a socket.
1959 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1960 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1965 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1966 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1967 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1968 !strcmp(text, "ip6-loopback"))
1972 if (!uses_net || is_localhost) {
1973 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1974 gtk_widget_set_sensitive(cw->button, 1);
1976 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1977 gtk_widget_set_sensitive(cw->button, 0);
1981 static int get_connection_details(char **host, int *port, int *type,
1984 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1985 struct connection_widgets cw;
1988 dialog = gtk_dialog_new_with_buttons("Connection details",
1989 GTK_WINDOW(main_ui.window),
1990 GTK_DIALOG_DESTROY_WITH_PARENT,
1991 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1992 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1994 frame = gtk_frame_new("Hostname / socket name");
1995 /* gtk_dialog_get_content_area() is 2.14 and newer */
1996 vbox = GTK_DIALOG(dialog)->vbox;
1997 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1999 box = gtk_vbox_new(FALSE, 6);
2000 gtk_container_add(GTK_CONTAINER(frame), box);
2002 hbox = gtk_hbox_new(TRUE, 10);
2003 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2004 cw.hentry = gtk_entry_new();
2005 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2006 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
2008 frame = gtk_frame_new("Port");
2009 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2010 box = gtk_vbox_new(FALSE, 10);
2011 gtk_container_add(GTK_CONTAINER(frame), box);
2013 hbox = gtk_hbox_new(TRUE, 4);
2014 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2015 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2017 frame = gtk_frame_new("Type");
2018 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2019 box = gtk_vbox_new(FALSE, 10);
2020 gtk_container_add(GTK_CONTAINER(frame), box);
2022 hbox = gtk_hbox_new(TRUE, 4);
2023 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2025 cw.combo = gtk_combo_box_new_text();
2026 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2027 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2028 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2029 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
2031 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
2033 frame = gtk_frame_new("Options");
2034 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2035 box = gtk_vbox_new(FALSE, 10);
2036 gtk_container_add(GTK_CONTAINER(frame), box);
2038 hbox = gtk_hbox_new(TRUE, 4);
2039 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2041 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2042 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2043 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.");
2044 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2047 * Connect edit signal, so we can show/not-show the auto start button
2049 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2050 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
2052 gtk_widget_show_all(dialog);
2054 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2055 gtk_widget_destroy(dialog);
2059 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2060 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2062 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2063 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2064 *type = Fio_client_ipv4;
2065 else if (!strncmp(typeentry, "IPv6", 4))
2066 *type = Fio_client_ipv6;
2068 *type = Fio_client_socket;
2071 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2073 gtk_widget_destroy(dialog);
2077 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2079 struct gfio_client *gc;
2081 gc = malloc(sizeof(*gc));
2082 memset(gc, 0, sizeof(*gc));
2084 gc->client = fio_get_client(client);
2088 client->client_data = gc;
2091 static GtkWidget *new_client_page(struct gui_entry *ge);
2093 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2095 struct gui_entry *ge;
2097 ge = malloc(sizeof(*ge));
2098 memset(ge, 0, sizeof(*ge));
2099 ge->state = GE_STATE_NEW;
2100 INIT_FLIST_HEAD(&ge->list);
2101 flist_add_tail(&ge->list, &ui->list);
2106 static struct gui_entry *get_new_ge_with_tab(const char *name)
2108 struct gui_entry *ge;
2110 ge = alloc_new_gui_entry(&main_ui);
2112 ge->vbox = new_client_page(ge);
2113 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
2115 ge->page_label = gtk_label_new(name);
2116 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2118 gtk_widget_show_all(main_ui.window);
2122 static void file_new(GtkWidget *w, gpointer data)
2124 struct gui *ui = (struct gui *) data;
2125 struct gui_entry *ge;
2127 ge = get_new_ge_with_tab("Untitled");
2128 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2132 * Return the 'ge' corresponding to the tab. If the active tab is the
2133 * main tab, open a new tab.
2135 static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
2137 struct flist_head *entry;
2138 struct gui_entry *ge;
2143 return get_new_ge_with_tab("Untitled");
2149 flist_for_each(entry, &main_ui.list) {
2150 ge = flist_entry(entry, struct gui_entry, list);
2151 if (ge->page_num == cur_page)
2158 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2163 * Main tab is tab 0, so any current page other than 0 holds
2166 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2168 return get_ge_from_page(cur_page, NULL);
2173 static void file_close(GtkWidget *w, gpointer data)
2175 struct gui *ui = (struct gui *) data;
2176 struct gui_entry *ge;
2179 * Can't close the main tab
2181 ge = get_ge_from_cur_tab(ui);
2183 gtk_widget_destroy(ge->vbox);
2187 if (!flist_empty(&ui->list)) {
2188 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2195 static void file_add_recent(struct gui *ui, const gchar *uri)
2199 memset(&grd, 0, sizeof(grd));
2200 grd.display_name = strdup("gfio");
2201 grd.description = strdup("Fio job file");
2202 grd.mime_type = strdup(GFIO_MIME);
2203 grd.app_name = strdup(g_get_application_name());
2204 grd.app_exec = strdup("gfio %f/%u");
2206 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
2209 static gchar *get_filename_from_uri(const gchar *uri)
2211 if (strncmp(uri, "file://", 7))
2214 return strdup(uri + 7);
2217 static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2220 struct fio_client *client;
2223 filename = get_filename_from_uri(uri);
2225 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2226 ge->job_files[ge->nr_job_files] = strdup(filename);
2229 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2233 error = g_error_new(g_quark_from_string("fio"), 1,
2234 "Failed to add client %s", host);
2235 report_error(error);
2236 g_error_free(error);
2240 gfio_client_added(ge, client);
2241 file_add_recent(ge->ui, uri);
2245 static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
2247 int port, type, server_start;
2248 struct gui_entry *ge;
2251 int ret, ge_is_new = 0;
2254 * Creates new tab if current tab is the main window, or the
2255 * current tab already has a client.
2257 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2258 ge = get_ge_from_page(cur_page, &ge_is_new);
2260 ge = get_new_ge_with_tab("Untitled");
2264 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2266 if (get_connection_details(&host, &port, &type, &server_start)) {
2268 gtk_widget_destroy(ge->vbox);
2273 ret = do_file_open(ge, uri, host, type, port);
2279 gfio_start_server();
2282 gtk_widget_destroy(ge->vbox);
2288 static void recent_open(GtkAction *action, gpointer data)
2290 struct gui *ui = (struct gui *) data;
2291 GtkRecentInfo *info;
2294 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2295 uri = gtk_recent_info_get_uri(info);
2297 do_file_open_with_tab(ui, uri);
2300 static void file_open(GtkWidget *w, gpointer data)
2302 struct gui *ui = data;
2304 GSList *filenames, *fn_glist;
2305 GtkFileFilter *filter;
2307 dialog = gtk_file_chooser_dialog_new("Open File",
2308 GTK_WINDOW(ui->window),
2309 GTK_FILE_CHOOSER_ACTION_OPEN,
2310 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2311 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2313 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2315 filter = gtk_file_filter_new();
2316 gtk_file_filter_add_pattern(filter, "*.fio");
2317 gtk_file_filter_add_pattern(filter, "*.job");
2318 gtk_file_filter_add_pattern(filter, "*.ini");
2319 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
2320 gtk_file_filter_set_name(filter, "Fio job file");
2321 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2323 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2324 gtk_widget_destroy(dialog);
2328 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2330 gtk_widget_destroy(dialog);
2332 filenames = fn_glist;
2333 while (filenames != NULL) {
2334 if (do_file_open_with_tab(ui, filenames->data))
2336 filenames = g_slist_next(filenames);
2339 g_slist_free(fn_glist);
2342 static void file_save(GtkWidget *w, gpointer data)
2344 struct gui *ui = data;
2347 dialog = gtk_file_chooser_dialog_new("Save File",
2348 GTK_WINDOW(ui->window),
2349 GTK_FILE_CHOOSER_ACTION_SAVE,
2350 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2351 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2354 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2355 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2357 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2360 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2361 // save_job_file(filename);
2364 gtk_widget_destroy(dialog);
2367 static void view_log_destroy(GtkWidget *w, gpointer data)
2369 struct gui *ui = (struct gui *) data;
2371 gtk_widget_ref(ui->log_tree);
2372 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2373 gtk_widget_destroy(w);
2374 ui->log_view = NULL;
2377 static void view_log(GtkWidget *w, gpointer data)
2379 GtkWidget *win, *scroll, *vbox, *box;
2380 struct gui *ui = (struct gui *) data;
2385 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2386 gtk_window_set_title(GTK_WINDOW(win), "Log");
2387 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2389 scroll = gtk_scrolled_window_new(NULL, NULL);
2391 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2393 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2395 box = gtk_hbox_new(TRUE, 0);
2396 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2397 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2398 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2400 vbox = gtk_vbox_new(TRUE, 5);
2401 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2403 gtk_container_add(GTK_CONTAINER(win), vbox);
2404 gtk_widget_show_all(win);
2407 static void connect_job_entry(GtkWidget *w, gpointer data)
2409 struct gui *ui = (struct gui *) data;
2410 struct gui_entry *ge;
2412 ge = get_ge_from_cur_tab(ui);
2414 connect_clicked(w, ge);
2417 static void send_job_entry(GtkWidget *w, gpointer data)
2419 struct gui *ui = (struct gui *) data;
2420 struct gui_entry *ge;
2422 ge = get_ge_from_cur_tab(ui);
2424 send_clicked(w, ge);
2428 static void edit_job_entry(GtkWidget *w, gpointer data)
2432 static void start_job_entry(GtkWidget *w, gpointer data)
2434 struct gui *ui = (struct gui *) data;
2435 struct gui_entry *ge;
2437 ge = get_ge_from_cur_tab(ui);
2439 start_job_clicked(w, ge);
2442 static void __update_graph_limits(struct gfio_graphs *g)
2444 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2445 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2448 static void update_graph_limits(void)
2450 struct flist_head *entry;
2451 struct gui_entry *ge;
2453 __update_graph_limits(&main_ui.graphs);
2455 flist_for_each(entry, &main_ui.list) {
2456 ge = flist_entry(entry, struct gui_entry, list);
2457 __update_graph_limits(&ge->graphs);
2461 static void preferences(GtkWidget *w, gpointer data)
2463 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2464 GtkWidget *hbox, *spin, *entry, *spin_int;
2467 dialog = gtk_dialog_new_with_buttons("Preferences",
2468 GTK_WINDOW(main_ui.window),
2469 GTK_DIALOG_DESTROY_WITH_PARENT,
2470 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2471 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2474 frame = gtk_frame_new("Graphing");
2475 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2476 vbox = gtk_vbox_new(FALSE, 6);
2477 gtk_container_add(GTK_CONTAINER(frame), vbox);
2479 hbox = gtk_hbox_new(FALSE, 5);
2480 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2481 entry = gtk_label_new("Font face to use for graph labels");
2482 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2484 font = gtk_font_button_new();
2485 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2487 box = gtk_vbox_new(FALSE, 6);
2488 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2490 hbox = gtk_hbox_new(FALSE, 5);
2491 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2492 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2493 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2495 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2497 box = gtk_vbox_new(FALSE, 6);
2498 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2500 hbox = gtk_hbox_new(FALSE, 5);
2501 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2502 entry = gtk_label_new("Client ETA request interval (msec)");
2503 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2505 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2506 frame = gtk_frame_new("Debug logging");
2507 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2508 vbox = gtk_vbox_new(FALSE, 6);
2509 gtk_container_add(GTK_CONTAINER(frame), vbox);
2511 box = gtk_hbox_new(FALSE, 6);
2512 gtk_container_add(GTK_CONTAINER(vbox), box);
2514 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2516 for (i = 0; i < FD_DEBUG_MAX; i++) {
2518 box = gtk_hbox_new(FALSE, 6);
2519 gtk_container_add(GTK_CONTAINER(vbox), box);
2523 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2524 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2525 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2528 gtk_widget_show_all(dialog);
2530 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2531 gtk_widget_destroy(dialog);
2535 for (i = 0; i < FD_DEBUG_MAX; i++) {
2538 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2540 fio_debug |= (1UL << i);
2543 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2544 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2545 update_graph_limits();
2546 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2548 gtk_widget_destroy(dialog);
2551 static void about_dialog(GtkWidget *w, gpointer data)
2553 const char *authors[] = {
2554 "Jens Axboe <axboe@kernel.dk>",
2555 "Stephen Carmeron <stephenmcameron@gmail.com>",
2558 const char *license[] = {
2559 "Fio is free software; you can redistribute it and/or modify "
2560 "it under the terms of the GNU General Public License as published by "
2561 "the Free Software Foundation; either version 2 of the License, or "
2562 "(at your option) any later version.\n",
2563 "Fio is distributed in the hope that it will be useful, "
2564 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2565 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2566 "GNU General Public License for more details.\n",
2567 "You should have received a copy of the GNU General Public License "
2568 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2569 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2571 char *license_trans;
2573 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2574 license[2], "\n", NULL);
2576 gtk_show_about_dialog(NULL,
2577 "program-name", "gfio",
2578 "comments", "Gtk2 UI for fio",
2579 "license", license_trans,
2580 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2582 "version", fio_version_string,
2583 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2584 "logo-icon-name", "fio",
2586 "wrap-license", TRUE,
2589 g_free(license_trans);
2592 static GtkActionEntry menu_items[] = {
2593 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2594 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2595 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2596 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2597 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2598 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2599 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2600 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2601 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2602 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2603 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2604 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2605 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2606 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2607 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2608 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2610 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2612 static const gchar *ui_string = " \
2614 <menubar name=\"MainMenu\"> \
2615 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2616 <menuitem name=\"New\" action=\"NewFile\" /> \
2617 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2618 <separator name=\"Separator1\"/> \
2619 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2620 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2621 <separator name=\"Separator2\"/> \
2622 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2623 <separator name=\"Separator3\"/> \
2624 <placeholder name=\"FileRecentFiles\"/> \
2625 <separator name=\"Separator4\"/> \
2626 <menuitem name=\"Quit\" action=\"Quit\" /> \
2628 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2629 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2630 <separator name=\"Separator5\"/> \
2631 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2632 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2633 <separator name=\"Separator6\"/> \
2634 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2636 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2637 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2639 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2640 <menuitem name=\"About\" action=\"About\" /> \
2646 static void set_job_menu_visible(struct gui *ui, int visible)
2650 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2651 gtk_widget_set_sensitive(job, visible);
2654 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2657 GtkActionGroup *action_group;
2660 action_group = gtk_action_group_new("Menu");
2661 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2663 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2664 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2666 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2668 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2671 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2672 GtkWidget *vbox, GtkUIManager *ui_manager)
2674 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2677 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2679 struct gui_entry *ge = (struct gui_entry *) data;
2682 index = gtk_combo_box_get_active(box);
2684 multitext_set_entry(&ge->eta.iotype, index);
2685 multitext_set_entry(&ge->eta.ioengine, index);
2686 multitext_set_entry(&ge->eta.iodepth, index);
2689 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2691 struct gui_entry *ge = (struct gui_entry *) data;
2693 multitext_free(&ge->eta.iotype);
2694 multitext_free(&ge->eta.ioengine);
2695 multitext_free(&ge->eta.iodepth);
2698 static GtkWidget *new_client_page(struct gui_entry *ge)
2700 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2701 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2704 main_vbox = gtk_vbox_new(FALSE, 3);
2706 top_align = gtk_alignment_new(0, 0, 1, 0);
2707 top_vbox = gtk_vbox_new(FALSE, 3);
2708 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2709 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2711 probe = gtk_frame_new("Job");
2712 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2713 probe_frame = gtk_vbox_new(FALSE, 3);
2714 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2716 probe_box = gtk_hbox_new(FALSE, 3);
2717 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2718 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2719 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2720 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2721 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2723 probe_box = gtk_hbox_new(FALSE, 3);
2724 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2726 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2727 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2728 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2729 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2730 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2731 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2732 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2733 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2735 probe_box = gtk_hbox_new(FALSE, 3);
2736 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2737 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2738 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2739 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2740 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2743 * Only add this if we have a commit rate
2746 probe_box = gtk_hbox_new(FALSE, 3);
2747 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2749 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2750 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2752 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2753 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2757 * Set up a drawing area and IOPS and bandwidth graphs
2759 gdk_color_parse("white", &white);
2760 ge->graphs.drawing_area = gtk_drawing_area_new();
2761 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2762 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2763 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2764 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2765 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2766 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2767 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2768 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2769 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2770 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2771 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2772 ge->graphs.drawing_area);
2773 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2775 setup_graphs(&ge->graphs);
2778 * Set up alignments for widgets at the bottom of ui,
2779 * align bottom left, expand horizontally but not vertically
2781 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2782 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2783 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2784 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2786 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2789 * Set up thread status progress bar
2791 ge->thread_status_pb = gtk_progress_bar_new();
2792 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2793 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2794 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2800 static GtkWidget *new_main_page(struct gui *ui)
2802 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2803 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2806 main_vbox = gtk_vbox_new(FALSE, 3);
2809 * Set up alignments for widgets at the top of ui,
2810 * align top left, expand horizontally but not vertically
2812 top_align = gtk_alignment_new(0, 0, 1, 0);
2813 top_vbox = gtk_vbox_new(FALSE, 0);
2814 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2815 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2817 probe = gtk_frame_new("Run statistics");
2818 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2819 probe_frame = gtk_vbox_new(FALSE, 3);
2820 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2822 probe_box = gtk_hbox_new(FALSE, 3);
2823 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2824 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2825 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2826 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2827 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2828 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2831 * Only add this if we have a commit rate
2834 probe_box = gtk_hbox_new(FALSE, 3);
2835 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2837 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2838 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2840 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2841 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2845 * Set up a drawing area and IOPS and bandwidth graphs
2847 gdk_color_parse("white", &white);
2848 ui->graphs.drawing_area = gtk_drawing_area_new();
2849 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2850 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2851 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2852 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2853 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2854 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2855 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2856 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2857 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2858 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2859 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2860 ui->graphs.drawing_area);
2861 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
2864 setup_graphs(&ui->graphs);
2867 * Set up alignments for widgets at the bottom of ui,
2868 * align bottom left, expand horizontally but not vertically
2870 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2871 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2872 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2873 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2876 * Set up thread status progress bar
2878 ui->thread_status_pb = gtk_progress_bar_new();
2879 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2880 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2881 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2886 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2887 guint page, gpointer data)
2890 struct gui *ui = (struct gui *) data;
2891 struct gui_entry *ge;
2894 set_job_menu_visible(ui, 0);
2898 set_job_menu_visible(ui, 1);
2899 ge = get_ge_from_page(page, NULL);
2901 update_button_states(ui, ge);
2906 static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
2908 time_t time_a = gtk_recent_info_get_visited(a);
2909 time_t time_b = gtk_recent_info_get_visited(b);
2911 return time_b - time_a;
2914 static void add_recent_file_items(struct gui *ui)
2916 const gchar *gfio = g_get_application_name();
2917 GList *items, *item;
2920 if (ui->recent_ui_id) {
2921 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
2922 gtk_ui_manager_ensure_update(ui->uimanager);
2924 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
2926 if (ui->actiongroup) {
2927 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
2928 g_object_unref(ui->actiongroup);
2930 ui->actiongroup = gtk_action_group_new("RecentFileActions");
2932 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
2934 items = gtk_recent_manager_get_items(ui->recentmanager);
2935 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
2937 for (item = items; item && item->data; item = g_list_next(item)) {
2938 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
2943 if (!gtk_recent_info_has_application(info, gfio))
2947 * We only support local files for now
2949 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
2952 action_name = g_strdup_printf("RecentFile%u", i++);
2953 label = gtk_recent_info_get_display_name(info);
2955 action = g_object_new(GTK_TYPE_ACTION,
2956 "name", action_name,
2957 "label", label, NULL);
2959 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
2960 gtk_recent_info_ref(info),
2961 (GDestroyNotify) gtk_recent_info_unref);
2964 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
2966 gtk_action_group_add_action(ui->actiongroup, action);
2967 g_object_unref(action);
2969 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
2970 "/MainMenu/FileMenu/FileRecentFiles",
2972 GTK_UI_MANAGER_MENUITEM, FALSE);
2974 g_free(action_name);
2980 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
2984 static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
2985 gint x, gint y, GtkSelectionData *data,
2986 guint info, guint time)
2988 struct gui *ui = &main_ui;
2993 source = gtk_drag_get_source_widget(ctx);
2994 if (source && widget == gtk_widget_get_toplevel(source)) {
2995 gtk_drag_finish(ctx, FALSE, FALSE, time);
2999 uris = gtk_selection_data_get_uris(data);
3001 gtk_drag_finish(ctx, FALSE, FALSE, time);
3007 if (do_file_open_with_tab(ui, uris[i]))
3012 gtk_drag_finish(ctx, TRUE, FALSE, time);
3016 static void init_ui(int *argc, char **argv[], struct gui *ui)
3018 GtkSettings *settings;
3021 /* Magical g*thread incantation, you just need this thread stuff.
3022 * Without it, the update that happens in gfio_update_thread_status
3023 * doesn't really happen in a timely fashion, you need expose events
3025 if (!g_thread_supported())
3026 g_thread_init(NULL);
3029 gtk_init(argc, argv);
3030 settings = gtk_settings_get_default();
3031 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3034 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3035 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
3036 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3038 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3039 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3041 ui->vbox = gtk_vbox_new(FALSE, 0);
3042 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3044 ui->uimanager = gtk_ui_manager_new();
3045 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3046 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
3048 ui->recentmanager = gtk_recent_manager_get_default();
3049 add_recent_file_items(ui);
3051 ui->notebook = gtk_notebook_new();
3052 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
3053 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
3054 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
3055 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3057 vbox = new_main_page(ui);
3058 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3059 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3060 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
3062 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3064 gfio_ui_setup_log(ui);
3066 gtk_widget_show_all(ui->window);
3069 int main(int argc, char *argv[], char *envp[])
3071 if (initialize_fio(envp))
3073 if (fio_init_options())
3076 memset(&main_ui, 0, sizeof(main_ui));
3077 INIT_FLIST_HEAD(&main_ui.list);
3079 init_ui(&argc, &argv, &main_ui);
3081 gdk_threads_enter();
3083 gdk_threads_leave();