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 static int gfio_server_running;
36 static const char *gfio_graph_font;
37 static unsigned int gfio_graph_limit = 100;
39 static void view_log(GtkWidget *w, gpointer data);
41 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
43 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
45 static void connect_clicked(GtkWidget *widget, gpointer data);
46 static void start_job_clicked(GtkWidget *widget, gpointer data);
47 static void send_clicked(GtkWidget *widget, gpointer data);
49 static struct button_spec {
50 const char *buttontext;
52 const char *tooltiptext;
53 const int start_insensitive;
54 } buttonspeclist[] = {
55 #define CONNECT_BUTTON 0
57 #define START_JOB_BUTTON 2
58 { "Connect", connect_clicked, "Connect to host", 0 },
59 { "Send", send_clicked, "Send job description to host", 1 },
60 { "Start Job", start_job_clicked,
61 "Start the current job on the server", 1 },
71 struct multitext_widget {
74 unsigned int cur_text;
75 unsigned int max_text;
80 struct multitext_widget iotype;
81 struct multitext_widget ioengine;
82 struct multitext_widget iodepth;
90 GtkWidget *write_iops;
96 #define DRAWING_AREA_XDIM 1000
97 #define DRAWING_AREA_YDIM 400
98 GtkWidget *drawing_area;
99 struct graph *iops_graph;
100 struct graph *bandwidth_graph;
104 * Main window widgets and data
107 GtkUIManager *uimanager;
111 GtkWidget *thread_status_pb;
112 GtkWidget *buttonbox;
114 GtkWidget *error_info_bar;
115 GtkWidget *error_label;
116 GtkListStore *log_model;
119 struct gfio_graphs graphs;
120 struct probe_widget probe;
121 struct eta_widget eta;
127 struct flist_head list;
134 GE_STATE_JOB_STARTED,
135 GE_STATE_JOB_RUNNING,
143 struct flist_head list;
147 GtkWidget *job_notebook;
148 GtkWidget *thread_status_pb;
149 GtkWidget *buttonbox;
150 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
152 GtkWidget *error_info_bar;
153 GtkWidget *error_label;
154 GtkWidget *results_notebook;
155 GtkWidget *results_window;
156 GtkListStore *log_model;
159 struct gfio_graphs graphs;
160 struct probe_widget probe;
161 struct eta_widget eta;
162 GtkWidget *page_label;
166 struct gfio_client *client;
172 struct gui_entry *ge;
173 struct fio_client *client;
174 GtkWidget *results_widget;
175 GtkWidget *disk_util_frame;
176 GtkWidget *err_entry;
177 unsigned int job_added;
178 struct thread_options o;
181 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
182 static void gfio_update_thread_status_all(char *status_message, double perc);
183 void report_error(GError *error);
185 static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
187 switch (power_of_ten) {
188 case 9: graph_y_title(g, "Billions of IOs / sec");
190 case 6: graph_y_title(g, "Millions of IOs / sec");
192 case 3: graph_y_title(g, "Thousands of IOs / sec");
195 default: graph_y_title(g, "IOs / sec");
200 static struct graph *setup_iops_graph(void)
204 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
205 graph_title(g, "IOPS");
206 graph_x_title(g, "Time (secs)");
207 graph_y_title(g, "IOs / sec");
208 graph_add_label(g, "Read IOPS");
209 graph_add_label(g, "Write IOPS");
210 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
211 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
212 line_graph_set_data_count_limit(g, gfio_graph_limit);
213 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
214 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
218 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
220 switch (power_of_ten) {
221 case 9: graph_y_title(g, "Petabytes / sec");
223 case 6: graph_y_title(g, "Gigabytes / sec");
225 case 3: graph_y_title(g, "Megabytes / sec");
228 default: graph_y_title(g, "Kilobytes / sec");
233 static struct graph *setup_bandwidth_graph(void)
237 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
238 graph_title(g, "Bandwidth");
239 graph_x_title(g, "Time (secs)");
240 graph_y_title(g, "Kbytes / sec");
241 graph_add_label(g, "Read Bandwidth");
242 graph_add_label(g, "Write Bandwidth");
243 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
244 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
245 line_graph_set_data_count_limit(g, 100);
246 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
247 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
252 static void setup_graphs(struct gfio_graphs *g)
254 g->iops_graph = setup_iops_graph();
255 g->bandwidth_graph = setup_bandwidth_graph();
258 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
260 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
261 mt->text[mt->max_text] = strdup(text);
265 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
267 if (index >= mt->max_text)
269 if (!mt->text || !mt->text[index])
272 mt->cur_text = index;
273 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
276 static void multitext_update_entry(struct multitext_widget *mt,
277 unsigned int index, const char *text)
283 free(mt->text[index]);
285 mt->text[index] = strdup(text);
286 if (mt->cur_text == index)
287 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
290 static void multitext_free(struct multitext_widget *mt)
294 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
296 for (i = 0; i < mt->max_text; i++) {
306 static void clear_ge_ui_info(struct gui_entry *ge)
308 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
309 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
310 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
311 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
313 /* should we empty it... */
314 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
316 multitext_update_entry(&ge->eta.iotype, 0, "");
317 multitext_update_entry(&ge->eta.ioengine, 0, "");
318 multitext_update_entry(&ge->eta.iodepth, 0, "");
319 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
320 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
321 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
322 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
323 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
324 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
327 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
329 GtkWidget *entry, *frame;
331 frame = gtk_frame_new(label);
332 entry = gtk_combo_box_new_text();
333 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
334 gtk_container_add(GTK_CONTAINER(frame), entry);
339 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
341 GtkWidget *entry, *frame;
343 frame = gtk_frame_new(label);
344 entry = gtk_entry_new();
345 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
346 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
347 gtk_container_add(GTK_CONTAINER(frame), entry);
352 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
354 GtkWidget *label_widget;
357 frame = gtk_frame_new(label);
358 label_widget = gtk_label_new(NULL);
359 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
360 gtk_container_add(GTK_CONTAINER(frame), label_widget);
365 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
367 GtkWidget *button, *box;
369 box = gtk_hbox_new(FALSE, 3);
370 gtk_container_add(GTK_CONTAINER(hbox), box);
372 button = gtk_spin_button_new_with_range(min, max, 1.0);
373 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
375 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
376 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
381 static void label_set_int_value(GtkWidget *entry, unsigned int val)
385 sprintf(tmp, "%u", val);
386 gtk_label_set_text(GTK_LABEL(entry), tmp);
389 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
393 sprintf(tmp, "%u", val);
394 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
397 static void show_info_dialog(struct gui *ui, const char *title,
400 GtkWidget *dialog, *content, *label;
402 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
403 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
404 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
406 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
407 label = gtk_label_new(message);
408 gtk_container_add(GTK_CONTAINER(content), label);
409 gtk_widget_show_all(dialog);
410 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
411 gtk_dialog_run(GTK_DIALOG(dialog));
412 gtk_widget_destroy(dialog);
416 * Update sensitivity of job buttons and job menu items, based on the
417 * state of the client.
419 static void update_button_states(struct gui *ui, struct gui_entry *ge)
421 unsigned int connect_state, send_state, start_state, edit_state;
422 const char *connect_str = NULL;
429 sprintf(tmp, "Bad client state: %u\n", ge->state);
430 show_info_dialog(ui, "Error", tmp);
431 /* fall through to new state */
437 connect_str = "Connect";
441 case GE_STATE_CONNECTED:
444 connect_str = "Disconnect";
448 case GE_STATE_JOB_SENT:
451 connect_str = "Disconnect";
455 case GE_STATE_JOB_STARTED:
458 connect_str = "Disconnect";
462 case GE_STATE_JOB_RUNNING:
465 connect_str = "Disconnect";
469 case GE_STATE_JOB_DONE:
472 connect_str = "Connect";
478 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
479 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
480 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
481 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
484 * So the below doesn't work at all, how to set those menu items
487 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Connect");
488 gtk_widget_set_sensitive(w, connect_state);
489 gtk_menu_item_set_label(GTK_MENU_ITEM(w), connect_str);
491 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Edit job");
492 gtk_widget_set_sensitive(w, edit_state);
494 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Send job");
495 gtk_widget_set_sensitive(w, send_state);
497 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Start job");
498 gtk_widget_set_sensitive(w, start_state);
501 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
504 update_button_states(ge->ui, ge);
508 #define ALIGN_RIGHT 2
512 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
514 GtkCellRenderer *renderer;
515 GtkTreeViewColumn *col;
516 double xalign = 0.0; /* left as default */
517 PangoAlignment align;
520 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
521 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
523 visible = !(flags & INVISIBLE);
525 renderer = gtk_cell_renderer_text_new();
526 col = gtk_tree_view_column_new();
528 gtk_tree_view_column_set_title(col, title);
529 if (!(flags & UNSORTABLE))
530 gtk_tree_view_column_set_sort_column_id(col, index);
531 gtk_tree_view_column_set_resizable(col, TRUE);
532 gtk_tree_view_column_pack_start(col, renderer, TRUE);
533 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
534 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
536 case PANGO_ALIGN_LEFT:
539 case PANGO_ALIGN_CENTER:
542 case PANGO_ALIGN_RIGHT:
546 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
547 gtk_tree_view_column_set_visible(col, visible);
548 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
552 static void gfio_ui_setup_log(struct gui *ui)
554 GtkTreeSelection *selection;
556 GtkWidget *tree_view;
558 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
560 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
561 gtk_widget_set_can_focus(tree_view, FALSE);
563 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
564 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
565 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
566 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
568 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
569 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
570 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
571 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
573 ui->log_model = model;
574 ui->log_tree = tree_view;
577 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
583 GType types[FIO_IO_U_LIST_MAX_LEN];
584 GtkWidget *tree_view;
585 GtkTreeSelection *selection;
590 for (i = 0; i < len; i++)
591 types[i] = G_TYPE_INT;
593 model = gtk_list_store_newv(len, types);
595 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
596 gtk_widget_set_can_focus(tree_view, FALSE);
598 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
599 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
601 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
602 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
604 for (i = 0; i < len; i++) {
607 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
608 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
611 gtk_list_store_append(model, &iter);
613 for (i = 0; i < len; i++) {
615 ovals[i] = (ovals[i] + 999) / 1000;
616 gtk_list_store_set(model, &iter, i, ovals[i], -1);
622 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
625 unsigned int *io_u_plat = ts->io_u_plat[ddir];
626 unsigned long nr = ts->clat_stat[ddir].samples;
627 fio_fp64_t *plist = ts->percentile_list;
628 unsigned int *ovals, len, minv, maxv, scale_down;
630 GtkWidget *tree_view, *frame, *hbox;
633 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
638 * We default to usecs, but if the value range is such that we
639 * should scale down to msecs, do that.
641 if (minv > 2000 && maxv > 99999) {
649 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
651 sprintf(tmp, "Completion percentiles (%s)", base);
652 frame = gtk_frame_new(tmp);
653 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
655 hbox = gtk_hbox_new(FALSE, 3);
656 gtk_container_add(GTK_CONTAINER(frame), hbox);
658 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
664 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
665 unsigned long max, double mean, double dev)
667 const char *base = "(usec)";
668 GtkWidget *hbox, *label, *frame;
672 if (!usec_to_msec(&min, &max, &mean, &dev))
675 minp = num2str(min, 6, 1, 0);
676 maxp = num2str(max, 6, 1, 0);
678 sprintf(tmp, "%s %s", name, base);
679 frame = gtk_frame_new(tmp);
680 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
682 hbox = gtk_hbox_new(FALSE, 3);
683 gtk_container_add(GTK_CONTAINER(frame), hbox);
685 label = new_info_label_in_frame(hbox, "Minimum");
686 gtk_label_set_text(GTK_LABEL(label), minp);
687 label = new_info_label_in_frame(hbox, "Maximum");
688 gtk_label_set_text(GTK_LABEL(label), maxp);
689 label = new_info_label_in_frame(hbox, "Average");
690 sprintf(tmp, "%5.02f", mean);
691 gtk_label_set_text(GTK_LABEL(label), tmp);
692 label = new_info_label_in_frame(hbox, "Standard deviation");
693 sprintf(tmp, "%5.02f", dev);
694 gtk_label_set_text(GTK_LABEL(label), tmp);
705 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
706 struct thread_stat *ts, int ddir)
708 const char *ddir_label[2] = { "Read", "Write" };
709 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
710 unsigned long min[3], max[3], runt;
711 unsigned long long bw, iops;
712 unsigned int flags = 0;
713 double mean[3], dev[3];
714 char *io_p, *bw_p, *iops_p;
717 if (!ts->runtime[ddir])
720 i2p = is_power_of_2(rs->kb_base);
721 runt = ts->runtime[ddir];
723 bw = (1000 * ts->io_bytes[ddir]) / runt;
724 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
725 bw_p = num2str(bw, 6, 1, i2p);
727 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
728 iops_p = num2str(iops, 6, 1, 0);
730 box = gtk_hbox_new(FALSE, 3);
731 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
733 frame = gtk_frame_new(ddir_label[ddir]);
734 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
736 main_vbox = gtk_vbox_new(FALSE, 3);
737 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
739 box = gtk_hbox_new(FALSE, 3);
740 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
742 label = new_info_label_in_frame(box, "IO");
743 gtk_label_set_text(GTK_LABEL(label), io_p);
744 label = new_info_label_in_frame(box, "Bandwidth");
745 gtk_label_set_text(GTK_LABEL(label), bw_p);
746 label = new_info_label_in_frame(box, "IOPS");
747 gtk_label_set_text(GTK_LABEL(label), iops_p);
748 label = new_info_label_in_frame(box, "Runtime (msec)");
749 label_set_int_value(label, ts->runtime[ddir]);
751 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
752 double p_of_agg = 100.0;
753 const char *bw_str = "KB";
757 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
758 if (p_of_agg > 100.0)
762 if (mean[0] > 999999.9) {
770 sprintf(tmp, "Bandwidth (%s)", bw_str);
771 frame = gtk_frame_new(tmp);
772 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
774 box = gtk_hbox_new(FALSE, 3);
775 gtk_container_add(GTK_CONTAINER(frame), box);
777 label = new_info_label_in_frame(box, "Minimum");
778 label_set_int_value(label, min[0]);
779 label = new_info_label_in_frame(box, "Maximum");
780 label_set_int_value(label, max[0]);
781 label = new_info_label_in_frame(box, "Percentage of jobs");
782 sprintf(tmp, "%3.2f%%", p_of_agg);
783 gtk_label_set_text(GTK_LABEL(label), tmp);
784 label = new_info_label_in_frame(box, "Average");
785 sprintf(tmp, "%5.02f", mean[0]);
786 gtk_label_set_text(GTK_LABEL(label), tmp);
787 label = new_info_label_in_frame(box, "Standard deviation");
788 sprintf(tmp, "%5.02f", dev[0]);
789 gtk_label_set_text(GTK_LABEL(label), tmp);
792 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
794 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
796 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
800 frame = gtk_frame_new("Latency");
801 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
803 vbox = gtk_vbox_new(FALSE, 3);
804 gtk_container_add(GTK_CONTAINER(frame), vbox);
806 if (flags & GFIO_SLAT)
807 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
808 if (flags & GFIO_CLAT)
809 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
810 if (flags & GFIO_LAT)
811 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
814 if (ts->clat_percentiles)
815 gfio_show_clat_percentiles(main_vbox, ts, ddir);
823 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
826 GtkWidget *tree_view;
827 GtkTreeSelection *selection;
834 * Check if all are empty, in which case don't bother
836 for (i = 0, skipped = 0; i < num; i++)
843 types = malloc(num * sizeof(GType));
845 for (i = 0; i < num; i++)
846 types[i] = G_TYPE_STRING;
848 model = gtk_list_store_newv(num, types);
852 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
853 gtk_widget_set_can_focus(tree_view, FALSE);
855 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
856 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
858 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
859 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
861 for (i = 0; i < num; i++)
862 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
864 gtk_list_store_append(model, &iter);
866 for (i = 0; i < num; i++) {
870 sprintf(fbuf, "0.00");
872 sprintf(fbuf, "%3.2f%%", lat[i]);
874 gtk_list_store_set(model, &iter, i, fbuf, -1);
880 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
882 GtkWidget *box, *frame, *tree_view;
883 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
884 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
885 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
886 "250", "500", "750", "1000", };
887 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
888 "250", "500", "750", "1000", "2000",
891 stat_calc_lat_u(ts, io_u_lat_u);
892 stat_calc_lat_m(ts, io_u_lat_m);
894 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
896 frame = gtk_frame_new("Latency buckets (usec)");
897 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
899 box = gtk_hbox_new(FALSE, 3);
900 gtk_container_add(GTK_CONTAINER(frame), box);
901 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
904 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
906 frame = gtk_frame_new("Latency buckets (msec)");
907 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
909 box = gtk_hbox_new(FALSE, 3);
910 gtk_container_add(GTK_CONTAINER(frame), box);
911 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
915 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
917 GtkWidget *box, *frame, *entry;
918 double usr_cpu, sys_cpu;
919 unsigned long runtime;
922 runtime = ts->total_run_time;
924 double runt = (double) runtime;
926 usr_cpu = (double) ts->usr_time * 100 / runt;
927 sys_cpu = (double) ts->sys_time * 100 / runt;
933 frame = gtk_frame_new("OS resources");
934 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
936 box = gtk_hbox_new(FALSE, 3);
937 gtk_container_add(GTK_CONTAINER(frame), box);
939 entry = new_info_entry_in_frame(box, "User CPU");
940 sprintf(tmp, "%3.2f%%", usr_cpu);
941 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
942 entry = new_info_entry_in_frame(box, "System CPU");
943 sprintf(tmp, "%3.2f%%", sys_cpu);
944 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
945 entry = new_info_entry_in_frame(box, "Context switches");
946 entry_set_int_value(entry, ts->ctx);
947 entry = new_info_entry_in_frame(box, "Major faults");
948 entry_set_int_value(entry, ts->majf);
949 entry = new_info_entry_in_frame(box, "Minor faults");
950 entry_set_int_value(entry, ts->minf);
952 static void gfio_add_sc_depths_tree(GtkListStore *model,
953 struct thread_stat *ts, unsigned int len,
956 double io_u_dist[FIO_IO_U_MAP_NR];
958 /* Bits 0, and 3-8 */
959 const int add_mask = 0x1f9;
963 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
965 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
967 gtk_list_store_append(model, &iter);
969 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
971 for (i = 1, j = 0; i < len; i++) {
974 if (!(add_mask & (1UL << (i - 1))))
975 sprintf(fbuf, "0.0%%");
977 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
981 gtk_list_store_set(model, &iter, i, fbuf, -1);
986 static void gfio_add_total_depths_tree(GtkListStore *model,
987 struct thread_stat *ts, unsigned int len)
989 double io_u_dist[FIO_IO_U_MAP_NR];
991 /* Bits 1-6, and 8 */
992 const int add_mask = 0x17e;
995 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
997 gtk_list_store_append(model, &iter);
999 gtk_list_store_set(model, &iter, 0, "Total", -1);
1001 for (i = 1, j = 0; i < len; i++) {
1004 if (!(add_mask & (1UL << (i - 1))))
1005 sprintf(fbuf, "0.0%%");
1007 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1011 gtk_list_store_set(model, &iter, i, fbuf, -1);
1016 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1018 GtkWidget *frame, *box, *tree_view;
1019 GtkTreeSelection *selection;
1020 GtkListStore *model;
1021 GType types[FIO_IO_U_MAP_NR + 1];
1023 #define NR_LABELS 10
1024 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1026 frame = gtk_frame_new("IO depths");
1027 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1029 box = gtk_hbox_new(FALSE, 3);
1030 gtk_container_add(GTK_CONTAINER(frame), box);
1032 for (i = 0; i < NR_LABELS; i++)
1033 types[i] = G_TYPE_STRING;
1035 model = gtk_list_store_newv(NR_LABELS, types);
1037 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1038 gtk_widget_set_can_focus(tree_view, FALSE);
1040 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1041 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1043 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1044 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1046 for (i = 0; i < NR_LABELS; i++)
1047 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1049 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1050 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1051 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1053 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1056 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1058 struct gui_entry *ge = (struct gui_entry *) data;
1060 gtk_widget_destroy(w);
1061 ge->results_window = NULL;
1062 ge->results_notebook = NULL;
1066 static GtkWidget *get_results_window(struct gui_entry *ge)
1068 GtkWidget *win, *notebook;
1070 if (ge->results_window)
1071 return ge->results_notebook;
1073 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1074 gtk_window_set_title(GTK_WINDOW(win), "Results");
1075 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1076 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1077 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1079 notebook = gtk_notebook_new();
1080 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1081 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1082 gtk_container_add(GTK_CONTAINER(win), notebook);
1084 ge->results_window = win;
1085 ge->results_notebook = notebook;
1086 return ge->results_notebook;
1089 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1090 struct group_run_stats *rs)
1092 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
1093 struct gfio_client *gc = client->client_data;
1095 gdk_threads_enter();
1097 res_win = get_results_window(gc->ge);
1099 scroll = gtk_scrolled_window_new(NULL, NULL);
1100 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1101 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1103 vbox = gtk_vbox_new(FALSE, 3);
1105 box = gtk_hbox_new(FALSE, 0);
1106 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1108 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1110 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1112 gc->results_widget = vbox;
1114 entry = new_info_entry_in_frame(box, "Name");
1115 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1116 if (strlen(ts->description)) {
1117 entry = new_info_entry_in_frame(box, "Description");
1118 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1120 entry = new_info_entry_in_frame(box, "Group ID");
1121 entry_set_int_value(entry, ts->groupid);
1122 entry = new_info_entry_in_frame(box, "Jobs");
1123 entry_set_int_value(entry, ts->members);
1124 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1125 entry_set_int_value(entry, ts->error);
1126 entry = new_info_entry_in_frame(box, "PID");
1127 entry_set_int_value(entry, ts->pid);
1129 if (ts->io_bytes[DDIR_READ])
1130 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1131 if (ts->io_bytes[DDIR_WRITE])
1132 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1134 gfio_show_latency_buckets(vbox, ts);
1135 gfio_show_cpu_usage(vbox, ts);
1136 gfio_show_io_depths(vbox, ts);
1138 gtk_widget_show_all(gc->ge->results_window);
1139 gdk_threads_leave();
1142 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1144 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1145 struct gui *ui = &main_ui;
1149 char tmp[64], timebuf[80];
1152 tm = localtime(&sec);
1153 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1154 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1156 gdk_threads_enter();
1158 gtk_list_store_append(ui->log_model, &iter);
1159 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1160 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1161 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1162 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1164 if (p->level == FIO_LOG_ERR)
1165 view_log(NULL, (gpointer) ui);
1167 gdk_threads_leave();
1170 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1172 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1173 struct gfio_client *gc = client->client_data;
1174 GtkWidget *box, *frame, *entry, *vbox;
1178 gdk_threads_enter();
1180 if (!gc->results_widget)
1183 if (!gc->disk_util_frame) {
1184 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1185 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1188 vbox = gtk_vbox_new(FALSE, 3);
1189 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1191 frame = gtk_frame_new((char *) p->dus.name);
1192 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1194 box = gtk_vbox_new(FALSE, 3);
1195 gtk_container_add(GTK_CONTAINER(frame), box);
1197 frame = gtk_frame_new("Read");
1198 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1199 vbox = gtk_hbox_new(TRUE, 3);
1200 gtk_container_add(GTK_CONTAINER(frame), vbox);
1201 entry = new_info_entry_in_frame(vbox, "IOs");
1202 entry_set_int_value(entry, p->dus.ios[0]);
1203 entry = new_info_entry_in_frame(vbox, "Merges");
1204 entry_set_int_value(entry, p->dus.merges[0]);
1205 entry = new_info_entry_in_frame(vbox, "Sectors");
1206 entry_set_int_value(entry, p->dus.sectors[0]);
1207 entry = new_info_entry_in_frame(vbox, "Ticks");
1208 entry_set_int_value(entry, p->dus.ticks[0]);
1210 frame = gtk_frame_new("Write");
1211 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1212 vbox = gtk_hbox_new(TRUE, 3);
1213 gtk_container_add(GTK_CONTAINER(frame), vbox);
1214 entry = new_info_entry_in_frame(vbox, "IOs");
1215 entry_set_int_value(entry, p->dus.ios[1]);
1216 entry = new_info_entry_in_frame(vbox, "Merges");
1217 entry_set_int_value(entry, p->dus.merges[1]);
1218 entry = new_info_entry_in_frame(vbox, "Sectors");
1219 entry_set_int_value(entry, p->dus.sectors[1]);
1220 entry = new_info_entry_in_frame(vbox, "Ticks");
1221 entry_set_int_value(entry, p->dus.ticks[1]);
1223 frame = gtk_frame_new("Shared");
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, "IO ticks");
1228 entry_set_int_value(entry, p->dus.io_ticks);
1229 entry = new_info_entry_in_frame(vbox, "Time in queue");
1230 entry_set_int_value(entry, p->dus.time_in_queue);
1234 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1238 sprintf(tmp, "%3.2f%%", util);
1239 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1240 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1242 gtk_widget_show_all(gc->results_widget);
1244 gdk_threads_leave();
1247 extern int sum_stat_clients;
1248 extern struct thread_stat client_ts;
1249 extern struct group_run_stats client_gs;
1251 static int sum_stat_nr;
1253 static void gfio_thread_status_op(struct fio_client *client,
1254 struct fio_net_cmd *cmd)
1256 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1258 gfio_display_ts(client, &p->ts, &p->rs);
1260 if (sum_stat_clients == 1)
1263 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1264 sum_group_stats(&client_gs, &p->rs);
1266 client_ts.members++;
1267 client_ts.groupid = p->ts.groupid;
1269 if (++sum_stat_nr == sum_stat_clients) {
1270 strcpy(client_ts.name, "All clients");
1271 gfio_display_ts(client, &client_ts, &client_gs);
1275 static void gfio_group_stats_op(struct fio_client *client,
1276 struct fio_net_cmd *cmd)
1278 /* We're ignoring group stats for now */
1281 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1284 struct gfio_graphs *g = data;
1286 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1287 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1288 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1289 graph_set_position(g->bandwidth_graph, 0, 0);
1293 static void draw_graph(struct graph *g, cairo_t *cr)
1295 line_graph_draw(g, cr);
1299 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1300 gboolean keyboard_mode, GtkTooltip *tooltip,
1303 struct gfio_graphs *g = data;
1304 const char *text = NULL;
1306 if (graph_contains_xy(g->iops_graph, x, y))
1307 text = graph_find_tooltip(g->iops_graph, x, y);
1308 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1309 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1312 gtk_tooltip_set_text(tooltip, text);
1319 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1321 struct gfio_graphs *g = p;
1324 cr = gdk_cairo_create(w->window);
1326 if (graph_has_tooltips(g->iops_graph) ||
1327 graph_has_tooltips(g->bandwidth_graph)) {
1328 g_object_set(w, "has-tooltip", TRUE, NULL);
1329 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1332 cairo_set_source_rgb(cr, 0, 0, 0);
1333 draw_graph(g->iops_graph, cr);
1334 draw_graph(g->bandwidth_graph, cr);
1341 * Client specific ETA
1343 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1345 struct gfio_client *gc = client->client_data;
1346 struct gui_entry *ge = gc->ge;
1347 static int eta_good;
1354 gdk_threads_enter();
1359 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1360 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1361 eta_to_str(eta_str, je->eta_sec);
1364 sprintf(tmp, "%u", je->nr_running);
1365 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1366 sprintf(tmp, "%u", je->files_open);
1367 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1370 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1371 if (je->m_rate || je->t_rate) {
1374 mr = num2str(je->m_rate, 4, 0, i2p);
1375 tr = num2str(je->t_rate, 4, 0, i2p);
1376 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1377 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1380 } else if (je->m_iops || je->t_iops)
1381 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1383 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1384 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1385 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1386 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1389 if (je->eta_sec != INT_MAX && je->nr_running) {
1393 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1394 strcpy(output, "-.-% done");
1398 sprintf(output, "%3.1f%% done", perc);
1401 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1402 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1404 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1405 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1407 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1408 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1409 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1410 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1412 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1413 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1414 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1415 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1424 char *dst = output + strlen(output);
1426 sprintf(dst, " - %s", eta_str);
1429 gfio_update_thread_status(ge, output, perc);
1430 gdk_threads_leave();
1434 * Update ETA in main window for all clients
1436 static void gfio_update_all_eta(struct jobs_eta *je)
1438 struct gui *ui = &main_ui;
1439 static int eta_good;
1445 gdk_threads_enter();
1450 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1451 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1452 eta_to_str(eta_str, je->eta_sec);
1456 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1457 if (je->m_rate || je->t_rate) {
1460 mr = num2str(je->m_rate, 4, 0, i2p);
1461 tr = num2str(je->t_rate, 4, 0, i2p);
1462 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1463 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1466 } else if (je->m_iops || je->t_iops)
1467 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1469 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1470 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1471 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1472 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1475 entry_set_int_value(ui->eta.jobs, je->nr_running);
1477 if (je->eta_sec != INT_MAX && je->nr_running) {
1481 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1482 strcpy(output, "-.-% done");
1486 sprintf(output, "%3.1f%% done", perc);
1489 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1490 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1492 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1493 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1495 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1496 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1497 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1498 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1500 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1501 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1502 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1503 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1512 char *dst = output + strlen(output);
1514 sprintf(dst, " - %s", eta_str);
1517 gfio_update_thread_status_all(output, perc);
1518 gdk_threads_leave();
1521 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1523 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1524 struct gfio_client *gc = client->client_data;
1525 struct gui_entry *ge = gc->ge;
1526 const char *os, *arch;
1529 os = fio_get_os_string(probe->os);
1533 arch = fio_get_arch_string(probe->arch);
1538 client->name = strdup((char *) probe->hostname);
1540 gdk_threads_enter();
1542 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1543 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1544 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1545 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1546 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1548 gfio_set_state(ge, GE_STATE_CONNECTED);
1550 gdk_threads_leave();
1553 static void gfio_update_thread_status(struct gui_entry *ge,
1554 char *status_message, double perc)
1556 static char message[100];
1557 const char *m = message;
1559 strncpy(message, status_message, sizeof(message) - 1);
1560 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1561 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1562 gtk_widget_queue_draw(main_ui.window);
1565 static void gfio_update_thread_status_all(char *status_message, double perc)
1567 struct gui *ui = &main_ui;
1568 static char message[100];
1569 const char *m = message;
1571 strncpy(message, status_message, sizeof(message) - 1);
1572 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1573 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1574 gtk_widget_queue_draw(ui->window);
1577 static void gfio_quit_op(struct fio_client *client)
1579 struct gfio_client *gc = client->client_data;
1581 gdk_threads_enter();
1582 gfio_set_state(gc->ge, GE_STATE_NEW);
1583 gdk_threads_leave();
1586 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1588 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1589 struct gfio_client *gc = client->client_data;
1590 struct thread_options *o = &gc->o;
1591 struct gui_entry *ge = gc->ge;
1594 convert_thread_options_to_cpu(o, &p->top);
1596 gdk_threads_enter();
1598 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1600 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1601 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1603 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1604 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1606 sprintf(tmp, "%u", o->iodepth);
1607 multitext_add_entry(&ge->eta.iodepth, tmp);
1609 multitext_set_entry(&ge->eta.iotype, 0);
1610 multitext_set_entry(&ge->eta.ioengine, 0);
1611 multitext_set_entry(&ge->eta.iodepth, 0);
1615 gfio_set_state(ge, GE_STATE_JOB_SENT);
1617 gdk_threads_leave();
1620 static void gfio_client_timed_out(struct fio_client *client)
1622 struct gfio_client *gc = client->client_data;
1625 gdk_threads_enter();
1627 gfio_set_state(gc->ge, GE_STATE_NEW);
1628 clear_ge_ui_info(gc->ge);
1630 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1631 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1633 gdk_threads_leave();
1636 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1638 struct gfio_client *gc = client->client_data;
1640 gdk_threads_enter();
1642 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1645 entry_set_int_value(gc->err_entry, client->error);
1647 gdk_threads_leave();
1650 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1652 struct gfio_client *gc = client->client_data;
1654 gdk_threads_enter();
1655 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1656 gdk_threads_leave();
1659 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1661 struct gfio_client *gc = client->client_data;
1663 gdk_threads_enter();
1664 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1665 gdk_threads_leave();
1668 struct client_ops gfio_client_ops = {
1669 .text_op = gfio_text_op,
1670 .disk_util = gfio_disk_util_op,
1671 .thread_status = gfio_thread_status_op,
1672 .group_stats = gfio_group_stats_op,
1673 .jobs_eta = gfio_update_client_eta,
1674 .eta = gfio_update_all_eta,
1675 .probe = gfio_probe_op,
1676 .quit = gfio_quit_op,
1677 .add_job = gfio_add_job_op,
1678 .timed_out = gfio_client_timed_out,
1679 .stop = gfio_client_stop,
1680 .start = gfio_client_start,
1681 .job_start = gfio_client_job_start,
1682 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1683 .stay_connected = 1,
1686 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1687 __attribute__((unused)) gpointer data)
1692 static void *job_thread(void *arg)
1694 struct gui *ui = arg;
1696 ui->handler_running = 1;
1697 fio_handle_clients(&gfio_client_ops);
1698 ui->handler_running = 0;
1702 static int send_job_files(struct gui_entry *ge)
1704 struct gfio_client *gc = ge->client;
1707 for (i = 0; i < ge->nr_job_files; i++) {
1708 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1712 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1713 report_error(error);
1714 g_error_free(error);
1719 free(ge->job_files[i]);
1720 ge->job_files[i] = NULL;
1722 while (i < ge->nr_job_files) {
1723 free(ge->job_files[i]);
1724 ge->job_files[i] = NULL;
1728 ge->nr_job_files = 0;
1732 static void *server_thread(void *arg)
1735 gfio_server_running = 1;
1736 fio_start_server(NULL);
1737 gfio_server_running = 0;
1741 static void gfio_start_server(void)
1743 struct gui *ui = &main_ui;
1745 if (!gfio_server_running) {
1746 gfio_server_running = 1;
1747 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1748 pthread_detach(ui->server_t);
1752 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1755 struct gui_entry *ge = data;
1756 struct gfio_client *gc = ge->client;
1759 fio_start_client(gc->client);
1762 static void file_open(GtkWidget *w, gpointer data);
1764 static void connect_clicked(GtkWidget *widget, gpointer data)
1766 struct gui_entry *ge = data;
1767 struct gfio_client *gc = ge->client;
1769 if (ge->state == GE_STATE_NEW) {
1772 if (!ge->nr_job_files)
1773 file_open(widget, ge->ui);
1774 if (!ge->nr_job_files)
1777 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1778 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1779 ret = fio_client_connect(gc->client);
1781 if (!ge->ui->handler_running)
1782 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1783 gfio_set_state(ge, GE_STATE_CONNECTED);
1787 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1788 report_error(error);
1789 g_error_free(error);
1792 fio_client_terminate(gc->client);
1793 gfio_set_state(ge, GE_STATE_NEW);
1794 clear_ge_ui_info(ge);
1798 static void send_clicked(GtkWidget *widget, gpointer data)
1800 struct gui_entry *ge = data;
1802 if (send_job_files(ge)) {
1805 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);
1806 report_error(error);
1807 g_error_free(error);
1809 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1813 static GtkWidget *add_button(GtkWidget *buttonbox,
1814 struct button_spec *buttonspec, gpointer data)
1816 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1818 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1819 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1820 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1821 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1826 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1831 for (i = 0; i < nbuttons; i++)
1832 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1835 static void on_info_bar_response(GtkWidget *widget, gint response,
1838 struct gui *ui = &main_ui;
1840 if (response == GTK_RESPONSE_OK) {
1841 gtk_widget_destroy(widget);
1842 ui->error_info_bar = NULL;
1846 void report_error(GError *error)
1848 struct gui *ui = &main_ui;
1850 if (ui->error_info_bar == NULL) {
1851 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1854 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1855 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1858 ui->error_label = gtk_label_new(error->message);
1859 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1860 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1862 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1863 gtk_widget_show_all(ui->vbox);
1866 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1867 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1871 struct connection_widgets
1878 static void hostname_cb(GtkEntry *entry, gpointer data)
1880 struct connection_widgets *cw = data;
1881 int uses_net = 0, is_localhost = 0;
1886 * Check whether to display the 'auto start backend' box
1887 * or not. Show it if we are a localhost and using network,
1888 * or using a socket.
1890 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1891 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1896 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1897 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1898 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1899 !strcmp(text, "ip6-loopback"))
1903 if (!uses_net || is_localhost) {
1904 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1905 gtk_widget_set_sensitive(cw->button, 1);
1907 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1908 gtk_widget_set_sensitive(cw->button, 0);
1912 static int get_connection_details(char **host, int *port, int *type,
1915 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1916 struct connection_widgets cw;
1919 dialog = gtk_dialog_new_with_buttons("Connection details",
1920 GTK_WINDOW(main_ui.window),
1921 GTK_DIALOG_DESTROY_WITH_PARENT,
1922 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1923 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1925 frame = gtk_frame_new("Hostname / socket name");
1926 /* gtk_dialog_get_content_area() is 2.14 and newer */
1927 vbox = GTK_DIALOG(dialog)->vbox;
1928 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1930 box = gtk_vbox_new(FALSE, 6);
1931 gtk_container_add(GTK_CONTAINER(frame), box);
1933 hbox = gtk_hbox_new(TRUE, 10);
1934 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1935 cw.hentry = gtk_entry_new();
1936 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1937 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1939 frame = gtk_frame_new("Port");
1940 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1941 box = gtk_vbox_new(FALSE, 10);
1942 gtk_container_add(GTK_CONTAINER(frame), box);
1944 hbox = gtk_hbox_new(TRUE, 4);
1945 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1946 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1948 frame = gtk_frame_new("Type");
1949 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1950 box = gtk_vbox_new(FALSE, 10);
1951 gtk_container_add(GTK_CONTAINER(frame), box);
1953 hbox = gtk_hbox_new(TRUE, 4);
1954 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1956 cw.combo = gtk_combo_box_new_text();
1957 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1958 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1959 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1960 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1962 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1964 frame = gtk_frame_new("Options");
1965 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1966 box = gtk_vbox_new(FALSE, 10);
1967 gtk_container_add(GTK_CONTAINER(frame), box);
1969 hbox = gtk_hbox_new(TRUE, 4);
1970 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1972 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1973 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1974 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.");
1975 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1978 * Connect edit signal, so we can show/not-show the auto start button
1980 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1981 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1983 gtk_widget_show_all(dialog);
1985 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1986 gtk_widget_destroy(dialog);
1990 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1991 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1993 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1994 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1995 *type = Fio_client_ipv4;
1996 else if (!strncmp(typeentry, "IPv6", 4))
1997 *type = Fio_client_ipv6;
1999 *type = Fio_client_socket;
2002 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2004 gtk_widget_destroy(dialog);
2008 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2010 struct gfio_client *gc;
2012 gc = malloc(sizeof(*gc));
2013 memset(gc, 0, sizeof(*gc));
2015 gc->client = fio_get_client(client);
2019 client->client_data = gc;
2022 static GtkWidget *new_client_page(struct gui_entry *ge);
2024 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2026 struct gui_entry *ge;
2028 ge = malloc(sizeof(*ge));
2029 memset(ge, 0, sizeof(*ge));
2030 ge->state = GE_STATE_NEW;
2031 INIT_FLIST_HEAD(&ge->list);
2032 flist_add_tail(&ge->list, &ui->list);
2038 * FIXME: need more handling here
2040 static void ge_destroy(GtkWidget *w, gpointer data)
2042 struct gui_entry *ge = data;
2043 struct gfio_client *gc = ge->client;
2045 if (gc && gc->client) {
2046 if (ge->state >= GE_STATE_CONNECTED)
2047 fio_client_terminate(gc->client);
2049 fio_put_client(gc->client);
2052 flist_del(&ge->list);
2056 static struct gui_entry *get_new_ge_with_tab(const char *name)
2058 struct gui_entry *ge;
2060 ge = alloc_new_gui_entry(&main_ui);
2062 ge->vbox = new_client_page(ge);
2063 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
2065 ge->page_label = gtk_label_new(name);
2066 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2068 gtk_widget_show_all(main_ui.window);
2072 static void file_new(GtkWidget *w, gpointer data)
2074 struct gui *ui = (struct gui *) data;
2075 struct gui_entry *ge;
2077 ge = get_new_ge_with_tab("Untitled");
2078 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2082 * Return the 'ge' corresponding to the tab. If the active tab is the
2083 * main tab, open a new tab.
2085 static struct gui_entry *get_ge_from_page(gint cur_page)
2087 struct flist_head *entry;
2088 struct gui_entry *ge;
2091 return get_new_ge_with_tab("Untitled");
2093 flist_for_each(entry, &main_ui.list) {
2094 ge = flist_entry(entry, struct gui_entry, list);
2095 if (ge->page_num == cur_page)
2102 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2107 * Main tab is tab 0, so any current page other than 0 holds
2110 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2112 return get_ge_from_page(cur_page);
2117 static void file_close(GtkWidget *w, gpointer data)
2119 struct gui *ui = (struct gui *) data;
2120 struct gui_entry *ge;
2123 * Can't close the main tab
2125 ge = get_ge_from_cur_tab(ui);
2127 gtk_widget_destroy(ge->vbox);
2131 if (!flist_empty(&ui->list)) {
2132 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2139 static void file_open(GtkWidget *w, gpointer data)
2141 struct gui *ui = data;
2143 GSList *filenames, *fn_glist;
2144 GtkFileFilter *filter;
2146 int port, type, server_start;
2147 struct gui_entry *ge;
2151 * Creates new tab if current tab is the main window, or the
2152 * current tab already has a client.
2154 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2155 ge = get_ge_from_page(cur_page);
2157 ge = get_new_ge_with_tab("Untitled");
2159 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2161 dialog = gtk_file_chooser_dialog_new("Open File",
2162 GTK_WINDOW(ui->window),
2163 GTK_FILE_CHOOSER_ACTION_OPEN,
2164 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2165 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2167 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2169 filter = gtk_file_filter_new();
2170 gtk_file_filter_add_pattern(filter, "*.fio");
2171 gtk_file_filter_add_pattern(filter, "*.job");
2172 gtk_file_filter_add_pattern(filter, "*.ini");
2173 gtk_file_filter_add_mime_type(filter, "text/fio");
2174 gtk_file_filter_set_name(filter, "Fio job file");
2175 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2177 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2178 gtk_widget_destroy(dialog);
2182 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2184 gtk_widget_destroy(dialog);
2186 if (get_connection_details(&host, &port, &type, &server_start))
2189 filenames = fn_glist;
2190 while (filenames != NULL) {
2191 struct fio_client *client;
2193 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2194 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2197 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2201 error = g_error_new(g_quark_from_string("fio"), 1,
2202 "Failed to add client %s", host);
2203 report_error(error);
2204 g_error_free(error);
2206 gfio_client_added(ge, client);
2208 g_free(filenames->data);
2209 filenames = g_slist_next(filenames);
2214 gfio_start_server();
2216 g_slist_free(fn_glist);
2219 static void file_save(GtkWidget *w, gpointer data)
2221 struct gui *ui = data;
2224 dialog = gtk_file_chooser_dialog_new("Save File",
2225 GTK_WINDOW(ui->window),
2226 GTK_FILE_CHOOSER_ACTION_SAVE,
2227 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2228 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2231 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2232 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2234 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2237 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2238 // save_job_file(filename);
2241 gtk_widget_destroy(dialog);
2244 static void view_log_destroy(GtkWidget *w, gpointer data)
2246 struct gui *ui = (struct gui *) data;
2248 gtk_widget_ref(ui->log_tree);
2249 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2250 gtk_widget_destroy(w);
2251 ui->log_view = NULL;
2254 static void view_log(GtkWidget *w, gpointer data)
2256 GtkWidget *win, *scroll, *vbox, *box;
2257 struct gui *ui = (struct gui *) data;
2262 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2263 gtk_window_set_title(GTK_WINDOW(win), "Log");
2264 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2266 scroll = gtk_scrolled_window_new(NULL, NULL);
2268 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2270 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2272 box = gtk_hbox_new(TRUE, 0);
2273 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2274 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2275 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2277 vbox = gtk_vbox_new(TRUE, 5);
2278 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2280 gtk_container_add(GTK_CONTAINER(win), vbox);
2281 gtk_widget_show_all(win);
2284 static void connect_job_entry(GtkWidget *w, gpointer data)
2286 struct gui *ui = (struct gui *) data;
2287 struct gui_entry *ge;
2289 ge = get_ge_from_cur_tab(ui);
2291 connect_clicked(w, ge);
2294 static void send_job_entry(GtkWidget *w, gpointer data)
2296 struct gui *ui = (struct gui *) data;
2297 struct gui_entry *ge;
2299 ge = get_ge_from_cur_tab(ui);
2301 send_clicked(w, ge);
2305 static void edit_job_entry(GtkWidget *w, gpointer data)
2309 static void start_job_entry(GtkWidget *w, gpointer data)
2311 struct gui *ui = (struct gui *) data;
2312 struct gui_entry *ge;
2314 ge = get_ge_from_cur_tab(ui);
2316 start_job_clicked(w, ge);
2319 static void __update_graph_limits(struct gfio_graphs *g)
2321 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2322 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2325 static void update_graph_limits(void)
2327 struct flist_head *entry;
2328 struct gui_entry *ge;
2330 __update_graph_limits(&main_ui.graphs);
2332 flist_for_each(entry, &main_ui.list) {
2333 ge = flist_entry(entry, struct gui_entry, list);
2334 __update_graph_limits(&ge->graphs);
2338 static void preferences(GtkWidget *w, gpointer data)
2340 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2341 GtkWidget *hbox, *spin, *entry, *spin_int;
2344 dialog = gtk_dialog_new_with_buttons("Preferences",
2345 GTK_WINDOW(main_ui.window),
2346 GTK_DIALOG_DESTROY_WITH_PARENT,
2347 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2348 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2351 frame = gtk_frame_new("Graphing");
2352 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2353 vbox = gtk_vbox_new(FALSE, 6);
2354 gtk_container_add(GTK_CONTAINER(frame), vbox);
2356 hbox = gtk_hbox_new(FALSE, 5);
2357 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2358 entry = gtk_label_new("Font face to use for graph labels");
2359 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2361 font = gtk_font_button_new();
2362 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2364 box = gtk_vbox_new(FALSE, 6);
2365 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2367 hbox = gtk_hbox_new(FALSE, 5);
2368 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2369 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2370 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2372 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2374 box = gtk_vbox_new(FALSE, 6);
2375 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2377 hbox = gtk_hbox_new(FALSE, 5);
2378 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2379 entry = gtk_label_new("Client ETA request interval (msec)");
2380 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2382 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2383 frame = gtk_frame_new("Debug logging");
2384 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2385 vbox = gtk_vbox_new(FALSE, 6);
2386 gtk_container_add(GTK_CONTAINER(frame), vbox);
2388 box = gtk_hbox_new(FALSE, 6);
2389 gtk_container_add(GTK_CONTAINER(vbox), box);
2391 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2393 for (i = 0; i < FD_DEBUG_MAX; i++) {
2395 box = gtk_hbox_new(FALSE, 6);
2396 gtk_container_add(GTK_CONTAINER(vbox), box);
2400 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2401 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2402 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2405 gtk_widget_show_all(dialog);
2407 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2408 gtk_widget_destroy(dialog);
2412 for (i = 0; i < FD_DEBUG_MAX; i++) {
2415 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2417 fio_debug |= (1UL << i);
2420 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2421 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2422 update_graph_limits();
2423 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2425 gtk_widget_destroy(dialog);
2428 static void about_dialog(GtkWidget *w, gpointer data)
2430 const char *authors[] = {
2431 "Jens Axboe <axboe@kernel.dk>",
2432 "Stephen Carmeron <stephenmcameron@gmail.com>",
2435 const char *license[] = {
2436 "Fio is free software; you can redistribute it and/or modify "
2437 "it under the terms of the GNU General Public License as published by "
2438 "the Free Software Foundation; either version 2 of the License, or "
2439 "(at your option) any later version.\n",
2440 "Fio is distributed in the hope that it will be useful, "
2441 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2442 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2443 "GNU General Public License for more details.\n",
2444 "You should have received a copy of the GNU General Public License "
2445 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2446 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2448 char *license_trans;
2450 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2451 license[2], "\n", NULL);
2453 gtk_show_about_dialog(NULL,
2454 "program-name", "gfio",
2455 "comments", "Gtk2 UI for fio",
2456 "license", license_trans,
2457 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2459 "version", fio_version_string,
2460 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2461 "logo-icon-name", "fio",
2463 "wrap-license", TRUE,
2466 g_free(license_trans);
2469 static GtkActionEntry menu_items[] = {
2470 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2471 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2472 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2473 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2474 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2475 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2476 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2477 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2478 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2479 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2480 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2481 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2482 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2483 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2484 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2485 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2487 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2489 static const gchar *ui_string = " \
2491 <menubar name=\"MainMenu\"> \
2492 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2493 <menuitem name=\"New\" action=\"NewFile\" /> \
2494 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2495 <separator name=\"Separator1\"/> \
2496 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2497 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2498 <separator name=\"Separator2\"/> \
2499 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2500 <separator name=\"Separator3\"/> \
2501 <placeholder name=\"FileRecentFiles\"/> \
2502 <separator name=\"Separator4\"/> \
2503 <menuitem name=\"Quit\" action=\"Quit\" /> \
2505 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2506 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2507 <separator name=\"Separator5\"/> \
2508 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2509 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2510 <separator name=\"Separator6\"/> \
2511 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2513 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2514 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2516 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2517 <menuitem name=\"About\" action=\"About\" /> \
2523 static void set_job_menu_visible(struct gui *ui, int visible)
2527 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2528 gtk_widget_set_sensitive(job, visible);
2531 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2534 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2537 action_group = gtk_action_group_new("Menu");
2538 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2540 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2541 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2543 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2545 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2548 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2549 GtkWidget *vbox, GtkUIManager *ui_manager)
2551 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2554 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2556 struct gui_entry *ge = (struct gui_entry *) data;
2559 index = gtk_combo_box_get_active(box);
2561 multitext_set_entry(&ge->eta.iotype, index);
2562 multitext_set_entry(&ge->eta.ioengine, index);
2563 multitext_set_entry(&ge->eta.iodepth, index);
2566 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2568 struct gui_entry *ge = (struct gui_entry *) data;
2570 multitext_free(&ge->eta.iotype);
2571 multitext_free(&ge->eta.ioengine);
2572 multitext_free(&ge->eta.iodepth);
2575 static GtkWidget *new_client_page(struct gui_entry *ge)
2577 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2578 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2581 main_vbox = gtk_vbox_new(FALSE, 3);
2583 top_align = gtk_alignment_new(0, 0, 1, 0);
2584 top_vbox = gtk_vbox_new(FALSE, 3);
2585 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2586 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2588 probe = gtk_frame_new("Job");
2589 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2590 probe_frame = gtk_vbox_new(FALSE, 3);
2591 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2593 probe_box = gtk_hbox_new(FALSE, 3);
2594 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2595 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2596 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2597 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2598 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2600 probe_box = gtk_hbox_new(FALSE, 3);
2601 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2603 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2604 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2605 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2606 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2607 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2608 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2609 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2610 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2612 probe_box = gtk_hbox_new(FALSE, 3);
2613 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2614 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2615 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2616 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2617 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2620 * Only add this if we have a commit rate
2623 probe_box = gtk_hbox_new(FALSE, 3);
2624 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2626 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2627 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2629 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2630 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2634 * Set up a drawing area and IOPS and bandwidth graphs
2636 gdk_color_parse("white", &white);
2637 ge->graphs.drawing_area = gtk_drawing_area_new();
2638 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2639 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2640 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2641 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2642 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2643 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2644 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2645 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2646 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2647 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2648 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2649 ge->graphs.drawing_area);
2650 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2652 setup_graphs(&ge->graphs);
2655 * Set up alignments for widgets at the bottom of ui,
2656 * align bottom left, expand horizontally but not vertically
2658 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2659 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2660 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2661 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2663 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2666 * Set up thread status progress bar
2668 ge->thread_status_pb = gtk_progress_bar_new();
2669 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2670 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2671 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2677 static GtkWidget *new_main_page(struct gui *ui)
2679 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2680 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2683 main_vbox = gtk_vbox_new(FALSE, 3);
2686 * Set up alignments for widgets at the top of ui,
2687 * align top left, expand horizontally but not vertically
2689 top_align = gtk_alignment_new(0, 0, 1, 0);
2690 top_vbox = gtk_vbox_new(FALSE, 0);
2691 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2692 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2694 probe = gtk_frame_new("Run statistics");
2695 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2696 probe_frame = gtk_vbox_new(FALSE, 3);
2697 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2699 probe_box = gtk_hbox_new(FALSE, 3);
2700 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2701 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2702 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2703 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2704 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2705 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2708 * Only add this if we have a commit rate
2711 probe_box = gtk_hbox_new(FALSE, 3);
2712 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2714 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2715 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2717 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2718 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2722 * Set up a drawing area and IOPS and bandwidth graphs
2724 gdk_color_parse("white", &white);
2725 ui->graphs.drawing_area = gtk_drawing_area_new();
2726 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2727 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2728 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2729 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2730 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2731 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2732 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2733 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2734 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2735 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2736 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2737 ui->graphs.drawing_area);
2738 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
2741 setup_graphs(&ui->graphs);
2744 * Set up alignments for widgets at the bottom of ui,
2745 * align bottom left, expand horizontally but not vertically
2747 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2748 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2749 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2750 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2753 * Set up thread status progress bar
2755 ui->thread_status_pb = gtk_progress_bar_new();
2756 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2757 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2758 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2763 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2764 guint page, gpointer data)
2767 struct gui *ui = (struct gui *) data;
2768 struct gui_entry *ge;
2771 set_job_menu_visible(ui, 0);
2775 set_job_menu_visible(ui, 1);
2776 ge = get_ge_from_page(page);
2778 update_button_states(ui, ge);
2783 static void init_ui(int *argc, char **argv[], struct gui *ui)
2785 GtkSettings *settings;
2788 /* Magical g*thread incantation, you just need this thread stuff.
2789 * Without it, the update that happens in gfio_update_thread_status
2790 * doesn't really happen in a timely fashion, you need expose events
2792 if (!g_thread_supported())
2793 g_thread_init(NULL);
2796 gtk_init(argc, argv);
2797 settings = gtk_settings_get_default();
2798 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2801 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2802 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2803 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2805 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2806 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2808 ui->vbox = gtk_vbox_new(FALSE, 0);
2809 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2811 ui->uimanager = gtk_ui_manager_new();
2812 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
2813 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2815 ui->notebook = gtk_notebook_new();
2816 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2817 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2818 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2819 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2821 vbox = new_main_page(ui);
2823 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2825 gfio_ui_setup_log(ui);
2827 gtk_widget_show_all(ui->window);
2830 int main(int argc, char *argv[], char *envp[])
2832 if (initialize_fio(envp))
2834 if (fio_init_options())
2837 memset(&main_ui, 0, sizeof(main_ui));
2838 INIT_FLIST_HEAD(&main_ui.list);
2840 init_ui(&argc, &argv, &main_ui);
2842 gdk_threads_enter();
2844 gdk_threads_leave();