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;
113 GtkWidget *bottomalign;
114 GtkWidget *thread_status_pb;
115 GtkWidget *buttonbox;
116 GtkWidget *scrolled_window;
118 GtkWidget *error_info_bar;
119 GtkWidget *error_label;
120 GtkListStore *log_model;
123 struct gfio_graphs graphs;
124 struct probe_widget probe;
125 struct eta_widget eta;
131 struct flist_head list;
138 GE_STATE_JOB_STARTED,
139 GE_STATE_JOB_RUNNING,
147 struct flist_head list;
153 GtkWidget *bottomalign;
154 GtkWidget *job_notebook;
155 GtkWidget *thread_status_pb;
156 GtkWidget *buttonbox;
157 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
158 GtkWidget *scrolled_window;
160 GtkWidget *error_info_bar;
161 GtkWidget *error_label;
162 GtkWidget *results_notebook;
163 GtkWidget *results_window;
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 void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
195 switch (power_of_ten) {
196 case 9: graph_y_title(g, "Billions of IOs / sec");
198 case 6: graph_y_title(g, "Millions of IOs / sec");
200 case 3: graph_y_title(g, "Thousands of IOs / sec");
203 default: graph_y_title(g, "IOs / sec");
208 static struct graph *setup_iops_graph(void)
212 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
213 graph_title(g, "IOPS");
214 graph_x_title(g, "Time (secs)");
215 graph_y_title(g, "IOs / sec");
216 graph_add_label(g, "Read IOPS");
217 graph_add_label(g, "Write IOPS");
218 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
219 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
220 line_graph_set_data_count_limit(g, gfio_graph_limit);
221 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
222 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
226 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
228 switch (power_of_ten) {
229 case 9: graph_y_title(g, "Petabytes / sec");
231 case 6: graph_y_title(g, "Gigabytes / sec");
233 case 3: graph_y_title(g, "Megabytes / sec");
236 default: graph_y_title(g, "Kilobytes / sec");
241 static struct graph *setup_bandwidth_graph(void)
245 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
246 graph_title(g, "Bandwidth");
247 graph_x_title(g, "Time (secs)");
248 graph_y_title(g, "Kbytes / sec");
249 graph_add_label(g, "Read Bandwidth");
250 graph_add_label(g, "Write Bandwidth");
251 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
252 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
253 line_graph_set_data_count_limit(g, 100);
254 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
255 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
260 static void setup_graphs(struct gfio_graphs *g)
262 g->iops_graph = setup_iops_graph();
263 g->bandwidth_graph = setup_bandwidth_graph();
266 static void multitext_add_entry(struct multitext_widget *mt, const char *text)
268 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
269 mt->text[mt->max_text] = strdup(text);
273 static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
275 if (index >= mt->max_text)
277 if (!mt->text || !mt->text[index])
280 mt->cur_text = index;
281 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
284 static void multitext_update_entry(struct multitext_widget *mt,
285 unsigned int index, const char *text)
291 free(mt->text[index]);
293 mt->text[index] = strdup(text);
294 if (mt->cur_text == index)
295 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
298 static void multitext_free(struct multitext_widget *mt)
302 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
304 for (i = 0; i < mt->max_text; i++) {
314 static void clear_ge_ui_info(struct gui_entry *ge)
316 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
317 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
318 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
319 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
321 /* should we empty it... */
322 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
324 multitext_update_entry(&ge->eta.iotype, 0, "");
325 multitext_update_entry(&ge->eta.ioengine, 0, "");
326 multitext_update_entry(&ge->eta.iodepth, 0, "");
327 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
328 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
329 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
330 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
331 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
332 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
335 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
337 GtkWidget *entry, *frame;
339 frame = gtk_frame_new(label);
340 entry = gtk_combo_box_new_text();
341 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
342 gtk_container_add(GTK_CONTAINER(frame), entry);
347 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
349 GtkWidget *entry, *frame;
351 frame = gtk_frame_new(label);
352 entry = gtk_entry_new();
353 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
354 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
355 gtk_container_add(GTK_CONTAINER(frame), entry);
360 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
362 GtkWidget *label_widget;
365 frame = gtk_frame_new(label);
366 label_widget = gtk_label_new(NULL);
367 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
368 gtk_container_add(GTK_CONTAINER(frame), label_widget);
373 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
375 GtkWidget *button, *box;
377 box = gtk_hbox_new(FALSE, 3);
378 gtk_container_add(GTK_CONTAINER(hbox), box);
380 button = gtk_spin_button_new_with_range(min, max, 1.0);
381 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
383 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
384 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
389 static void label_set_int_value(GtkWidget *entry, unsigned int val)
393 sprintf(tmp, "%u", val);
394 gtk_label_set_text(GTK_LABEL(entry), tmp);
397 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
401 sprintf(tmp, "%u", val);
402 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
405 static void show_info_dialog(struct gui *ui, const char *title,
408 GtkWidget *dialog, *content, *label;
410 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
411 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
412 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
414 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
415 label = gtk_label_new(message);
416 gtk_container_add(GTK_CONTAINER(content), label);
417 gtk_widget_show_all(dialog);
418 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
419 gtk_dialog_run(GTK_DIALOG(dialog));
420 gtk_widget_destroy(dialog);
424 * Update sensitivity of job buttons and job menu items, based on the
425 * state of the client.
427 static void update_button_states(struct gui *ui, struct gui_entry *ge)
429 unsigned int connect_state, send_state, start_state, edit_state;
430 const char *connect_str = NULL;
437 sprintf(tmp, "Bad client state: %u\n", ge->state);
438 show_info_dialog(ui, "Error", tmp);
439 /* fall through to new state */
445 connect_str = "Connect";
449 case GE_STATE_CONNECTED:
452 connect_str = "Disconnect";
456 case GE_STATE_JOB_SENT:
459 connect_str = "Disconnect";
463 case GE_STATE_JOB_STARTED:
466 connect_str = "Disconnect";
470 case GE_STATE_JOB_RUNNING:
473 connect_str = "Disconnect";
477 case GE_STATE_JOB_DONE:
480 connect_str = "Connect";
486 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
487 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
488 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
489 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
492 * So the below doesn't work at all, how to set those menu items
495 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Connect");
497 gtk_widget_set_sensitive(w, connect_state);
499 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Edit job");
501 gtk_widget_set_sensitive(w, edit_state);
503 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Send job");
505 gtk_widget_set_sensitive(w, send_state);
507 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Start job");
509 gtk_widget_set_sensitive(w, start_state);
512 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
515 update_button_states(ge->ui, ge);
519 #define ALIGN_RIGHT 2
523 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
525 GtkCellRenderer *renderer;
526 GtkTreeViewColumn *col;
527 double xalign = 0.0; /* left as default */
528 PangoAlignment align;
531 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
532 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
534 visible = !(flags & INVISIBLE);
536 renderer = gtk_cell_renderer_text_new();
537 col = gtk_tree_view_column_new();
539 gtk_tree_view_column_set_title(col, title);
540 if (!(flags & UNSORTABLE))
541 gtk_tree_view_column_set_sort_column_id(col, index);
542 gtk_tree_view_column_set_resizable(col, TRUE);
543 gtk_tree_view_column_pack_start(col, renderer, TRUE);
544 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
545 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
547 case PANGO_ALIGN_LEFT:
550 case PANGO_ALIGN_CENTER:
553 case PANGO_ALIGN_RIGHT:
557 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
558 gtk_tree_view_column_set_visible(col, visible);
559 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
563 static void gfio_ui_setup_log(struct gui *ui)
565 GtkTreeSelection *selection;
567 GtkWidget *tree_view;
569 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
571 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
572 gtk_widget_set_can_focus(tree_view, FALSE);
574 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
575 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
576 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
577 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
579 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
580 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
581 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
582 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
584 ui->log_model = model;
585 ui->log_tree = tree_view;
588 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
594 GType types[FIO_IO_U_LIST_MAX_LEN];
595 GtkWidget *tree_view;
596 GtkTreeSelection *selection;
601 for (i = 0; i < len; i++)
602 types[i] = G_TYPE_INT;
604 model = gtk_list_store_newv(len, types);
606 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
607 gtk_widget_set_can_focus(tree_view, FALSE);
609 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
610 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
612 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
613 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
615 for (i = 0; i < len; i++) {
618 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
619 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
622 gtk_list_store_append(model, &iter);
624 for (i = 0; i < len; i++) {
626 ovals[i] = (ovals[i] + 999) / 1000;
627 gtk_list_store_set(model, &iter, i, ovals[i], -1);
633 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
636 unsigned int *io_u_plat = ts->io_u_plat[ddir];
637 unsigned long nr = ts->clat_stat[ddir].samples;
638 fio_fp64_t *plist = ts->percentile_list;
639 unsigned int *ovals, len, minv, maxv, scale_down;
641 GtkWidget *tree_view, *frame, *hbox;
644 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
649 * We default to usecs, but if the value range is such that we
650 * should scale down to msecs, do that.
652 if (minv > 2000 && maxv > 99999) {
660 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
662 sprintf(tmp, "Completion percentiles (%s)", base);
663 frame = gtk_frame_new(tmp);
664 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
666 hbox = gtk_hbox_new(FALSE, 3);
667 gtk_container_add(GTK_CONTAINER(frame), hbox);
669 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
675 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
676 unsigned long max, double mean, double dev)
678 const char *base = "(usec)";
679 GtkWidget *hbox, *label, *frame;
683 if (!usec_to_msec(&min, &max, &mean, &dev))
686 minp = num2str(min, 6, 1, 0);
687 maxp = num2str(max, 6, 1, 0);
689 sprintf(tmp, "%s %s", name, base);
690 frame = gtk_frame_new(tmp);
691 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
693 hbox = gtk_hbox_new(FALSE, 3);
694 gtk_container_add(GTK_CONTAINER(frame), hbox);
696 label = new_info_label_in_frame(hbox, "Minimum");
697 gtk_label_set_text(GTK_LABEL(label), minp);
698 label = new_info_label_in_frame(hbox, "Maximum");
699 gtk_label_set_text(GTK_LABEL(label), maxp);
700 label = new_info_label_in_frame(hbox, "Average");
701 sprintf(tmp, "%5.02f", mean);
702 gtk_label_set_text(GTK_LABEL(label), tmp);
703 label = new_info_label_in_frame(hbox, "Standard deviation");
704 sprintf(tmp, "%5.02f", dev);
705 gtk_label_set_text(GTK_LABEL(label), tmp);
716 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
717 struct thread_stat *ts, int ddir)
719 const char *ddir_label[2] = { "Read", "Write" };
720 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
721 unsigned long min[3], max[3], runt;
722 unsigned long long bw, iops;
723 unsigned int flags = 0;
724 double mean[3], dev[3];
725 char *io_p, *bw_p, *iops_p;
728 if (!ts->runtime[ddir])
731 i2p = is_power_of_2(rs->kb_base);
732 runt = ts->runtime[ddir];
734 bw = (1000 * ts->io_bytes[ddir]) / runt;
735 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
736 bw_p = num2str(bw, 6, 1, i2p);
738 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
739 iops_p = num2str(iops, 6, 1, 0);
741 box = gtk_hbox_new(FALSE, 3);
742 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
744 frame = gtk_frame_new(ddir_label[ddir]);
745 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
747 main_vbox = gtk_vbox_new(FALSE, 3);
748 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
750 box = gtk_hbox_new(FALSE, 3);
751 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
753 label = new_info_label_in_frame(box, "IO");
754 gtk_label_set_text(GTK_LABEL(label), io_p);
755 label = new_info_label_in_frame(box, "Bandwidth");
756 gtk_label_set_text(GTK_LABEL(label), bw_p);
757 label = new_info_label_in_frame(box, "IOPS");
758 gtk_label_set_text(GTK_LABEL(label), iops_p);
759 label = new_info_label_in_frame(box, "Runtime (msec)");
760 label_set_int_value(label, ts->runtime[ddir]);
762 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
763 double p_of_agg = 100.0;
764 const char *bw_str = "KB";
768 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
769 if (p_of_agg > 100.0)
773 if (mean[0] > 999999.9) {
781 sprintf(tmp, "Bandwidth (%s)", bw_str);
782 frame = gtk_frame_new(tmp);
783 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
785 box = gtk_hbox_new(FALSE, 3);
786 gtk_container_add(GTK_CONTAINER(frame), box);
788 label = new_info_label_in_frame(box, "Minimum");
789 label_set_int_value(label, min[0]);
790 label = new_info_label_in_frame(box, "Maximum");
791 label_set_int_value(label, max[0]);
792 label = new_info_label_in_frame(box, "Percentage of jobs");
793 sprintf(tmp, "%3.2f%%", p_of_agg);
794 gtk_label_set_text(GTK_LABEL(label), tmp);
795 label = new_info_label_in_frame(box, "Average");
796 sprintf(tmp, "%5.02f", mean[0]);
797 gtk_label_set_text(GTK_LABEL(label), tmp);
798 label = new_info_label_in_frame(box, "Standard deviation");
799 sprintf(tmp, "%5.02f", dev[0]);
800 gtk_label_set_text(GTK_LABEL(label), tmp);
803 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
805 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
807 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
811 frame = gtk_frame_new("Latency");
812 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
814 vbox = gtk_vbox_new(FALSE, 3);
815 gtk_container_add(GTK_CONTAINER(frame), vbox);
817 if (flags & GFIO_SLAT)
818 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
819 if (flags & GFIO_CLAT)
820 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
821 if (flags & GFIO_LAT)
822 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
825 if (ts->clat_percentiles)
826 gfio_show_clat_percentiles(main_vbox, ts, ddir);
834 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
837 GtkWidget *tree_view;
838 GtkTreeSelection *selection;
845 * Check if all are empty, in which case don't bother
847 for (i = 0, skipped = 0; i < num; i++)
854 types = malloc(num * sizeof(GType));
856 for (i = 0; i < num; i++)
857 types[i] = G_TYPE_STRING;
859 model = gtk_list_store_newv(num, types);
863 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
864 gtk_widget_set_can_focus(tree_view, FALSE);
866 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
867 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
869 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
870 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
872 for (i = 0; i < num; i++)
873 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
875 gtk_list_store_append(model, &iter);
877 for (i = 0; i < num; i++) {
881 sprintf(fbuf, "0.00");
883 sprintf(fbuf, "%3.2f%%", lat[i]);
885 gtk_list_store_set(model, &iter, i, fbuf, -1);
891 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
893 GtkWidget *box, *frame, *tree_view;
894 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
895 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
896 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
897 "250", "500", "750", "1000", };
898 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
899 "250", "500", "750", "1000", "2000",
902 stat_calc_lat_u(ts, io_u_lat_u);
903 stat_calc_lat_m(ts, io_u_lat_m);
905 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
907 frame = gtk_frame_new("Latency buckets (usec)");
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);
912 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
915 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
917 frame = gtk_frame_new("Latency buckets (msec)");
918 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
920 box = gtk_hbox_new(FALSE, 3);
921 gtk_container_add(GTK_CONTAINER(frame), box);
922 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
926 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
928 GtkWidget *box, *frame, *entry;
929 double usr_cpu, sys_cpu;
930 unsigned long runtime;
933 runtime = ts->total_run_time;
935 double runt = (double) runtime;
937 usr_cpu = (double) ts->usr_time * 100 / runt;
938 sys_cpu = (double) ts->sys_time * 100 / runt;
944 frame = gtk_frame_new("OS resources");
945 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
947 box = gtk_hbox_new(FALSE, 3);
948 gtk_container_add(GTK_CONTAINER(frame), box);
950 entry = new_info_entry_in_frame(box, "User CPU");
951 sprintf(tmp, "%3.2f%%", usr_cpu);
952 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
953 entry = new_info_entry_in_frame(box, "System CPU");
954 sprintf(tmp, "%3.2f%%", sys_cpu);
955 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
956 entry = new_info_entry_in_frame(box, "Context switches");
957 entry_set_int_value(entry, ts->ctx);
958 entry = new_info_entry_in_frame(box, "Major faults");
959 entry_set_int_value(entry, ts->majf);
960 entry = new_info_entry_in_frame(box, "Minor faults");
961 entry_set_int_value(entry, ts->minf);
963 static void gfio_add_sc_depths_tree(GtkListStore *model,
964 struct thread_stat *ts, unsigned int len,
967 double io_u_dist[FIO_IO_U_MAP_NR];
969 /* Bits 0, and 3-8 */
970 const int add_mask = 0x1f9;
974 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
976 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
978 gtk_list_store_append(model, &iter);
980 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
982 for (i = 1, j = 0; i < len; i++) {
985 if (!(add_mask & (1UL << (i - 1))))
986 sprintf(fbuf, "0.0%%");
988 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
992 gtk_list_store_set(model, &iter, i, fbuf, -1);
997 static void gfio_add_total_depths_tree(GtkListStore *model,
998 struct thread_stat *ts, unsigned int len)
1000 double io_u_dist[FIO_IO_U_MAP_NR];
1002 /* Bits 1-6, and 8 */
1003 const int add_mask = 0x17e;
1006 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1008 gtk_list_store_append(model, &iter);
1010 gtk_list_store_set(model, &iter, 0, "Total", -1);
1012 for (i = 1, j = 0; i < len; i++) {
1015 if (!(add_mask & (1UL << (i - 1))))
1016 sprintf(fbuf, "0.0%%");
1018 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1022 gtk_list_store_set(model, &iter, i, fbuf, -1);
1027 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1029 GtkWidget *frame, *box, *tree_view;
1030 GtkTreeSelection *selection;
1031 GtkListStore *model;
1032 GType types[FIO_IO_U_MAP_NR + 1];
1034 #define NR_LABELS 10
1035 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1037 frame = gtk_frame_new("IO depths");
1038 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1040 box = gtk_hbox_new(FALSE, 3);
1041 gtk_container_add(GTK_CONTAINER(frame), box);
1043 for (i = 0; i < NR_LABELS; i++)
1044 types[i] = G_TYPE_STRING;
1046 model = gtk_list_store_newv(NR_LABELS, types);
1048 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1049 gtk_widget_set_can_focus(tree_view, FALSE);
1051 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1052 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1054 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1055 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1057 for (i = 0; i < NR_LABELS; i++)
1058 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1060 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1061 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1062 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1064 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1067 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1069 struct gui_entry *ge = (struct gui_entry *) data;
1071 gtk_widget_destroy(w);
1072 ge->results_window = NULL;
1073 ge->results_notebook = NULL;
1077 static GtkWidget *get_results_window(struct gui_entry *ge)
1079 GtkWidget *win, *notebook;
1081 if (ge->results_window)
1082 return ge->results_notebook;
1084 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1085 gtk_window_set_title(GTK_WINDOW(win), "Results");
1086 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1087 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1088 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1090 notebook = gtk_notebook_new();
1091 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1092 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1093 gtk_container_add(GTK_CONTAINER(win), notebook);
1095 ge->results_window = win;
1096 ge->results_notebook = notebook;
1097 return ge->results_notebook;
1100 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1101 struct group_run_stats *rs)
1103 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
1104 struct gfio_client *gc = client->client_data;
1106 gdk_threads_enter();
1108 res_win = get_results_window(gc->ge);
1110 scroll = gtk_scrolled_window_new(NULL, NULL);
1111 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1112 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1114 vbox = gtk_vbox_new(FALSE, 3);
1116 box = gtk_hbox_new(FALSE, 0);
1117 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1119 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1121 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1123 gc->results_widget = vbox;
1125 entry = new_info_entry_in_frame(box, "Name");
1126 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1127 if (strlen(ts->description)) {
1128 entry = new_info_entry_in_frame(box, "Description");
1129 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1131 entry = new_info_entry_in_frame(box, "Group ID");
1132 entry_set_int_value(entry, ts->groupid);
1133 entry = new_info_entry_in_frame(box, "Jobs");
1134 entry_set_int_value(entry, ts->members);
1135 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1136 entry_set_int_value(entry, ts->error);
1137 entry = new_info_entry_in_frame(box, "PID");
1138 entry_set_int_value(entry, ts->pid);
1140 if (ts->io_bytes[DDIR_READ])
1141 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1142 if (ts->io_bytes[DDIR_WRITE])
1143 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1145 gfio_show_latency_buckets(vbox, ts);
1146 gfio_show_cpu_usage(vbox, ts);
1147 gfio_show_io_depths(vbox, ts);
1149 gtk_widget_show_all(gc->ge->results_window);
1150 gdk_threads_leave();
1153 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1155 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1156 struct gui *ui = &main_ui;
1160 char tmp[64], timebuf[80];
1163 tm = localtime(&sec);
1164 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1165 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1167 gdk_threads_enter();
1169 gtk_list_store_append(ui->log_model, &iter);
1170 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1171 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1172 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1173 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1175 if (p->level == FIO_LOG_ERR)
1176 view_log(NULL, (gpointer) ui);
1178 gdk_threads_leave();
1181 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1183 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1184 struct gfio_client *gc = client->client_data;
1185 GtkWidget *box, *frame, *entry, *vbox;
1189 gdk_threads_enter();
1191 if (!gc->results_widget)
1194 if (!gc->disk_util_frame) {
1195 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1196 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1199 vbox = gtk_vbox_new(FALSE, 3);
1200 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1202 frame = gtk_frame_new((char *) p->dus.name);
1203 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1205 box = gtk_vbox_new(FALSE, 3);
1206 gtk_container_add(GTK_CONTAINER(frame), box);
1208 frame = gtk_frame_new("Read");
1209 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1210 vbox = gtk_hbox_new(TRUE, 3);
1211 gtk_container_add(GTK_CONTAINER(frame), vbox);
1212 entry = new_info_entry_in_frame(vbox, "IOs");
1213 entry_set_int_value(entry, p->dus.ios[0]);
1214 entry = new_info_entry_in_frame(vbox, "Merges");
1215 entry_set_int_value(entry, p->dus.merges[0]);
1216 entry = new_info_entry_in_frame(vbox, "Sectors");
1217 entry_set_int_value(entry, p->dus.sectors[0]);
1218 entry = new_info_entry_in_frame(vbox, "Ticks");
1219 entry_set_int_value(entry, p->dus.ticks[0]);
1221 frame = gtk_frame_new("Write");
1222 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1223 vbox = gtk_hbox_new(TRUE, 3);
1224 gtk_container_add(GTK_CONTAINER(frame), vbox);
1225 entry = new_info_entry_in_frame(vbox, "IOs");
1226 entry_set_int_value(entry, p->dus.ios[1]);
1227 entry = new_info_entry_in_frame(vbox, "Merges");
1228 entry_set_int_value(entry, p->dus.merges[1]);
1229 entry = new_info_entry_in_frame(vbox, "Sectors");
1230 entry_set_int_value(entry, p->dus.sectors[1]);
1231 entry = new_info_entry_in_frame(vbox, "Ticks");
1232 entry_set_int_value(entry, p->dus.ticks[1]);
1234 frame = gtk_frame_new("Shared");
1235 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1236 vbox = gtk_hbox_new(TRUE, 3);
1237 gtk_container_add(GTK_CONTAINER(frame), vbox);
1238 entry = new_info_entry_in_frame(vbox, "IO ticks");
1239 entry_set_int_value(entry, p->dus.io_ticks);
1240 entry = new_info_entry_in_frame(vbox, "Time in queue");
1241 entry_set_int_value(entry, p->dus.time_in_queue);
1245 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1249 sprintf(tmp, "%3.2f%%", util);
1250 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1251 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1253 gtk_widget_show_all(gc->results_widget);
1255 gdk_threads_leave();
1258 extern int sum_stat_clients;
1259 extern struct thread_stat client_ts;
1260 extern struct group_run_stats client_gs;
1262 static int sum_stat_nr;
1264 static void gfio_thread_status_op(struct fio_client *client,
1265 struct fio_net_cmd *cmd)
1267 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1269 gfio_display_ts(client, &p->ts, &p->rs);
1271 if (sum_stat_clients == 1)
1274 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1275 sum_group_stats(&client_gs, &p->rs);
1277 client_ts.members++;
1278 client_ts.groupid = p->ts.groupid;
1280 if (++sum_stat_nr == sum_stat_clients) {
1281 strcpy(client_ts.name, "All clients");
1282 gfio_display_ts(client, &client_ts, &client_gs);
1286 static void gfio_group_stats_op(struct fio_client *client,
1287 struct fio_net_cmd *cmd)
1289 /* We're ignoring group stats for now */
1292 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1295 struct gfio_graphs *g = data;
1297 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1298 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1299 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1300 graph_set_position(g->bandwidth_graph, 0, 0);
1304 static void draw_graph(struct graph *g, cairo_t *cr)
1306 line_graph_draw(g, cr);
1310 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1311 gboolean keyboard_mode, GtkTooltip *tooltip,
1314 struct gfio_graphs *g = data;
1315 const char *text = NULL;
1317 if (graph_contains_xy(g->iops_graph, x, y))
1318 text = graph_find_tooltip(g->iops_graph, x, y);
1319 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1320 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1323 gtk_tooltip_set_text(tooltip, text);
1330 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1332 struct gfio_graphs *g = p;
1335 cr = gdk_cairo_create(w->window);
1337 if (graph_has_tooltips(g->iops_graph) ||
1338 graph_has_tooltips(g->bandwidth_graph)) {
1339 g_object_set(w, "has-tooltip", TRUE, NULL);
1340 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1343 cairo_set_source_rgb(cr, 0, 0, 0);
1344 draw_graph(g->iops_graph, cr);
1345 draw_graph(g->bandwidth_graph, cr);
1352 * Client specific ETA
1354 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1356 struct gfio_client *gc = client->client_data;
1357 struct gui_entry *ge = gc->ge;
1358 static int eta_good;
1365 gdk_threads_enter();
1370 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1371 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1372 eta_to_str(eta_str, je->eta_sec);
1375 sprintf(tmp, "%u", je->nr_running);
1376 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1377 sprintf(tmp, "%u", je->files_open);
1378 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1381 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1382 if (je->m_rate || je->t_rate) {
1385 mr = num2str(je->m_rate, 4, 0, i2p);
1386 tr = num2str(je->t_rate, 4, 0, i2p);
1387 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1388 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1391 } else if (je->m_iops || je->t_iops)
1392 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1394 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1395 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1396 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1397 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1400 if (je->eta_sec != INT_MAX && je->nr_running) {
1404 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1405 strcpy(output, "-.-% done");
1409 sprintf(output, "%3.1f%% done", perc);
1412 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1413 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1415 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1416 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1418 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1419 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1420 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1421 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1423 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1424 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1425 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1426 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1435 char *dst = output + strlen(output);
1437 sprintf(dst, " - %s", eta_str);
1440 gfio_update_thread_status(ge, output, perc);
1441 gdk_threads_leave();
1445 * Update ETA in main window for all clients
1447 static void gfio_update_all_eta(struct jobs_eta *je)
1449 struct gui *ui = &main_ui;
1450 static int eta_good;
1456 gdk_threads_enter();
1461 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1462 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1463 eta_to_str(eta_str, je->eta_sec);
1467 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1468 if (je->m_rate || je->t_rate) {
1471 mr = num2str(je->m_rate, 4, 0, i2p);
1472 tr = num2str(je->t_rate, 4, 0, i2p);
1473 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1474 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1477 } else if (je->m_iops || je->t_iops)
1478 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1480 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1481 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1482 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1483 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1486 entry_set_int_value(ui->eta.jobs, je->nr_running);
1488 if (je->eta_sec != INT_MAX && je->nr_running) {
1492 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1493 strcpy(output, "-.-% done");
1497 sprintf(output, "%3.1f%% done", perc);
1500 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1501 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1503 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1504 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1506 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1507 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1508 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1509 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1511 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1512 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1513 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1514 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1523 char *dst = output + strlen(output);
1525 sprintf(dst, " - %s", eta_str);
1528 gfio_update_thread_status_all(output, perc);
1529 gdk_threads_leave();
1532 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1534 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1535 struct gfio_client *gc = client->client_data;
1536 struct gui_entry *ge = gc->ge;
1537 const char *os, *arch;
1540 os = fio_get_os_string(probe->os);
1544 arch = fio_get_arch_string(probe->arch);
1549 client->name = strdup((char *) probe->hostname);
1551 gdk_threads_enter();
1553 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1554 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1555 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1556 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1557 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1559 gfio_set_state(ge, GE_STATE_CONNECTED);
1561 gdk_threads_leave();
1564 static void gfio_update_thread_status(struct gui_entry *ge,
1565 char *status_message, double perc)
1567 static char message[100];
1568 const char *m = message;
1570 strncpy(message, status_message, sizeof(message) - 1);
1571 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1572 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1573 gtk_widget_queue_draw(main_ui.window);
1576 static void gfio_update_thread_status_all(char *status_message, double perc)
1578 struct gui *ui = &main_ui;
1579 static char message[100];
1580 const char *m = message;
1582 strncpy(message, status_message, sizeof(message) - 1);
1583 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1584 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1585 gtk_widget_queue_draw(ui->window);
1588 static void gfio_quit_op(struct fio_client *client)
1590 struct gfio_client *gc = client->client_data;
1592 gdk_threads_enter();
1593 gfio_set_state(gc->ge, GE_STATE_NEW);
1594 gdk_threads_leave();
1597 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1599 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1600 struct gfio_client *gc = client->client_data;
1601 struct thread_options *o = &gc->o;
1602 struct gui_entry *ge = gc->ge;
1605 convert_thread_options_to_cpu(o, &p->top);
1607 gdk_threads_enter();
1609 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1611 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1612 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1614 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1615 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1617 sprintf(tmp, "%u", o->iodepth);
1618 multitext_add_entry(&ge->eta.iodepth, tmp);
1620 multitext_set_entry(&ge->eta.iotype, 0);
1621 multitext_set_entry(&ge->eta.ioengine, 0);
1622 multitext_set_entry(&ge->eta.iodepth, 0);
1626 gfio_set_state(ge, GE_STATE_JOB_SENT);
1628 gdk_threads_leave();
1631 static void gfio_client_timed_out(struct fio_client *client)
1633 struct gfio_client *gc = client->client_data;
1636 gdk_threads_enter();
1638 gfio_set_state(gc->ge, GE_STATE_NEW);
1639 clear_ge_ui_info(gc->ge);
1641 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1642 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1644 gdk_threads_leave();
1647 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1649 struct gfio_client *gc = client->client_data;
1651 gdk_threads_enter();
1653 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1656 entry_set_int_value(gc->err_entry, client->error);
1658 gdk_threads_leave();
1661 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1663 struct gfio_client *gc = client->client_data;
1665 gdk_threads_enter();
1666 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1667 gdk_threads_leave();
1670 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1672 struct gfio_client *gc = client->client_data;
1674 gdk_threads_enter();
1675 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1676 gdk_threads_leave();
1679 struct client_ops gfio_client_ops = {
1680 .text_op = gfio_text_op,
1681 .disk_util = gfio_disk_util_op,
1682 .thread_status = gfio_thread_status_op,
1683 .group_stats = gfio_group_stats_op,
1684 .jobs_eta = gfio_update_client_eta,
1685 .eta = gfio_update_all_eta,
1686 .probe = gfio_probe_op,
1687 .quit = gfio_quit_op,
1688 .add_job = gfio_add_job_op,
1689 .timed_out = gfio_client_timed_out,
1690 .stop = gfio_client_stop,
1691 .start = gfio_client_start,
1692 .job_start = gfio_client_job_start,
1693 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1694 .stay_connected = 1,
1697 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1698 __attribute__((unused)) gpointer data)
1703 static void *job_thread(void *arg)
1705 struct gui *ui = arg;
1707 ui->handler_running = 1;
1708 fio_handle_clients(&gfio_client_ops);
1709 ui->handler_running = 0;
1713 static int send_job_files(struct gui_entry *ge)
1715 struct gfio_client *gc = ge->client;
1718 for (i = 0; i < ge->nr_job_files; i++) {
1719 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1723 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1724 report_error(error);
1725 g_error_free(error);
1730 free(ge->job_files[i]);
1731 ge->job_files[i] = NULL;
1733 while (i < ge->nr_job_files) {
1734 free(ge->job_files[i]);
1735 ge->job_files[i] = NULL;
1739 ge->nr_job_files = 0;
1743 static void *server_thread(void *arg)
1746 gfio_server_running = 1;
1747 fio_start_server(NULL);
1748 gfio_server_running = 0;
1752 static void gfio_start_server(void)
1754 struct gui *ui = &main_ui;
1756 if (!gfio_server_running) {
1757 gfio_server_running = 1;
1758 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1759 pthread_detach(ui->server_t);
1763 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1766 struct gui_entry *ge = data;
1767 struct gfio_client *gc = ge->client;
1770 fio_start_client(gc->client);
1773 static void file_open(GtkWidget *w, gpointer data);
1775 static void connect_clicked(GtkWidget *widget, gpointer data)
1777 struct gui_entry *ge = data;
1778 struct gfio_client *gc = ge->client;
1780 if (ge->state == GE_STATE_NEW) {
1783 if (!ge->nr_job_files)
1784 file_open(widget, ge->ui);
1785 if (!ge->nr_job_files)
1788 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1789 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1790 ret = fio_client_connect(gc->client);
1792 if (!ge->ui->handler_running)
1793 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1794 gfio_set_state(ge, GE_STATE_CONNECTED);
1798 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1799 report_error(error);
1800 g_error_free(error);
1803 fio_client_terminate(gc->client);
1804 gfio_set_state(ge, GE_STATE_NEW);
1805 clear_ge_ui_info(ge);
1809 static void send_clicked(GtkWidget *widget, gpointer data)
1811 struct gui_entry *ge = data;
1813 if (send_job_files(ge)) {
1816 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);
1817 report_error(error);
1818 g_error_free(error);
1820 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1824 static GtkWidget *add_button(GtkWidget *buttonbox,
1825 struct button_spec *buttonspec, gpointer data)
1827 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1829 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1830 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1831 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1832 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1837 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1842 for (i = 0; i < nbuttons; i++)
1843 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1846 static void on_info_bar_response(GtkWidget *widget, gint response,
1849 struct gui *ui = &main_ui;
1851 if (response == GTK_RESPONSE_OK) {
1852 gtk_widget_destroy(widget);
1853 ui->error_info_bar = NULL;
1857 void report_error(GError *error)
1859 struct gui *ui = &main_ui;
1861 if (ui->error_info_bar == NULL) {
1862 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1865 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1866 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1869 ui->error_label = gtk_label_new(error->message);
1870 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1871 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1873 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1874 gtk_widget_show_all(ui->vbox);
1877 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1878 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1882 struct connection_widgets
1889 static void hostname_cb(GtkEntry *entry, gpointer data)
1891 struct connection_widgets *cw = data;
1892 int uses_net = 0, is_localhost = 0;
1897 * Check whether to display the 'auto start backend' box
1898 * or not. Show it if we are a localhost and using network,
1899 * or using a socket.
1901 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1902 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1907 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1908 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1909 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1910 !strcmp(text, "ip6-loopback"))
1914 if (!uses_net || is_localhost) {
1915 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1916 gtk_widget_set_sensitive(cw->button, 1);
1918 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1919 gtk_widget_set_sensitive(cw->button, 0);
1923 static int get_connection_details(char **host, int *port, int *type,
1926 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1927 struct connection_widgets cw;
1930 dialog = gtk_dialog_new_with_buttons("Connection details",
1931 GTK_WINDOW(main_ui.window),
1932 GTK_DIALOG_DESTROY_WITH_PARENT,
1933 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1934 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1936 frame = gtk_frame_new("Hostname / socket name");
1937 /* gtk_dialog_get_content_area() is 2.14 and newer */
1938 vbox = GTK_DIALOG(dialog)->vbox;
1939 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1941 box = gtk_vbox_new(FALSE, 6);
1942 gtk_container_add(GTK_CONTAINER(frame), box);
1944 hbox = gtk_hbox_new(TRUE, 10);
1945 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1946 cw.hentry = gtk_entry_new();
1947 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1948 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1950 frame = gtk_frame_new("Port");
1951 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1952 box = gtk_vbox_new(FALSE, 10);
1953 gtk_container_add(GTK_CONTAINER(frame), box);
1955 hbox = gtk_hbox_new(TRUE, 4);
1956 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1957 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1959 frame = gtk_frame_new("Type");
1960 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1961 box = gtk_vbox_new(FALSE, 10);
1962 gtk_container_add(GTK_CONTAINER(frame), box);
1964 hbox = gtk_hbox_new(TRUE, 4);
1965 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1967 cw.combo = gtk_combo_box_new_text();
1968 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1969 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1970 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1971 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1973 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1975 frame = gtk_frame_new("Options");
1976 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1977 box = gtk_vbox_new(FALSE, 10);
1978 gtk_container_add(GTK_CONTAINER(frame), box);
1980 hbox = gtk_hbox_new(TRUE, 4);
1981 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1983 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1984 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1985 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.");
1986 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1989 * Connect edit signal, so we can show/not-show the auto start button
1991 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1992 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1994 gtk_widget_show_all(dialog);
1996 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1997 gtk_widget_destroy(dialog);
2001 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
2002 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2004 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
2005 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2006 *type = Fio_client_ipv4;
2007 else if (!strncmp(typeentry, "IPv6", 4))
2008 *type = Fio_client_ipv6;
2010 *type = Fio_client_socket;
2013 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2015 gtk_widget_destroy(dialog);
2019 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2021 struct gfio_client *gc;
2023 gc = malloc(sizeof(*gc));
2024 memset(gc, 0, sizeof(*gc));
2026 gc->client = fio_get_client(client);
2030 client->client_data = gc;
2033 static GtkWidget *new_client_page(struct gui_entry *ge);
2035 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2037 struct gui_entry *ge;
2039 ge = malloc(sizeof(*ge));
2040 memset(ge, 0, sizeof(*ge));
2041 ge->state = GE_STATE_NEW;
2042 INIT_FLIST_HEAD(&ge->list);
2043 flist_add_tail(&ge->list, &ui->list);
2049 * FIXME: need more handling here
2051 static void ge_destroy(GtkWidget *w, gpointer data)
2053 struct gui_entry *ge = data;
2054 struct gfio_client *gc = ge->client;
2056 if (gc && gc->client) {
2057 if (ge->state >= GE_STATE_CONNECTED)
2058 fio_client_terminate(gc->client);
2060 fio_put_client(gc->client);
2063 flist_del(&ge->list);
2067 static struct gui_entry *get_new_ge_with_tab(const char *name)
2069 struct gui_entry *ge;
2071 ge = alloc_new_gui_entry(&main_ui);
2073 ge->vbox = new_client_page(ge);
2074 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
2076 ge->page_label = gtk_label_new(name);
2077 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2079 gtk_widget_show_all(main_ui.window);
2083 static void file_new(GtkWidget *w, gpointer data)
2085 struct gui *ui = (struct gui *) data;
2086 struct gui_entry *ge;
2088 ge = get_new_ge_with_tab("Untitled");
2089 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2093 * Return the 'ge' corresponding to the tab. If the active tab is the
2094 * main tab, open a new tab.
2096 static struct gui_entry *get_ge_from_page(gint cur_page)
2098 struct flist_head *entry;
2099 struct gui_entry *ge;
2102 return get_new_ge_with_tab("Untitled");
2104 flist_for_each(entry, &main_ui.list) {
2105 ge = flist_entry(entry, struct gui_entry, list);
2106 if (ge->page_num == cur_page)
2113 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2118 * Main tab is tab 0, so any current page other than 0 holds
2121 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2123 return get_ge_from_page(cur_page);
2128 static void file_close(GtkWidget *w, gpointer data)
2130 struct gui *ui = (struct gui *) data;
2131 struct gui_entry *ge;
2134 * Can't close the main tab
2136 ge = get_ge_from_cur_tab(ui);
2138 gtk_widget_destroy(ge->vbox);
2142 if (!flist_empty(&ui->list)) {
2143 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2150 static void file_open(GtkWidget *w, gpointer data)
2152 struct gui *ui = data;
2154 GSList *filenames, *fn_glist;
2155 GtkFileFilter *filter;
2157 int port, type, server_start;
2158 struct gui_entry *ge;
2162 * Creates new tab if current tab is the main window, or the
2163 * current tab already has a client.
2165 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2166 ge = get_ge_from_page(cur_page);
2168 ge = get_new_ge_with_tab("Untitled");
2170 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2172 dialog = gtk_file_chooser_dialog_new("Open File",
2173 GTK_WINDOW(ui->window),
2174 GTK_FILE_CHOOSER_ACTION_OPEN,
2175 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2176 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2178 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2180 filter = gtk_file_filter_new();
2181 gtk_file_filter_add_pattern(filter, "*.fio");
2182 gtk_file_filter_add_pattern(filter, "*.job");
2183 gtk_file_filter_add_pattern(filter, "*.ini");
2184 gtk_file_filter_add_mime_type(filter, "text/fio");
2185 gtk_file_filter_set_name(filter, "Fio job file");
2186 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2188 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2189 gtk_widget_destroy(dialog);
2193 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2195 gtk_widget_destroy(dialog);
2197 if (get_connection_details(&host, &port, &type, &server_start))
2200 filenames = fn_glist;
2201 while (filenames != NULL) {
2202 struct fio_client *client;
2204 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2205 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2208 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2212 error = g_error_new(g_quark_from_string("fio"), 1,
2213 "Failed to add client %s", host);
2214 report_error(error);
2215 g_error_free(error);
2217 gfio_client_added(ge, client);
2219 g_free(filenames->data);
2220 filenames = g_slist_next(filenames);
2225 gfio_start_server();
2227 g_slist_free(fn_glist);
2230 static void file_save(GtkWidget *w, gpointer data)
2232 struct gui *ui = data;
2235 dialog = gtk_file_chooser_dialog_new("Save File",
2236 GTK_WINDOW(ui->window),
2237 GTK_FILE_CHOOSER_ACTION_SAVE,
2238 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2239 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2242 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2243 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2245 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2248 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2249 // save_job_file(filename);
2252 gtk_widget_destroy(dialog);
2255 static void view_log_destroy(GtkWidget *w, gpointer data)
2257 struct gui *ui = (struct gui *) data;
2259 gtk_widget_ref(ui->log_tree);
2260 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2261 gtk_widget_destroy(w);
2262 ui->log_view = NULL;
2265 static void view_log(GtkWidget *w, gpointer data)
2267 GtkWidget *win, *scroll, *vbox, *box;
2268 struct gui *ui = (struct gui *) data;
2273 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2274 gtk_window_set_title(GTK_WINDOW(win), "Log");
2275 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2277 scroll = gtk_scrolled_window_new(NULL, NULL);
2279 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2281 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2283 box = gtk_hbox_new(TRUE, 0);
2284 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2285 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2286 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2288 vbox = gtk_vbox_new(TRUE, 5);
2289 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2291 gtk_container_add(GTK_CONTAINER(win), vbox);
2292 gtk_widget_show_all(win);
2295 static void connect_job_entry(GtkWidget *w, gpointer data)
2297 struct gui *ui = (struct gui *) data;
2298 struct gui_entry *ge;
2300 ge = get_ge_from_cur_tab(ui);
2302 connect_clicked(w, ge);
2305 static void send_job_entry(GtkWidget *w, gpointer data)
2307 struct gui *ui = (struct gui *) data;
2308 struct gui_entry *ge;
2310 ge = get_ge_from_cur_tab(ui);
2312 send_clicked(w, ge);
2316 static void edit_job_entry(GtkWidget *w, gpointer data)
2320 static void start_job_entry(GtkWidget *w, gpointer data)
2322 struct gui *ui = (struct gui *) data;
2323 struct gui_entry *ge;
2325 ge = get_ge_from_cur_tab(ui);
2327 start_job_clicked(w, ge);
2330 static void __update_graph_limits(struct gfio_graphs *g)
2332 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2333 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2336 static void update_graph_limits(void)
2338 struct flist_head *entry;
2339 struct gui_entry *ge;
2341 __update_graph_limits(&main_ui.graphs);
2343 flist_for_each(entry, &main_ui.list) {
2344 ge = flist_entry(entry, struct gui_entry, list);
2345 __update_graph_limits(&ge->graphs);
2349 static void preferences(GtkWidget *w, gpointer data)
2351 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2352 GtkWidget *hbox, *spin, *entry, *spin_int;
2355 dialog = gtk_dialog_new_with_buttons("Preferences",
2356 GTK_WINDOW(main_ui.window),
2357 GTK_DIALOG_DESTROY_WITH_PARENT,
2358 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2359 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2362 frame = gtk_frame_new("Graphing");
2363 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2364 vbox = gtk_vbox_new(FALSE, 6);
2365 gtk_container_add(GTK_CONTAINER(frame), vbox);
2367 hbox = gtk_hbox_new(FALSE, 5);
2368 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2369 entry = gtk_label_new("Font face to use for graph labels");
2370 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2372 font = gtk_font_button_new();
2373 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2375 box = gtk_vbox_new(FALSE, 6);
2376 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2378 hbox = gtk_hbox_new(FALSE, 5);
2379 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2380 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2381 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2383 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2385 box = gtk_vbox_new(FALSE, 6);
2386 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2388 hbox = gtk_hbox_new(FALSE, 5);
2389 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2390 entry = gtk_label_new("Client ETA request interval (msec)");
2391 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2393 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2394 frame = gtk_frame_new("Debug logging");
2395 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2396 vbox = gtk_vbox_new(FALSE, 6);
2397 gtk_container_add(GTK_CONTAINER(frame), vbox);
2399 box = gtk_hbox_new(FALSE, 6);
2400 gtk_container_add(GTK_CONTAINER(vbox), box);
2402 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2404 for (i = 0; i < FD_DEBUG_MAX; i++) {
2406 box = gtk_hbox_new(FALSE, 6);
2407 gtk_container_add(GTK_CONTAINER(vbox), box);
2411 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2412 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2413 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2416 gtk_widget_show_all(dialog);
2418 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2419 gtk_widget_destroy(dialog);
2423 for (i = 0; i < FD_DEBUG_MAX; i++) {
2426 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2428 fio_debug |= (1UL << i);
2431 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2432 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2433 update_graph_limits();
2434 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2436 gtk_widget_destroy(dialog);
2439 static void about_dialog(GtkWidget *w, gpointer data)
2441 const char *authors[] = {
2442 "Jens Axboe <axboe@kernel.dk>",
2443 "Stephen Carmeron <stephenmcameron@gmail.com>",
2446 const char *license[] = {
2447 "Fio is free software; you can redistribute it and/or modify "
2448 "it under the terms of the GNU General Public License as published by "
2449 "the Free Software Foundation; either version 2 of the License, or "
2450 "(at your option) any later version.\n",
2451 "Fio is distributed in the hope that it will be useful, "
2452 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2453 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2454 "GNU General Public License for more details.\n",
2455 "You should have received a copy of the GNU General Public License "
2456 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2457 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2459 char *license_trans;
2461 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2462 license[2], "\n", NULL);
2464 gtk_show_about_dialog(NULL,
2465 "program-name", "gfio",
2466 "comments", "Gtk2 UI for fio",
2467 "license", license_trans,
2468 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2470 "version", fio_version_string,
2471 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2472 "logo-icon-name", "fio",
2474 "wrap-license", TRUE,
2477 g_free(license_trans);
2480 static GtkActionEntry menu_items[] = {
2481 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2482 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2483 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2484 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2485 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2486 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2487 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2488 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2489 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2490 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2491 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2492 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2493 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2494 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2495 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2496 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2498 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2500 static const gchar *ui_string = " \
2502 <menubar name=\"MainMenu\"> \
2503 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2504 <menuitem name=\"New\" action=\"NewFile\" /> \
2505 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2506 <separator name=\"Separator1\"/> \
2507 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2508 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2509 <separator name=\"Separator2\"/> \
2510 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2511 <separator name=\"Separator3\"/> \
2512 <placeholder name=\"FileRecentFiles\"/> \
2513 <separator name=\"Separator4\"/> \
2514 <menuitem name=\"Quit\" action=\"Quit\" /> \
2516 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2517 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2518 <separator name=\"Separator5\"/> \
2519 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2520 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2521 <separator name=\"Separator6\"/> \
2522 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2524 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2525 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2527 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2528 <menuitem name=\"About\" action=\"About\" /> \
2534 static void set_job_menu_visible(struct gui *ui, int visible)
2538 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2539 gtk_widget_set_sensitive(job, visible);
2542 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2545 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2548 action_group = gtk_action_group_new("Menu");
2549 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2551 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2552 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2554 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2556 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2559 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2560 GtkWidget *vbox, GtkUIManager *ui_manager)
2562 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2565 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2567 struct gui_entry *ge = (struct gui_entry *) data;
2570 index = gtk_combo_box_get_active(box);
2572 multitext_set_entry(&ge->eta.iotype, index);
2573 multitext_set_entry(&ge->eta.ioengine, index);
2574 multitext_set_entry(&ge->eta.iodepth, index);
2577 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2579 struct gui_entry *ge = (struct gui_entry *) data;
2581 multitext_free(&ge->eta.iotype);
2582 multitext_free(&ge->eta.ioengine);
2583 multitext_free(&ge->eta.iodepth);
2586 static GtkWidget *new_client_page(struct gui_entry *ge)
2588 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2591 main_vbox = gtk_vbox_new(FALSE, 3);
2593 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2594 ge->topvbox = gtk_vbox_new(FALSE, 3);
2595 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2596 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2598 probe = gtk_frame_new("Job");
2599 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2600 probe_frame = gtk_vbox_new(FALSE, 3);
2601 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2603 probe_box = gtk_hbox_new(FALSE, 3);
2604 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2605 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2606 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2607 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2608 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2610 probe_box = gtk_hbox_new(FALSE, 3);
2611 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2613 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2614 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2615 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2616 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2617 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2618 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2619 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2620 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2622 probe_box = gtk_hbox_new(FALSE, 3);
2623 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2624 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2625 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2626 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2627 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2630 * Only add this if we have a commit rate
2633 probe_box = gtk_hbox_new(FALSE, 3);
2634 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2636 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2637 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2639 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2640 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2644 * Set up a drawing area and IOPS and bandwidth graphs
2646 gdk_color_parse("white", &white);
2647 ge->graphs.drawing_area = gtk_drawing_area_new();
2648 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2649 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2650 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2651 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2652 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2653 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2654 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2655 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2656 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2657 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2658 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2659 ge->graphs.drawing_area);
2660 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2663 setup_graphs(&ge->graphs);
2666 * Set up alignments for widgets at the bottom of ui,
2667 * align bottom left, expand horizontally but not vertically
2669 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2670 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2671 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2672 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2675 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2678 * Set up thread status progress bar
2680 ge->thread_status_pb = gtk_progress_bar_new();
2681 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2682 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2683 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2689 static GtkWidget *new_main_page(struct gui *ui)
2691 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2694 main_vbox = gtk_vbox_new(FALSE, 3);
2697 * Set up alignments for widgets at the top of ui,
2698 * align top left, expand horizontally but not vertically
2700 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2701 ui->topvbox = gtk_vbox_new(FALSE, 0);
2702 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2703 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2705 probe = gtk_frame_new("Run statistics");
2706 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2707 probe_frame = gtk_vbox_new(FALSE, 3);
2708 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2710 probe_box = gtk_hbox_new(FALSE, 3);
2711 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2712 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2713 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2714 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2715 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2716 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2719 * Only add this if we have a commit rate
2722 probe_box = gtk_hbox_new(FALSE, 3);
2723 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2725 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2726 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2728 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2729 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2733 * Set up a drawing area and IOPS and bandwidth graphs
2735 gdk_color_parse("white", &white);
2736 ui->graphs.drawing_area = gtk_drawing_area_new();
2737 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2738 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2739 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2740 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2741 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2742 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2743 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2744 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2745 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2746 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2747 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2748 ui->graphs.drawing_area);
2749 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2752 setup_graphs(&ui->graphs);
2755 * Set up alignments for widgets at the bottom of ui,
2756 * align bottom left, expand horizontally but not vertically
2758 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2759 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2760 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2761 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2765 * Set up thread status progress bar
2767 ui->thread_status_pb = gtk_progress_bar_new();
2768 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2769 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2770 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2775 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2776 guint page, gpointer data)
2779 struct gui *ui = (struct gui *) data;
2780 struct gui_entry *ge;
2783 set_job_menu_visible(ui, 0);
2787 set_job_menu_visible(ui, 1);
2788 ge = get_ge_from_page(page);
2790 update_button_states(ui, ge);
2795 static void init_ui(int *argc, char **argv[], struct gui *ui)
2797 GtkSettings *settings;
2800 /* Magical g*thread incantation, you just need this thread stuff.
2801 * Without it, the update that happens in gfio_update_thread_status
2802 * doesn't really happen in a timely fashion, you need expose events
2804 if (!g_thread_supported())
2805 g_thread_init(NULL);
2808 gtk_init(argc, argv);
2809 settings = gtk_settings_get_default();
2810 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2813 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2814 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2815 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2817 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2818 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2820 ui->vbox = gtk_vbox_new(FALSE, 0);
2821 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2823 ui->uimanager = gtk_ui_manager_new();
2824 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
2825 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2827 ui->notebook = gtk_notebook_new();
2828 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2829 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2830 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2831 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2833 vbox = new_main_page(ui);
2835 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2837 gfio_ui_setup_log(ui);
2839 gtk_widget_show_all(ui->window);
2842 int main(int argc, char *argv[], char *envp[])
2844 if (initialize_fio(envp))
2846 if (fio_init_options())
2849 memset(&main_ui, 0, sizeof(main_ui));
2850 INIT_FLIST_HEAD(&main_ui.list);
2852 init_ui(&argc, &argv, &main_ui);
2854 gdk_threads_enter();
2856 gdk_threads_leave();