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");
489 gtk_widget_set_sensitive(w, connect_state);
491 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Edit job");
493 gtk_widget_set_sensitive(w, edit_state);
495 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Send job");
497 gtk_widget_set_sensitive(w, send_state);
499 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Start job");
501 gtk_widget_set_sensitive(w, start_state);
504 static void gfio_set_state(struct gui_entry *ge, unsigned int state)
507 update_button_states(ge->ui, ge);
511 #define ALIGN_RIGHT 2
515 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
517 GtkCellRenderer *renderer;
518 GtkTreeViewColumn *col;
519 double xalign = 0.0; /* left as default */
520 PangoAlignment align;
523 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
524 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
526 visible = !(flags & INVISIBLE);
528 renderer = gtk_cell_renderer_text_new();
529 col = gtk_tree_view_column_new();
531 gtk_tree_view_column_set_title(col, title);
532 if (!(flags & UNSORTABLE))
533 gtk_tree_view_column_set_sort_column_id(col, index);
534 gtk_tree_view_column_set_resizable(col, TRUE);
535 gtk_tree_view_column_pack_start(col, renderer, TRUE);
536 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
537 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
539 case PANGO_ALIGN_LEFT:
542 case PANGO_ALIGN_CENTER:
545 case PANGO_ALIGN_RIGHT:
549 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
550 gtk_tree_view_column_set_visible(col, visible);
551 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
555 static void gfio_ui_setup_log(struct gui *ui)
557 GtkTreeSelection *selection;
559 GtkWidget *tree_view;
561 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
563 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
564 gtk_widget_set_can_focus(tree_view, FALSE);
566 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
567 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
568 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
569 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
571 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
572 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
573 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
574 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
576 ui->log_model = model;
577 ui->log_tree = tree_view;
580 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
586 GType types[FIO_IO_U_LIST_MAX_LEN];
587 GtkWidget *tree_view;
588 GtkTreeSelection *selection;
593 for (i = 0; i < len; i++)
594 types[i] = G_TYPE_INT;
596 model = gtk_list_store_newv(len, types);
598 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
599 gtk_widget_set_can_focus(tree_view, FALSE);
601 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
602 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
604 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
605 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
607 for (i = 0; i < len; i++) {
610 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
611 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
614 gtk_list_store_append(model, &iter);
616 for (i = 0; i < len; i++) {
618 ovals[i] = (ovals[i] + 999) / 1000;
619 gtk_list_store_set(model, &iter, i, ovals[i], -1);
625 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
628 unsigned int *io_u_plat = ts->io_u_plat[ddir];
629 unsigned long nr = ts->clat_stat[ddir].samples;
630 fio_fp64_t *plist = ts->percentile_list;
631 unsigned int *ovals, len, minv, maxv, scale_down;
633 GtkWidget *tree_view, *frame, *hbox;
636 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
641 * We default to usecs, but if the value range is such that we
642 * should scale down to msecs, do that.
644 if (minv > 2000 && maxv > 99999) {
652 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
654 sprintf(tmp, "Completion percentiles (%s)", base);
655 frame = gtk_frame_new(tmp);
656 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
658 hbox = gtk_hbox_new(FALSE, 3);
659 gtk_container_add(GTK_CONTAINER(frame), hbox);
661 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
667 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
668 unsigned long max, double mean, double dev)
670 const char *base = "(usec)";
671 GtkWidget *hbox, *label, *frame;
675 if (!usec_to_msec(&min, &max, &mean, &dev))
678 minp = num2str(min, 6, 1, 0);
679 maxp = num2str(max, 6, 1, 0);
681 sprintf(tmp, "%s %s", name, base);
682 frame = gtk_frame_new(tmp);
683 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
685 hbox = gtk_hbox_new(FALSE, 3);
686 gtk_container_add(GTK_CONTAINER(frame), hbox);
688 label = new_info_label_in_frame(hbox, "Minimum");
689 gtk_label_set_text(GTK_LABEL(label), minp);
690 label = new_info_label_in_frame(hbox, "Maximum");
691 gtk_label_set_text(GTK_LABEL(label), maxp);
692 label = new_info_label_in_frame(hbox, "Average");
693 sprintf(tmp, "%5.02f", mean);
694 gtk_label_set_text(GTK_LABEL(label), tmp);
695 label = new_info_label_in_frame(hbox, "Standard deviation");
696 sprintf(tmp, "%5.02f", dev);
697 gtk_label_set_text(GTK_LABEL(label), tmp);
708 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
709 struct thread_stat *ts, int ddir)
711 const char *ddir_label[2] = { "Read", "Write" };
712 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
713 unsigned long min[3], max[3], runt;
714 unsigned long long bw, iops;
715 unsigned int flags = 0;
716 double mean[3], dev[3];
717 char *io_p, *bw_p, *iops_p;
720 if (!ts->runtime[ddir])
723 i2p = is_power_of_2(rs->kb_base);
724 runt = ts->runtime[ddir];
726 bw = (1000 * ts->io_bytes[ddir]) / runt;
727 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
728 bw_p = num2str(bw, 6, 1, i2p);
730 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
731 iops_p = num2str(iops, 6, 1, 0);
733 box = gtk_hbox_new(FALSE, 3);
734 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
736 frame = gtk_frame_new(ddir_label[ddir]);
737 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
739 main_vbox = gtk_vbox_new(FALSE, 3);
740 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
742 box = gtk_hbox_new(FALSE, 3);
743 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
745 label = new_info_label_in_frame(box, "IO");
746 gtk_label_set_text(GTK_LABEL(label), io_p);
747 label = new_info_label_in_frame(box, "Bandwidth");
748 gtk_label_set_text(GTK_LABEL(label), bw_p);
749 label = new_info_label_in_frame(box, "IOPS");
750 gtk_label_set_text(GTK_LABEL(label), iops_p);
751 label = new_info_label_in_frame(box, "Runtime (msec)");
752 label_set_int_value(label, ts->runtime[ddir]);
754 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
755 double p_of_agg = 100.0;
756 const char *bw_str = "KB";
760 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
761 if (p_of_agg > 100.0)
765 if (mean[0] > 999999.9) {
773 sprintf(tmp, "Bandwidth (%s)", bw_str);
774 frame = gtk_frame_new(tmp);
775 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
777 box = gtk_hbox_new(FALSE, 3);
778 gtk_container_add(GTK_CONTAINER(frame), box);
780 label = new_info_label_in_frame(box, "Minimum");
781 label_set_int_value(label, min[0]);
782 label = new_info_label_in_frame(box, "Maximum");
783 label_set_int_value(label, max[0]);
784 label = new_info_label_in_frame(box, "Percentage of jobs");
785 sprintf(tmp, "%3.2f%%", p_of_agg);
786 gtk_label_set_text(GTK_LABEL(label), tmp);
787 label = new_info_label_in_frame(box, "Average");
788 sprintf(tmp, "%5.02f", mean[0]);
789 gtk_label_set_text(GTK_LABEL(label), tmp);
790 label = new_info_label_in_frame(box, "Standard deviation");
791 sprintf(tmp, "%5.02f", dev[0]);
792 gtk_label_set_text(GTK_LABEL(label), tmp);
795 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
797 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
799 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
803 frame = gtk_frame_new("Latency");
804 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
806 vbox = gtk_vbox_new(FALSE, 3);
807 gtk_container_add(GTK_CONTAINER(frame), vbox);
809 if (flags & GFIO_SLAT)
810 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
811 if (flags & GFIO_CLAT)
812 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
813 if (flags & GFIO_LAT)
814 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
817 if (ts->clat_percentiles)
818 gfio_show_clat_percentiles(main_vbox, ts, ddir);
826 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
829 GtkWidget *tree_view;
830 GtkTreeSelection *selection;
837 * Check if all are empty, in which case don't bother
839 for (i = 0, skipped = 0; i < num; i++)
846 types = malloc(num * sizeof(GType));
848 for (i = 0; i < num; i++)
849 types[i] = G_TYPE_STRING;
851 model = gtk_list_store_newv(num, types);
855 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
856 gtk_widget_set_can_focus(tree_view, FALSE);
858 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
859 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
861 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
862 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
864 for (i = 0; i < num; i++)
865 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
867 gtk_list_store_append(model, &iter);
869 for (i = 0; i < num; i++) {
873 sprintf(fbuf, "0.00");
875 sprintf(fbuf, "%3.2f%%", lat[i]);
877 gtk_list_store_set(model, &iter, i, fbuf, -1);
883 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
885 GtkWidget *box, *frame, *tree_view;
886 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
887 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
888 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
889 "250", "500", "750", "1000", };
890 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
891 "250", "500", "750", "1000", "2000",
894 stat_calc_lat_u(ts, io_u_lat_u);
895 stat_calc_lat_m(ts, io_u_lat_m);
897 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
899 frame = gtk_frame_new("Latency buckets (usec)");
900 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
902 box = gtk_hbox_new(FALSE, 3);
903 gtk_container_add(GTK_CONTAINER(frame), box);
904 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
907 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
909 frame = gtk_frame_new("Latency buckets (msec)");
910 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
912 box = gtk_hbox_new(FALSE, 3);
913 gtk_container_add(GTK_CONTAINER(frame), box);
914 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
918 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
920 GtkWidget *box, *frame, *entry;
921 double usr_cpu, sys_cpu;
922 unsigned long runtime;
925 runtime = ts->total_run_time;
927 double runt = (double) runtime;
929 usr_cpu = (double) ts->usr_time * 100 / runt;
930 sys_cpu = (double) ts->sys_time * 100 / runt;
936 frame = gtk_frame_new("OS resources");
937 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
939 box = gtk_hbox_new(FALSE, 3);
940 gtk_container_add(GTK_CONTAINER(frame), box);
942 entry = new_info_entry_in_frame(box, "User CPU");
943 sprintf(tmp, "%3.2f%%", usr_cpu);
944 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
945 entry = new_info_entry_in_frame(box, "System CPU");
946 sprintf(tmp, "%3.2f%%", sys_cpu);
947 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
948 entry = new_info_entry_in_frame(box, "Context switches");
949 entry_set_int_value(entry, ts->ctx);
950 entry = new_info_entry_in_frame(box, "Major faults");
951 entry_set_int_value(entry, ts->majf);
952 entry = new_info_entry_in_frame(box, "Minor faults");
953 entry_set_int_value(entry, ts->minf);
955 static void gfio_add_sc_depths_tree(GtkListStore *model,
956 struct thread_stat *ts, unsigned int len,
959 double io_u_dist[FIO_IO_U_MAP_NR];
961 /* Bits 0, and 3-8 */
962 const int add_mask = 0x1f9;
966 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
968 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
970 gtk_list_store_append(model, &iter);
972 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
974 for (i = 1, j = 0; i < len; i++) {
977 if (!(add_mask & (1UL << (i - 1))))
978 sprintf(fbuf, "0.0%%");
980 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
984 gtk_list_store_set(model, &iter, i, fbuf, -1);
989 static void gfio_add_total_depths_tree(GtkListStore *model,
990 struct thread_stat *ts, unsigned int len)
992 double io_u_dist[FIO_IO_U_MAP_NR];
994 /* Bits 1-6, and 8 */
995 const int add_mask = 0x17e;
998 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1000 gtk_list_store_append(model, &iter);
1002 gtk_list_store_set(model, &iter, 0, "Total", -1);
1004 for (i = 1, j = 0; i < len; i++) {
1007 if (!(add_mask & (1UL << (i - 1))))
1008 sprintf(fbuf, "0.0%%");
1010 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1014 gtk_list_store_set(model, &iter, i, fbuf, -1);
1019 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1021 GtkWidget *frame, *box, *tree_view;
1022 GtkTreeSelection *selection;
1023 GtkListStore *model;
1024 GType types[FIO_IO_U_MAP_NR + 1];
1026 #define NR_LABELS 10
1027 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
1029 frame = gtk_frame_new("IO depths");
1030 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1032 box = gtk_hbox_new(FALSE, 3);
1033 gtk_container_add(GTK_CONTAINER(frame), box);
1035 for (i = 0; i < NR_LABELS; i++)
1036 types[i] = G_TYPE_STRING;
1038 model = gtk_list_store_newv(NR_LABELS, types);
1040 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1041 gtk_widget_set_can_focus(tree_view, FALSE);
1043 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1044 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1046 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1047 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1049 for (i = 0; i < NR_LABELS; i++)
1050 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1052 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1053 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1054 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
1056 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1059 static gboolean results_window_delete(GtkWidget *w, gpointer data)
1061 struct gui_entry *ge = (struct gui_entry *) data;
1063 gtk_widget_destroy(w);
1064 ge->results_window = NULL;
1065 ge->results_notebook = NULL;
1069 static GtkWidget *get_results_window(struct gui_entry *ge)
1071 GtkWidget *win, *notebook;
1073 if (ge->results_window)
1074 return ge->results_notebook;
1076 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1077 gtk_window_set_title(GTK_WINDOW(win), "Results");
1078 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
1079 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1080 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
1082 notebook = gtk_notebook_new();
1083 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1084 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
1085 gtk_container_add(GTK_CONTAINER(win), notebook);
1087 ge->results_window = win;
1088 ge->results_notebook = notebook;
1089 return ge->results_notebook;
1092 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1093 struct group_run_stats *rs)
1095 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
1096 struct gfio_client *gc = client->client_data;
1098 gdk_threads_enter();
1100 res_win = get_results_window(gc->ge);
1102 scroll = gtk_scrolled_window_new(NULL, NULL);
1103 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1104 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1106 vbox = gtk_vbox_new(FALSE, 3);
1108 box = gtk_hbox_new(FALSE, 0);
1109 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1111 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1113 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
1115 gc->results_widget = vbox;
1117 entry = new_info_entry_in_frame(box, "Name");
1118 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1119 if (strlen(ts->description)) {
1120 entry = new_info_entry_in_frame(box, "Description");
1121 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1123 entry = new_info_entry_in_frame(box, "Group ID");
1124 entry_set_int_value(entry, ts->groupid);
1125 entry = new_info_entry_in_frame(box, "Jobs");
1126 entry_set_int_value(entry, ts->members);
1127 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1128 entry_set_int_value(entry, ts->error);
1129 entry = new_info_entry_in_frame(box, "PID");
1130 entry_set_int_value(entry, ts->pid);
1132 if (ts->io_bytes[DDIR_READ])
1133 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1134 if (ts->io_bytes[DDIR_WRITE])
1135 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1137 gfio_show_latency_buckets(vbox, ts);
1138 gfio_show_cpu_usage(vbox, ts);
1139 gfio_show_io_depths(vbox, ts);
1141 gtk_widget_show_all(gc->ge->results_window);
1142 gdk_threads_leave();
1145 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1147 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1148 struct gui *ui = &main_ui;
1152 char tmp[64], timebuf[80];
1155 tm = localtime(&sec);
1156 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1157 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1159 gdk_threads_enter();
1161 gtk_list_store_append(ui->log_model, &iter);
1162 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1163 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1164 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1165 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1167 if (p->level == FIO_LOG_ERR)
1168 view_log(NULL, (gpointer) ui);
1170 gdk_threads_leave();
1173 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1175 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1176 struct gfio_client *gc = client->client_data;
1177 GtkWidget *box, *frame, *entry, *vbox;
1181 gdk_threads_enter();
1183 if (!gc->results_widget)
1186 if (!gc->disk_util_frame) {
1187 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1188 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1191 vbox = gtk_vbox_new(FALSE, 3);
1192 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1194 frame = gtk_frame_new((char *) p->dus.name);
1195 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1197 box = gtk_vbox_new(FALSE, 3);
1198 gtk_container_add(GTK_CONTAINER(frame), box);
1200 frame = gtk_frame_new("Read");
1201 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1202 vbox = gtk_hbox_new(TRUE, 3);
1203 gtk_container_add(GTK_CONTAINER(frame), vbox);
1204 entry = new_info_entry_in_frame(vbox, "IOs");
1205 entry_set_int_value(entry, p->dus.ios[0]);
1206 entry = new_info_entry_in_frame(vbox, "Merges");
1207 entry_set_int_value(entry, p->dus.merges[0]);
1208 entry = new_info_entry_in_frame(vbox, "Sectors");
1209 entry_set_int_value(entry, p->dus.sectors[0]);
1210 entry = new_info_entry_in_frame(vbox, "Ticks");
1211 entry_set_int_value(entry, p->dus.ticks[0]);
1213 frame = gtk_frame_new("Write");
1214 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1215 vbox = gtk_hbox_new(TRUE, 3);
1216 gtk_container_add(GTK_CONTAINER(frame), vbox);
1217 entry = new_info_entry_in_frame(vbox, "IOs");
1218 entry_set_int_value(entry, p->dus.ios[1]);
1219 entry = new_info_entry_in_frame(vbox, "Merges");
1220 entry_set_int_value(entry, p->dus.merges[1]);
1221 entry = new_info_entry_in_frame(vbox, "Sectors");
1222 entry_set_int_value(entry, p->dus.sectors[1]);
1223 entry = new_info_entry_in_frame(vbox, "Ticks");
1224 entry_set_int_value(entry, p->dus.ticks[1]);
1226 frame = gtk_frame_new("Shared");
1227 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1228 vbox = gtk_hbox_new(TRUE, 3);
1229 gtk_container_add(GTK_CONTAINER(frame), vbox);
1230 entry = new_info_entry_in_frame(vbox, "IO ticks");
1231 entry_set_int_value(entry, p->dus.io_ticks);
1232 entry = new_info_entry_in_frame(vbox, "Time in queue");
1233 entry_set_int_value(entry, p->dus.time_in_queue);
1237 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1241 sprintf(tmp, "%3.2f%%", util);
1242 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1243 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1245 gtk_widget_show_all(gc->results_widget);
1247 gdk_threads_leave();
1250 extern int sum_stat_clients;
1251 extern struct thread_stat client_ts;
1252 extern struct group_run_stats client_gs;
1254 static int sum_stat_nr;
1256 static void gfio_thread_status_op(struct fio_client *client,
1257 struct fio_net_cmd *cmd)
1259 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1261 gfio_display_ts(client, &p->ts, &p->rs);
1263 if (sum_stat_clients == 1)
1266 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1267 sum_group_stats(&client_gs, &p->rs);
1269 client_ts.members++;
1270 client_ts.groupid = p->ts.groupid;
1272 if (++sum_stat_nr == sum_stat_clients) {
1273 strcpy(client_ts.name, "All clients");
1274 gfio_display_ts(client, &client_ts, &client_gs);
1278 static void gfio_group_stats_op(struct fio_client *client,
1279 struct fio_net_cmd *cmd)
1281 /* We're ignoring group stats for now */
1284 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1287 struct gfio_graphs *g = data;
1289 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1290 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1291 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1292 graph_set_position(g->bandwidth_graph, 0, 0);
1296 static void draw_graph(struct graph *g, cairo_t *cr)
1298 line_graph_draw(g, cr);
1302 static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1303 gboolean keyboard_mode, GtkTooltip *tooltip,
1306 struct gfio_graphs *g = data;
1307 const char *text = NULL;
1309 if (graph_contains_xy(g->iops_graph, x, y))
1310 text = graph_find_tooltip(g->iops_graph, x, y);
1311 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1312 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1315 gtk_tooltip_set_text(tooltip, text);
1322 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1324 struct gfio_graphs *g = p;
1327 cr = gdk_cairo_create(w->window);
1329 if (graph_has_tooltips(g->iops_graph) ||
1330 graph_has_tooltips(g->bandwidth_graph)) {
1331 g_object_set(w, "has-tooltip", TRUE, NULL);
1332 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1335 cairo_set_source_rgb(cr, 0, 0, 0);
1336 draw_graph(g->iops_graph, cr);
1337 draw_graph(g->bandwidth_graph, cr);
1344 * Client specific ETA
1346 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1348 struct gfio_client *gc = client->client_data;
1349 struct gui_entry *ge = gc->ge;
1350 static int eta_good;
1357 gdk_threads_enter();
1362 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1363 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1364 eta_to_str(eta_str, je->eta_sec);
1367 sprintf(tmp, "%u", je->nr_running);
1368 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1369 sprintf(tmp, "%u", je->files_open);
1370 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1373 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1374 if (je->m_rate || je->t_rate) {
1377 mr = num2str(je->m_rate, 4, 0, i2p);
1378 tr = num2str(je->t_rate, 4, 0, i2p);
1379 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1380 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1383 } else if (je->m_iops || je->t_iops)
1384 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1386 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1387 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1388 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1389 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1392 if (je->eta_sec != INT_MAX && je->nr_running) {
1396 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1397 strcpy(output, "-.-% done");
1401 sprintf(output, "%3.1f%% done", perc);
1404 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1405 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1407 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1408 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1410 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1411 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1412 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1413 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1415 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1416 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1417 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1418 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1427 char *dst = output + strlen(output);
1429 sprintf(dst, " - %s", eta_str);
1432 gfio_update_thread_status(ge, output, perc);
1433 gdk_threads_leave();
1437 * Update ETA in main window for all clients
1439 static void gfio_update_all_eta(struct jobs_eta *je)
1441 struct gui *ui = &main_ui;
1442 static int eta_good;
1448 gdk_threads_enter();
1453 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1454 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1455 eta_to_str(eta_str, je->eta_sec);
1459 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1460 if (je->m_rate || je->t_rate) {
1463 mr = num2str(je->m_rate, 4, 0, i2p);
1464 tr = num2str(je->t_rate, 4, 0, i2p);
1465 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1466 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1469 } else if (je->m_iops || je->t_iops)
1470 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1472 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1473 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1474 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1475 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1478 entry_set_int_value(ui->eta.jobs, je->nr_running);
1480 if (je->eta_sec != INT_MAX && je->nr_running) {
1484 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1485 strcpy(output, "-.-% done");
1489 sprintf(output, "%3.1f%% done", perc);
1492 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1493 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1495 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1496 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1498 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1499 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1500 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1501 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1503 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1504 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1505 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1506 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
1515 char *dst = output + strlen(output);
1517 sprintf(dst, " - %s", eta_str);
1520 gfio_update_thread_status_all(output, perc);
1521 gdk_threads_leave();
1524 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1526 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1527 struct gfio_client *gc = client->client_data;
1528 struct gui_entry *ge = gc->ge;
1529 const char *os, *arch;
1532 os = fio_get_os_string(probe->os);
1536 arch = fio_get_arch_string(probe->arch);
1541 client->name = strdup((char *) probe->hostname);
1543 gdk_threads_enter();
1545 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1546 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1547 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1548 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1549 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1551 gfio_set_state(ge, GE_STATE_CONNECTED);
1553 gdk_threads_leave();
1556 static void gfio_update_thread_status(struct gui_entry *ge,
1557 char *status_message, double perc)
1559 static char message[100];
1560 const char *m = message;
1562 strncpy(message, status_message, sizeof(message) - 1);
1563 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1564 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1565 gtk_widget_queue_draw(main_ui.window);
1568 static void gfio_update_thread_status_all(char *status_message, double perc)
1570 struct gui *ui = &main_ui;
1571 static char message[100];
1572 const char *m = message;
1574 strncpy(message, status_message, sizeof(message) - 1);
1575 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1576 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1577 gtk_widget_queue_draw(ui->window);
1580 static void gfio_quit_op(struct fio_client *client)
1582 struct gfio_client *gc = client->client_data;
1584 gdk_threads_enter();
1585 gfio_set_state(gc->ge, GE_STATE_NEW);
1586 gdk_threads_leave();
1589 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1591 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1592 struct gfio_client *gc = client->client_data;
1593 struct thread_options *o = &gc->o;
1594 struct gui_entry *ge = gc->ge;
1597 convert_thread_options_to_cpu(o, &p->top);
1599 gdk_threads_enter();
1601 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1603 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1604 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1606 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1607 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
1609 sprintf(tmp, "%u", o->iodepth);
1610 multitext_add_entry(&ge->eta.iodepth, tmp);
1612 multitext_set_entry(&ge->eta.iotype, 0);
1613 multitext_set_entry(&ge->eta.ioengine, 0);
1614 multitext_set_entry(&ge->eta.iodepth, 0);
1618 gfio_set_state(ge, GE_STATE_JOB_SENT);
1620 gdk_threads_leave();
1623 static void gfio_client_timed_out(struct fio_client *client)
1625 struct gfio_client *gc = client->client_data;
1628 gdk_threads_enter();
1630 gfio_set_state(gc->ge, GE_STATE_NEW);
1631 clear_ge_ui_info(gc->ge);
1633 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1634 show_info_dialog(gc->ge->ui, "Network timeout", buf);
1636 gdk_threads_leave();
1639 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1641 struct gfio_client *gc = client->client_data;
1643 gdk_threads_enter();
1645 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
1648 entry_set_int_value(gc->err_entry, client->error);
1650 gdk_threads_leave();
1653 static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1655 struct gfio_client *gc = client->client_data;
1657 gdk_threads_enter();
1658 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1659 gdk_threads_leave();
1662 static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1664 struct gfio_client *gc = client->client_data;
1666 gdk_threads_enter();
1667 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1668 gdk_threads_leave();
1671 struct client_ops gfio_client_ops = {
1672 .text_op = gfio_text_op,
1673 .disk_util = gfio_disk_util_op,
1674 .thread_status = gfio_thread_status_op,
1675 .group_stats = gfio_group_stats_op,
1676 .jobs_eta = gfio_update_client_eta,
1677 .eta = gfio_update_all_eta,
1678 .probe = gfio_probe_op,
1679 .quit = gfio_quit_op,
1680 .add_job = gfio_add_job_op,
1681 .timed_out = gfio_client_timed_out,
1682 .stop = gfio_client_stop,
1683 .start = gfio_client_start,
1684 .job_start = gfio_client_job_start,
1685 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1686 .stay_connected = 1,
1689 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1690 __attribute__((unused)) gpointer data)
1695 static void *job_thread(void *arg)
1697 struct gui *ui = arg;
1699 ui->handler_running = 1;
1700 fio_handle_clients(&gfio_client_ops);
1701 ui->handler_running = 0;
1705 static int send_job_files(struct gui_entry *ge)
1707 struct gfio_client *gc = ge->client;
1710 for (i = 0; i < ge->nr_job_files; i++) {
1711 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1715 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1716 report_error(error);
1717 g_error_free(error);
1722 free(ge->job_files[i]);
1723 ge->job_files[i] = NULL;
1725 while (i < ge->nr_job_files) {
1726 free(ge->job_files[i]);
1727 ge->job_files[i] = NULL;
1731 ge->nr_job_files = 0;
1735 static void *server_thread(void *arg)
1738 gfio_server_running = 1;
1739 fio_start_server(NULL);
1740 gfio_server_running = 0;
1744 static void gfio_start_server(void)
1746 struct gui *ui = &main_ui;
1748 if (!gfio_server_running) {
1749 gfio_server_running = 1;
1750 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1751 pthread_detach(ui->server_t);
1755 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1758 struct gui_entry *ge = data;
1759 struct gfio_client *gc = ge->client;
1762 fio_start_client(gc->client);
1765 static void file_open(GtkWidget *w, gpointer data);
1767 static void connect_clicked(GtkWidget *widget, gpointer data)
1769 struct gui_entry *ge = data;
1770 struct gfio_client *gc = ge->client;
1772 if (ge->state == GE_STATE_NEW) {
1775 if (!ge->nr_job_files)
1776 file_open(widget, ge->ui);
1777 if (!ge->nr_job_files)
1780 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1781 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1782 ret = fio_client_connect(gc->client);
1784 if (!ge->ui->handler_running)
1785 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1786 gfio_set_state(ge, GE_STATE_CONNECTED);
1790 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1791 report_error(error);
1792 g_error_free(error);
1795 fio_client_terminate(gc->client);
1796 gfio_set_state(ge, GE_STATE_NEW);
1797 clear_ge_ui_info(ge);
1801 static void send_clicked(GtkWidget *widget, gpointer data)
1803 struct gui_entry *ge = data;
1805 if (send_job_files(ge)) {
1808 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);
1809 report_error(error);
1810 g_error_free(error);
1812 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1816 static GtkWidget *add_button(GtkWidget *buttonbox,
1817 struct button_spec *buttonspec, gpointer data)
1819 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1821 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1822 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1823 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1824 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1829 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1834 for (i = 0; i < nbuttons; i++)
1835 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1838 static void on_info_bar_response(GtkWidget *widget, gint response,
1841 struct gui *ui = &main_ui;
1843 if (response == GTK_RESPONSE_OK) {
1844 gtk_widget_destroy(widget);
1845 ui->error_info_bar = NULL;
1849 void report_error(GError *error)
1851 struct gui *ui = &main_ui;
1853 if (ui->error_info_bar == NULL) {
1854 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1857 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1858 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1861 ui->error_label = gtk_label_new(error->message);
1862 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1863 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1865 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1866 gtk_widget_show_all(ui->vbox);
1869 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1870 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1874 struct connection_widgets
1881 static void hostname_cb(GtkEntry *entry, gpointer data)
1883 struct connection_widgets *cw = data;
1884 int uses_net = 0, is_localhost = 0;
1889 * Check whether to display the 'auto start backend' box
1890 * or not. Show it if we are a localhost and using network,
1891 * or using a socket.
1893 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1894 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1899 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1900 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1901 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1902 !strcmp(text, "ip6-loopback"))
1906 if (!uses_net || is_localhost) {
1907 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1908 gtk_widget_set_sensitive(cw->button, 1);
1910 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1911 gtk_widget_set_sensitive(cw->button, 0);
1915 static int get_connection_details(char **host, int *port, int *type,
1918 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1919 struct connection_widgets cw;
1922 dialog = gtk_dialog_new_with_buttons("Connection details",
1923 GTK_WINDOW(main_ui.window),
1924 GTK_DIALOG_DESTROY_WITH_PARENT,
1925 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1926 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1928 frame = gtk_frame_new("Hostname / socket name");
1929 /* gtk_dialog_get_content_area() is 2.14 and newer */
1930 vbox = GTK_DIALOG(dialog)->vbox;
1931 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1933 box = gtk_vbox_new(FALSE, 6);
1934 gtk_container_add(GTK_CONTAINER(frame), box);
1936 hbox = gtk_hbox_new(TRUE, 10);
1937 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1938 cw.hentry = gtk_entry_new();
1939 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1940 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1942 frame = gtk_frame_new("Port");
1943 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1944 box = gtk_vbox_new(FALSE, 10);
1945 gtk_container_add(GTK_CONTAINER(frame), box);
1947 hbox = gtk_hbox_new(TRUE, 4);
1948 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1949 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1951 frame = gtk_frame_new("Type");
1952 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1953 box = gtk_vbox_new(FALSE, 10);
1954 gtk_container_add(GTK_CONTAINER(frame), box);
1956 hbox = gtk_hbox_new(TRUE, 4);
1957 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1959 cw.combo = gtk_combo_box_new_text();
1960 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1961 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1962 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1963 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1965 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1967 frame = gtk_frame_new("Options");
1968 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1969 box = gtk_vbox_new(FALSE, 10);
1970 gtk_container_add(GTK_CONTAINER(frame), box);
1972 hbox = gtk_hbox_new(TRUE, 4);
1973 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1975 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1976 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1977 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.");
1978 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1981 * Connect edit signal, so we can show/not-show the auto start button
1983 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1984 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1986 gtk_widget_show_all(dialog);
1988 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1989 gtk_widget_destroy(dialog);
1993 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1994 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1996 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1997 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1998 *type = Fio_client_ipv4;
1999 else if (!strncmp(typeentry, "IPv6", 4))
2000 *type = Fio_client_ipv6;
2002 *type = Fio_client_socket;
2005 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
2007 gtk_widget_destroy(dialog);
2011 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
2013 struct gfio_client *gc;
2015 gc = malloc(sizeof(*gc));
2016 memset(gc, 0, sizeof(*gc));
2018 gc->client = fio_get_client(client);
2022 client->client_data = gc;
2025 static GtkWidget *new_client_page(struct gui_entry *ge);
2027 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2029 struct gui_entry *ge;
2031 ge = malloc(sizeof(*ge));
2032 memset(ge, 0, sizeof(*ge));
2033 ge->state = GE_STATE_NEW;
2034 INIT_FLIST_HEAD(&ge->list);
2035 flist_add_tail(&ge->list, &ui->list);
2041 * FIXME: need more handling here
2043 static void ge_destroy(GtkWidget *w, gpointer data)
2045 struct gui_entry *ge = data;
2046 struct gfio_client *gc = ge->client;
2048 if (gc && gc->client) {
2049 if (ge->state >= GE_STATE_CONNECTED)
2050 fio_client_terminate(gc->client);
2052 fio_put_client(gc->client);
2055 flist_del(&ge->list);
2059 static struct gui_entry *get_new_ge_with_tab(const char *name)
2061 struct gui_entry *ge;
2063 ge = alloc_new_gui_entry(&main_ui);
2065 ge->vbox = new_client_page(ge);
2066 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
2068 ge->page_label = gtk_label_new(name);
2069 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2071 gtk_widget_show_all(main_ui.window);
2075 static void file_new(GtkWidget *w, gpointer data)
2077 struct gui *ui = (struct gui *) data;
2078 struct gui_entry *ge;
2080 ge = get_new_ge_with_tab("Untitled");
2081 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2085 * Return the 'ge' corresponding to the tab. If the active tab is the
2086 * main tab, open a new tab.
2088 static struct gui_entry *get_ge_from_page(gint cur_page)
2090 struct flist_head *entry;
2091 struct gui_entry *ge;
2094 return get_new_ge_with_tab("Untitled");
2096 flist_for_each(entry, &main_ui.list) {
2097 ge = flist_entry(entry, struct gui_entry, list);
2098 if (ge->page_num == cur_page)
2105 static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2110 * Main tab is tab 0, so any current page other than 0 holds
2113 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2115 return get_ge_from_page(cur_page);
2120 static void file_close(GtkWidget *w, gpointer data)
2122 struct gui *ui = (struct gui *) data;
2123 struct gui_entry *ge;
2126 * Can't close the main tab
2128 ge = get_ge_from_cur_tab(ui);
2130 gtk_widget_destroy(ge->vbox);
2134 if (!flist_empty(&ui->list)) {
2135 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2142 static void file_open(GtkWidget *w, gpointer data)
2144 struct gui *ui = data;
2146 GSList *filenames, *fn_glist;
2147 GtkFileFilter *filter;
2149 int port, type, server_start;
2150 struct gui_entry *ge;
2154 * Creates new tab if current tab is the main window, or the
2155 * current tab already has a client.
2157 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2158 ge = get_ge_from_page(cur_page);
2160 ge = get_new_ge_with_tab("Untitled");
2162 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
2164 dialog = gtk_file_chooser_dialog_new("Open File",
2165 GTK_WINDOW(ui->window),
2166 GTK_FILE_CHOOSER_ACTION_OPEN,
2167 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2168 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2170 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2172 filter = gtk_file_filter_new();
2173 gtk_file_filter_add_pattern(filter, "*.fio");
2174 gtk_file_filter_add_pattern(filter, "*.job");
2175 gtk_file_filter_add_pattern(filter, "*.ini");
2176 gtk_file_filter_add_mime_type(filter, "text/fio");
2177 gtk_file_filter_set_name(filter, "Fio job file");
2178 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2180 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2181 gtk_widget_destroy(dialog);
2185 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
2187 gtk_widget_destroy(dialog);
2189 if (get_connection_details(&host, &port, &type, &server_start))
2192 filenames = fn_glist;
2193 while (filenames != NULL) {
2194 struct fio_client *client;
2196 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2197 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2200 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2204 error = g_error_new(g_quark_from_string("fio"), 1,
2205 "Failed to add client %s", host);
2206 report_error(error);
2207 g_error_free(error);
2209 gfio_client_added(ge, client);
2211 g_free(filenames->data);
2212 filenames = g_slist_next(filenames);
2217 gfio_start_server();
2219 g_slist_free(fn_glist);
2222 static void file_save(GtkWidget *w, gpointer data)
2224 struct gui *ui = data;
2227 dialog = gtk_file_chooser_dialog_new("Save File",
2228 GTK_WINDOW(ui->window),
2229 GTK_FILE_CHOOSER_ACTION_SAVE,
2230 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2231 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2234 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2235 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2237 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2240 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2241 // save_job_file(filename);
2244 gtk_widget_destroy(dialog);
2247 static void view_log_destroy(GtkWidget *w, gpointer data)
2249 struct gui *ui = (struct gui *) data;
2251 gtk_widget_ref(ui->log_tree);
2252 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2253 gtk_widget_destroy(w);
2254 ui->log_view = NULL;
2257 static void view_log(GtkWidget *w, gpointer data)
2259 GtkWidget *win, *scroll, *vbox, *box;
2260 struct gui *ui = (struct gui *) data;
2265 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2266 gtk_window_set_title(GTK_WINDOW(win), "Log");
2267 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2269 scroll = gtk_scrolled_window_new(NULL, NULL);
2271 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2273 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2275 box = gtk_hbox_new(TRUE, 0);
2276 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2277 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2278 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2280 vbox = gtk_vbox_new(TRUE, 5);
2281 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2283 gtk_container_add(GTK_CONTAINER(win), vbox);
2284 gtk_widget_show_all(win);
2287 static void connect_job_entry(GtkWidget *w, gpointer data)
2289 struct gui *ui = (struct gui *) data;
2290 struct gui_entry *ge;
2292 ge = get_ge_from_cur_tab(ui);
2294 connect_clicked(w, ge);
2297 static void send_job_entry(GtkWidget *w, gpointer data)
2299 struct gui *ui = (struct gui *) data;
2300 struct gui_entry *ge;
2302 ge = get_ge_from_cur_tab(ui);
2304 send_clicked(w, ge);
2308 static void edit_job_entry(GtkWidget *w, gpointer data)
2312 static void start_job_entry(GtkWidget *w, gpointer data)
2314 struct gui *ui = (struct gui *) data;
2315 struct gui_entry *ge;
2317 ge = get_ge_from_cur_tab(ui);
2319 start_job_clicked(w, ge);
2322 static void __update_graph_limits(struct gfio_graphs *g)
2324 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2325 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2328 static void update_graph_limits(void)
2330 struct flist_head *entry;
2331 struct gui_entry *ge;
2333 __update_graph_limits(&main_ui.graphs);
2335 flist_for_each(entry, &main_ui.list) {
2336 ge = flist_entry(entry, struct gui_entry, list);
2337 __update_graph_limits(&ge->graphs);
2341 static void preferences(GtkWidget *w, gpointer data)
2343 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2344 GtkWidget *hbox, *spin, *entry, *spin_int;
2347 dialog = gtk_dialog_new_with_buttons("Preferences",
2348 GTK_WINDOW(main_ui.window),
2349 GTK_DIALOG_DESTROY_WITH_PARENT,
2350 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2351 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2354 frame = gtk_frame_new("Graphing");
2355 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2356 vbox = gtk_vbox_new(FALSE, 6);
2357 gtk_container_add(GTK_CONTAINER(frame), vbox);
2359 hbox = gtk_hbox_new(FALSE, 5);
2360 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2361 entry = gtk_label_new("Font face to use for graph labels");
2362 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2364 font = gtk_font_button_new();
2365 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2367 box = gtk_vbox_new(FALSE, 6);
2368 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2370 hbox = gtk_hbox_new(FALSE, 5);
2371 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2372 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2373 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2375 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2377 box = gtk_vbox_new(FALSE, 6);
2378 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2380 hbox = gtk_hbox_new(FALSE, 5);
2381 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2382 entry = gtk_label_new("Client ETA request interval (msec)");
2383 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2385 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2386 frame = gtk_frame_new("Debug logging");
2387 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2388 vbox = gtk_vbox_new(FALSE, 6);
2389 gtk_container_add(GTK_CONTAINER(frame), vbox);
2391 box = gtk_hbox_new(FALSE, 6);
2392 gtk_container_add(GTK_CONTAINER(vbox), box);
2394 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2396 for (i = 0; i < FD_DEBUG_MAX; i++) {
2398 box = gtk_hbox_new(FALSE, 6);
2399 gtk_container_add(GTK_CONTAINER(vbox), box);
2403 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2404 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2405 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2408 gtk_widget_show_all(dialog);
2410 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2411 gtk_widget_destroy(dialog);
2415 for (i = 0; i < FD_DEBUG_MAX; i++) {
2418 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2420 fio_debug |= (1UL << i);
2423 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2424 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2425 update_graph_limits();
2426 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2428 gtk_widget_destroy(dialog);
2431 static void about_dialog(GtkWidget *w, gpointer data)
2433 const char *authors[] = {
2434 "Jens Axboe <axboe@kernel.dk>",
2435 "Stephen Carmeron <stephenmcameron@gmail.com>",
2438 const char *license[] = {
2439 "Fio is free software; you can redistribute it and/or modify "
2440 "it under the terms of the GNU General Public License as published by "
2441 "the Free Software Foundation; either version 2 of the License, or "
2442 "(at your option) any later version.\n",
2443 "Fio is distributed in the hope that it will be useful, "
2444 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2445 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2446 "GNU General Public License for more details.\n",
2447 "You should have received a copy of the GNU General Public License "
2448 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2449 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2451 char *license_trans;
2453 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2454 license[2], "\n", NULL);
2456 gtk_show_about_dialog(NULL,
2457 "program-name", "gfio",
2458 "comments", "Gtk2 UI for fio",
2459 "license", license_trans,
2460 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2462 "version", fio_version_string,
2463 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2464 "logo-icon-name", "fio",
2466 "wrap-license", TRUE,
2469 g_free(license_trans);
2472 static GtkActionEntry menu_items[] = {
2473 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2474 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2475 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
2476 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2477 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2478 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
2479 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2480 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2481 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2482 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2483 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2484 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2485 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2486 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
2487 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2488 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2490 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2492 static const gchar *ui_string = " \
2494 <menubar name=\"MainMenu\"> \
2495 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2496 <menuitem name=\"New\" action=\"NewFile\" /> \
2497 <menuitem name=\"Close\" action=\"CloseFile\" /> \
2498 <separator name=\"Separator1\"/> \
2499 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2500 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2501 <separator name=\"Separator2\"/> \
2502 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2503 <separator name=\"Separator3\"/> \
2504 <placeholder name=\"FileRecentFiles\"/> \
2505 <separator name=\"Separator4\"/> \
2506 <menuitem name=\"Quit\" action=\"Quit\" /> \
2508 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2509 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
2510 <separator name=\"Separator5\"/> \
2511 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2512 <menuitem name=\"Send job\" action=\"SendJob\" /> \
2513 <separator name=\"Separator6\"/> \
2514 <menuitem name=\"Start job\" action=\"StartJob\" /> \
2516 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2517 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2519 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2520 <menuitem name=\"About\" action=\"About\" /> \
2526 static void set_job_menu_visible(struct gui *ui, int visible)
2530 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2531 gtk_widget_set_sensitive(job, visible);
2534 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2537 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2540 action_group = gtk_action_group_new("Menu");
2541 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2543 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2544 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2546 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2548 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2551 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2552 GtkWidget *vbox, GtkUIManager *ui_manager)
2554 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2557 static void combo_entry_changed(GtkComboBox *box, gpointer data)
2559 struct gui_entry *ge = (struct gui_entry *) data;
2562 index = gtk_combo_box_get_active(box);
2564 multitext_set_entry(&ge->eta.iotype, index);
2565 multitext_set_entry(&ge->eta.ioengine, index);
2566 multitext_set_entry(&ge->eta.iodepth, index);
2569 static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2571 struct gui_entry *ge = (struct gui_entry *) data;
2573 multitext_free(&ge->eta.iotype);
2574 multitext_free(&ge->eta.ioengine);
2575 multitext_free(&ge->eta.iodepth);
2578 static GtkWidget *new_client_page(struct gui_entry *ge)
2580 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2581 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2584 main_vbox = gtk_vbox_new(FALSE, 3);
2586 top_align = gtk_alignment_new(0, 0, 1, 0);
2587 top_vbox = gtk_vbox_new(FALSE, 3);
2588 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2589 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2591 probe = gtk_frame_new("Job");
2592 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2593 probe_frame = gtk_vbox_new(FALSE, 3);
2594 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2596 probe_box = gtk_hbox_new(FALSE, 3);
2597 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2598 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2599 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2600 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2601 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2603 probe_box = gtk_hbox_new(FALSE, 3);
2604 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2606 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2607 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2608 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2609 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2610 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2611 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
2612 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2613 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2615 probe_box = gtk_hbox_new(FALSE, 3);
2616 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2617 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2618 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2619 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2620 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2623 * Only add this if we have a commit rate
2626 probe_box = gtk_hbox_new(FALSE, 3);
2627 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2629 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2630 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2632 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2633 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2637 * Set up a drawing area and IOPS and bandwidth graphs
2639 gdk_color_parse("white", &white);
2640 ge->graphs.drawing_area = gtk_drawing_area_new();
2641 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2642 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2643 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2644 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2645 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2646 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2647 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2648 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2649 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2650 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2651 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2652 ge->graphs.drawing_area);
2653 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
2655 setup_graphs(&ge->graphs);
2658 * Set up alignments for widgets at the bottom of ui,
2659 * align bottom left, expand horizontally but not vertically
2661 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2662 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2663 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2664 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2666 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2669 * Set up thread status progress bar
2671 ge->thread_status_pb = gtk_progress_bar_new();
2672 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2673 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2674 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2680 static GtkWidget *new_main_page(struct gui *ui)
2682 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2683 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
2686 main_vbox = gtk_vbox_new(FALSE, 3);
2689 * Set up alignments for widgets at the top of ui,
2690 * align top left, expand horizontally but not vertically
2692 top_align = gtk_alignment_new(0, 0, 1, 0);
2693 top_vbox = gtk_vbox_new(FALSE, 0);
2694 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2695 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
2697 probe = gtk_frame_new("Run statistics");
2698 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2699 probe_frame = gtk_vbox_new(FALSE, 3);
2700 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2702 probe_box = gtk_hbox_new(FALSE, 3);
2703 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2704 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2705 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2706 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2707 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2708 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2711 * Only add this if we have a commit rate
2714 probe_box = gtk_hbox_new(FALSE, 3);
2715 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2717 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2718 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2720 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2721 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2725 * Set up a drawing area and IOPS and bandwidth graphs
2727 gdk_color_parse("white", &white);
2728 ui->graphs.drawing_area = gtk_drawing_area_new();
2729 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2730 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2731 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2732 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2733 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2734 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2735 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2736 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2737 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
2738 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2739 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
2740 ui->graphs.drawing_area);
2741 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
2744 setup_graphs(&ui->graphs);
2747 * Set up alignments for widgets at the bottom of ui,
2748 * align bottom left, expand horizontally but not vertically
2750 bottom_align = gtk_alignment_new(0, 1, 1, 0);
2751 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2752 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2753 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
2756 * Set up thread status progress bar
2758 ui->thread_status_pb = gtk_progress_bar_new();
2759 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2760 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2761 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2766 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2767 guint page, gpointer data)
2770 struct gui *ui = (struct gui *) data;
2771 struct gui_entry *ge;
2774 set_job_menu_visible(ui, 0);
2778 set_job_menu_visible(ui, 1);
2779 ge = get_ge_from_page(page);
2781 update_button_states(ui, ge);
2786 static void init_ui(int *argc, char **argv[], struct gui *ui)
2788 GtkSettings *settings;
2791 /* Magical g*thread incantation, you just need this thread stuff.
2792 * Without it, the update that happens in gfio_update_thread_status
2793 * doesn't really happen in a timely fashion, you need expose events
2795 if (!g_thread_supported())
2796 g_thread_init(NULL);
2799 gtk_init(argc, argv);
2800 settings = gtk_settings_get_default();
2801 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2804 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2805 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2806 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2808 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2809 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2811 ui->vbox = gtk_vbox_new(FALSE, 0);
2812 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2814 ui->uimanager = gtk_ui_manager_new();
2815 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
2816 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
2818 ui->notebook = gtk_notebook_new();
2819 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2820 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2821 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2822 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2824 vbox = new_main_page(ui);
2826 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2828 gfio_ui_setup_log(ui);
2830 gtk_widget_show_all(ui->window);
2833 int main(int argc, char *argv[], char *envp[])
2835 if (initialize_fio(envp))
2837 if (fio_init_options())
2840 memset(&main_ui, 0, sizeof(main_ui));
2841 INIT_FLIST_HEAD(&main_ui.list);
2843 init_ui(&argc, &argv, &main_ui);
2845 gdk_threads_enter();
2847 gdk_threads_leave();