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 },
83 GtkWidget *write_iops;
89 #define DRAWING_AREA_XDIM 1000
90 #define DRAWING_AREA_YDIM 400
91 GtkWidget *drawing_area;
92 struct graph *iops_graph;
93 struct graph *bandwidth_graph;
97 * Main window widgets and data
104 GtkWidget *bottomalign;
105 GtkWidget *thread_status_pb;
106 GtkWidget *buttonbox;
107 GtkWidget *scrolled_window;
109 GtkWidget *error_info_bar;
110 GtkWidget *error_label;
111 GtkListStore *log_model;
114 struct gfio_graphs graphs;
115 struct probe_widget probe;
116 struct eta_widget eta;
122 struct flist_head list;
129 struct flist_head list;
135 GtkWidget *bottomalign;
136 GtkWidget *thread_status_pb;
137 GtkWidget *buttonbox;
138 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
139 GtkWidget *scrolled_window;
141 GtkWidget *error_info_bar;
142 GtkWidget *error_label;
143 GtkWidget *results_notebook;
144 GtkWidget *results_window;
145 GtkListStore *log_model;
148 struct gfio_graphs graphs;
149 struct probe_widget probe;
150 struct eta_widget eta;
151 GtkWidget *page_label;
155 struct gfio_client *client;
161 struct gui_entry *ge;
162 struct fio_client *client;
163 GtkWidget *results_widget;
164 GtkWidget *disk_util_frame;
165 GtkWidget *err_entry;
166 unsigned int job_added;
167 struct thread_options o;
170 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
171 static void gfio_update_thread_status_all(char *status_message, double perc);
172 void report_error(GError *error);
174 static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
176 switch (power_of_ten) {
177 case 9: graph_y_title(g, "Billions of IOs / sec");
179 case 6: graph_y_title(g, "Millions of IOs / sec");
181 case 3: graph_y_title(g, "Thousands of IOs / sec");
184 default: graph_y_title(g, "IOs / sec");
189 static struct graph *setup_iops_graph(void)
193 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
194 graph_title(g, "IOPS");
195 graph_x_title(g, "Time (secs)");
196 graph_y_title(g, "IOs / sec");
197 graph_add_label(g, "Read IOPS");
198 graph_add_label(g, "Write IOPS");
199 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
200 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
201 line_graph_set_data_count_limit(g, gfio_graph_limit);
202 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
206 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
208 switch (power_of_ten) {
209 case 9: graph_y_title(g, "Petabytes / sec");
211 case 6: graph_y_title(g, "Gigabytes / sec");
213 case 3: graph_y_title(g, "Megabytes / sec");
216 default: graph_y_title(g, "Kilobytes / sec");
221 static struct graph *setup_bandwidth_graph(void)
225 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
226 graph_title(g, "Bandwidth");
227 graph_x_title(g, "Time (secs)");
228 graph_y_title(g, "Kbytes / sec");
229 graph_add_label(g, "Read Bandwidth");
230 graph_add_label(g, "Write Bandwidth");
231 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
232 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
233 line_graph_set_data_count_limit(g, 100);
234 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
238 static void setup_graphs(struct gfio_graphs *g)
240 g->iops_graph = setup_iops_graph();
241 g->bandwidth_graph = setup_bandwidth_graph();
244 static void clear_ge_ui_info(struct gui_entry *ge)
246 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
247 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
248 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
249 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
251 /* should we empty it... */
252 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
254 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
255 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
256 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
257 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
258 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
259 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
260 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
261 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
262 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
265 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
267 GtkWidget *entry, *frame;
269 frame = gtk_frame_new(label);
270 entry = gtk_combo_box_new_text();
271 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
272 gtk_container_add(GTK_CONTAINER(frame), entry);
277 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
279 GtkWidget *entry, *frame;
281 frame = gtk_frame_new(label);
282 entry = gtk_entry_new();
283 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
284 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
285 gtk_container_add(GTK_CONTAINER(frame), entry);
290 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
292 GtkWidget *label_widget;
295 frame = gtk_frame_new(label);
296 label_widget = gtk_label_new(NULL);
297 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
298 gtk_container_add(GTK_CONTAINER(frame), label_widget);
303 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
305 GtkWidget *button, *box;
307 box = gtk_hbox_new(FALSE, 3);
308 gtk_container_add(GTK_CONTAINER(hbox), box);
310 button = gtk_spin_button_new_with_range(min, max, 1.0);
311 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
313 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
314 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
319 static void gfio_set_connected(struct gui_entry *ge, int connected)
322 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
324 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
325 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
328 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
329 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
330 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
331 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
335 static void label_set_int_value(GtkWidget *entry, unsigned int val)
339 sprintf(tmp, "%u", val);
340 gtk_label_set_text(GTK_LABEL(entry), tmp);
343 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
347 sprintf(tmp, "%u", val);
348 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
352 #define ALIGN_RIGHT 2
356 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
358 GtkCellRenderer *renderer;
359 GtkTreeViewColumn *col;
360 double xalign = 0.0; /* left as default */
361 PangoAlignment align;
364 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
365 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
367 visible = !(flags & INVISIBLE);
369 renderer = gtk_cell_renderer_text_new();
370 col = gtk_tree_view_column_new();
372 gtk_tree_view_column_set_title(col, title);
373 if (!(flags & UNSORTABLE))
374 gtk_tree_view_column_set_sort_column_id(col, index);
375 gtk_tree_view_column_set_resizable(col, TRUE);
376 gtk_tree_view_column_pack_start(col, renderer, TRUE);
377 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
378 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
380 case PANGO_ALIGN_LEFT:
383 case PANGO_ALIGN_CENTER:
386 case PANGO_ALIGN_RIGHT:
390 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
391 gtk_tree_view_column_set_visible(col, visible);
392 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
396 static void gfio_ui_setup_log(struct gui *ui)
398 GtkTreeSelection *selection;
400 GtkWidget *tree_view;
402 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
404 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
405 gtk_widget_set_can_focus(tree_view, FALSE);
407 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
408 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
409 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
410 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
412 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
413 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
414 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
415 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
417 ui->log_model = model;
418 ui->log_tree = tree_view;
421 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
427 GType types[FIO_IO_U_LIST_MAX_LEN];
428 GtkWidget *tree_view;
429 GtkTreeSelection *selection;
434 for (i = 0; i < len; i++)
435 types[i] = G_TYPE_INT;
437 model = gtk_list_store_newv(len, types);
439 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
440 gtk_widget_set_can_focus(tree_view, FALSE);
442 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
443 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
445 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
446 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
448 for (i = 0; i < len; i++) {
451 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
452 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
455 gtk_list_store_append(model, &iter);
457 for (i = 0; i < len; i++) {
459 ovals[i] = (ovals[i] + 999) / 1000;
460 gtk_list_store_set(model, &iter, i, ovals[i], -1);
466 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
469 unsigned int *io_u_plat = ts->io_u_plat[ddir];
470 unsigned long nr = ts->clat_stat[ddir].samples;
471 fio_fp64_t *plist = ts->percentile_list;
472 unsigned int *ovals, len, minv, maxv, scale_down;
474 GtkWidget *tree_view, *frame, *hbox;
477 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
482 * We default to usecs, but if the value range is such that we
483 * should scale down to msecs, do that.
485 if (minv > 2000 && maxv > 99999) {
493 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
495 sprintf(tmp, "Completion percentiles (%s)", base);
496 frame = gtk_frame_new(tmp);
497 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
499 hbox = gtk_hbox_new(FALSE, 3);
500 gtk_container_add(GTK_CONTAINER(frame), hbox);
502 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
508 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
509 unsigned long max, double mean, double dev)
511 const char *base = "(usec)";
512 GtkWidget *hbox, *label, *frame;
516 if (!usec_to_msec(&min, &max, &mean, &dev))
519 minp = num2str(min, 6, 1, 0);
520 maxp = num2str(max, 6, 1, 0);
522 sprintf(tmp, "%s %s", name, base);
523 frame = gtk_frame_new(tmp);
524 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
526 hbox = gtk_hbox_new(FALSE, 3);
527 gtk_container_add(GTK_CONTAINER(frame), hbox);
529 label = new_info_label_in_frame(hbox, "Minimum");
530 gtk_label_set_text(GTK_LABEL(label), minp);
531 label = new_info_label_in_frame(hbox, "Maximum");
532 gtk_label_set_text(GTK_LABEL(label), maxp);
533 label = new_info_label_in_frame(hbox, "Average");
534 sprintf(tmp, "%5.02f", mean);
535 gtk_label_set_text(GTK_LABEL(label), tmp);
536 label = new_info_label_in_frame(hbox, "Standard deviation");
537 sprintf(tmp, "%5.02f", dev);
538 gtk_label_set_text(GTK_LABEL(label), tmp);
549 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
550 struct thread_stat *ts, int ddir)
552 const char *ddir_label[2] = { "Read", "Write" };
553 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
554 unsigned long min[3], max[3], runt;
555 unsigned long long bw, iops;
556 unsigned int flags = 0;
557 double mean[3], dev[3];
558 char *io_p, *bw_p, *iops_p;
561 if (!ts->runtime[ddir])
564 i2p = is_power_of_2(rs->kb_base);
565 runt = ts->runtime[ddir];
567 bw = (1000 * ts->io_bytes[ddir]) / runt;
568 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
569 bw_p = num2str(bw, 6, 1, i2p);
571 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
572 iops_p = num2str(iops, 6, 1, 0);
574 box = gtk_hbox_new(FALSE, 3);
575 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
577 frame = gtk_frame_new(ddir_label[ddir]);
578 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
580 main_vbox = gtk_vbox_new(FALSE, 3);
581 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
583 box = gtk_hbox_new(FALSE, 3);
584 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
586 label = new_info_label_in_frame(box, "IO");
587 gtk_label_set_text(GTK_LABEL(label), io_p);
588 label = new_info_label_in_frame(box, "Bandwidth");
589 gtk_label_set_text(GTK_LABEL(label), bw_p);
590 label = new_info_label_in_frame(box, "IOPS");
591 gtk_label_set_text(GTK_LABEL(label), iops_p);
592 label = new_info_label_in_frame(box, "Runtime (msec)");
593 label_set_int_value(label, ts->runtime[ddir]);
595 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
596 double p_of_agg = 100.0;
597 const char *bw_str = "KB";
601 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
602 if (p_of_agg > 100.0)
606 if (mean[0] > 999999.9) {
614 sprintf(tmp, "Bandwidth (%s)", bw_str);
615 frame = gtk_frame_new(tmp);
616 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
618 box = gtk_hbox_new(FALSE, 3);
619 gtk_container_add(GTK_CONTAINER(frame), box);
621 label = new_info_label_in_frame(box, "Minimum");
622 label_set_int_value(label, min[0]);
623 label = new_info_label_in_frame(box, "Maximum");
624 label_set_int_value(label, max[0]);
625 label = new_info_label_in_frame(box, "Percentage of jobs");
626 sprintf(tmp, "%3.2f%%", p_of_agg);
627 gtk_label_set_text(GTK_LABEL(label), tmp);
628 label = new_info_label_in_frame(box, "Average");
629 sprintf(tmp, "%5.02f", mean[0]);
630 gtk_label_set_text(GTK_LABEL(label), tmp);
631 label = new_info_label_in_frame(box, "Standard deviation");
632 sprintf(tmp, "%5.02f", dev[0]);
633 gtk_label_set_text(GTK_LABEL(label), tmp);
636 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
638 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
640 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
644 frame = gtk_frame_new("Latency");
645 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
647 vbox = gtk_vbox_new(FALSE, 3);
648 gtk_container_add(GTK_CONTAINER(frame), vbox);
650 if (flags & GFIO_SLAT)
651 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
652 if (flags & GFIO_CLAT)
653 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
654 if (flags & GFIO_LAT)
655 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
658 if (ts->clat_percentiles)
659 gfio_show_clat_percentiles(main_vbox, ts, ddir);
667 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
670 GtkWidget *tree_view;
671 GtkTreeSelection *selection;
678 * Check if all are empty, in which case don't bother
680 for (i = 0, skipped = 0; i < num; i++)
687 types = malloc(num * sizeof(GType));
689 for (i = 0; i < num; i++)
690 types[i] = G_TYPE_STRING;
692 model = gtk_list_store_newv(num, types);
696 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
697 gtk_widget_set_can_focus(tree_view, FALSE);
699 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
700 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
702 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
703 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
705 for (i = 0; i < num; i++)
706 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
708 gtk_list_store_append(model, &iter);
710 for (i = 0; i < num; i++) {
714 sprintf(fbuf, "0.00");
716 sprintf(fbuf, "%3.2f%%", lat[i]);
718 gtk_list_store_set(model, &iter, i, fbuf, -1);
724 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
726 GtkWidget *box, *frame, *tree_view;
727 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
728 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
729 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
730 "250", "500", "750", "1000", };
731 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
732 "250", "500", "750", "1000", "2000",
735 stat_calc_lat_u(ts, io_u_lat_u);
736 stat_calc_lat_m(ts, io_u_lat_m);
738 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
740 frame = gtk_frame_new("Latency buckets (usec)");
741 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
743 box = gtk_hbox_new(FALSE, 3);
744 gtk_container_add(GTK_CONTAINER(frame), box);
745 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
748 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
750 frame = gtk_frame_new("Latency buckets (msec)");
751 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
753 box = gtk_hbox_new(FALSE, 3);
754 gtk_container_add(GTK_CONTAINER(frame), box);
755 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
759 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
761 GtkWidget *box, *frame, *entry;
762 double usr_cpu, sys_cpu;
763 unsigned long runtime;
766 runtime = ts->total_run_time;
768 double runt = (double) runtime;
770 usr_cpu = (double) ts->usr_time * 100 / runt;
771 sys_cpu = (double) ts->sys_time * 100 / runt;
777 frame = gtk_frame_new("OS resources");
778 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
780 box = gtk_hbox_new(FALSE, 3);
781 gtk_container_add(GTK_CONTAINER(frame), box);
783 entry = new_info_entry_in_frame(box, "User CPU");
784 sprintf(tmp, "%3.2f%%", usr_cpu);
785 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
786 entry = new_info_entry_in_frame(box, "System CPU");
787 sprintf(tmp, "%3.2f%%", sys_cpu);
788 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
789 entry = new_info_entry_in_frame(box, "Context switches");
790 entry_set_int_value(entry, ts->ctx);
791 entry = new_info_entry_in_frame(box, "Major faults");
792 entry_set_int_value(entry, ts->majf);
793 entry = new_info_entry_in_frame(box, "Minor faults");
794 entry_set_int_value(entry, ts->minf);
796 static void gfio_add_sc_depths_tree(GtkListStore *model,
797 struct thread_stat *ts, unsigned int len,
800 double io_u_dist[FIO_IO_U_MAP_NR];
802 /* Bits 0, and 3-8 */
803 const int add_mask = 0x1f9;
807 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
809 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
811 gtk_list_store_append(model, &iter);
813 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
815 for (i = 1, j = 0; i < len; i++) {
818 if (!(add_mask & (1UL << (i - 1))))
819 sprintf(fbuf, "0.0%%");
821 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
825 gtk_list_store_set(model, &iter, i, fbuf, -1);
830 static void gfio_add_total_depths_tree(GtkListStore *model,
831 struct thread_stat *ts, unsigned int len)
833 double io_u_dist[FIO_IO_U_MAP_NR];
835 /* Bits 1-6, and 8 */
836 const int add_mask = 0x17e;
839 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
841 gtk_list_store_append(model, &iter);
843 gtk_list_store_set(model, &iter, 0, "Total", -1);
845 for (i = 1, j = 0; i < len; i++) {
848 if (!(add_mask & (1UL << (i - 1))))
849 sprintf(fbuf, "0.0%%");
851 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
855 gtk_list_store_set(model, &iter, i, fbuf, -1);
860 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
862 GtkWidget *frame, *box, *tree_view;
863 GtkTreeSelection *selection;
865 GType types[FIO_IO_U_MAP_NR + 1];
868 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
870 frame = gtk_frame_new("IO depths");
871 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
873 box = gtk_hbox_new(FALSE, 3);
874 gtk_container_add(GTK_CONTAINER(frame), box);
876 for (i = 0; i < NR_LABELS; i++)
877 types[i] = G_TYPE_STRING;
879 model = gtk_list_store_newv(NR_LABELS, types);
881 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
882 gtk_widget_set_can_focus(tree_view, FALSE);
884 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
885 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
887 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
888 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
890 for (i = 0; i < NR_LABELS; i++)
891 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
893 gfio_add_total_depths_tree(model, ts, NR_LABELS);
894 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
895 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
897 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
900 static gboolean results_window_delete(GtkWidget *w, gpointer data)
902 struct gui_entry *ge = (struct gui_entry *) data;
904 gtk_widget_destroy(w);
905 ge->results_window = NULL;
906 ge->results_notebook = NULL;
910 static GtkWidget *get_results_window(struct gui_entry *ge)
912 GtkWidget *win, *notebook;
914 if (ge->results_window)
915 return ge->results_notebook;
917 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
918 gtk_window_set_title(GTK_WINDOW(win), "Results");
919 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
920 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
921 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
923 notebook = gtk_notebook_new();
924 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
925 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
926 gtk_container_add(GTK_CONTAINER(win), notebook);
928 ge->results_window = win;
929 ge->results_notebook = notebook;
930 return ge->results_notebook;
933 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
934 struct group_run_stats *rs)
936 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
937 struct gfio_client *gc = client->client_data;
941 res_win = get_results_window(gc->ge);
943 scroll = gtk_scrolled_window_new(NULL, NULL);
944 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
945 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
947 vbox = gtk_vbox_new(FALSE, 3);
949 box = gtk_hbox_new(FALSE, 0);
950 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
952 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
954 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
956 gc->results_widget = vbox;
958 entry = new_info_entry_in_frame(box, "Name");
959 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
960 if (strlen(ts->description)) {
961 entry = new_info_entry_in_frame(box, "Description");
962 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
964 entry = new_info_entry_in_frame(box, "Group ID");
965 entry_set_int_value(entry, ts->groupid);
966 entry = new_info_entry_in_frame(box, "Jobs");
967 entry_set_int_value(entry, ts->members);
968 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
969 entry_set_int_value(entry, ts->error);
970 entry = new_info_entry_in_frame(box, "PID");
971 entry_set_int_value(entry, ts->pid);
973 if (ts->io_bytes[DDIR_READ])
974 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
975 if (ts->io_bytes[DDIR_WRITE])
976 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
978 gfio_show_latency_buckets(vbox, ts);
979 gfio_show_cpu_usage(vbox, ts);
980 gfio_show_io_depths(vbox, ts);
982 gtk_widget_show_all(gc->ge->results_window);
986 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
988 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
989 struct gui *ui = &main_ui;
993 char tmp[64], timebuf[80];
996 tm = localtime(&sec);
997 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
998 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1000 gdk_threads_enter();
1002 gtk_list_store_append(ui->log_model, &iter);
1003 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1004 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1005 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1006 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1008 if (p->level == FIO_LOG_ERR)
1009 view_log(NULL, (gpointer) ui);
1011 gdk_threads_leave();
1014 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1016 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1017 struct gfio_client *gc = client->client_data;
1018 GtkWidget *box, *frame, *entry, *vbox;
1022 gdk_threads_enter();
1024 if (!gc->results_widget)
1027 if (!gc->disk_util_frame) {
1028 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1029 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1032 vbox = gtk_vbox_new(FALSE, 3);
1033 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1035 frame = gtk_frame_new((char *) p->dus.name);
1036 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1038 box = gtk_vbox_new(FALSE, 3);
1039 gtk_container_add(GTK_CONTAINER(frame), box);
1041 frame = gtk_frame_new("Read");
1042 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1043 vbox = gtk_hbox_new(TRUE, 3);
1044 gtk_container_add(GTK_CONTAINER(frame), vbox);
1045 entry = new_info_entry_in_frame(vbox, "IOs");
1046 entry_set_int_value(entry, p->dus.ios[0]);
1047 entry = new_info_entry_in_frame(vbox, "Merges");
1048 entry_set_int_value(entry, p->dus.merges[0]);
1049 entry = new_info_entry_in_frame(vbox, "Sectors");
1050 entry_set_int_value(entry, p->dus.sectors[0]);
1051 entry = new_info_entry_in_frame(vbox, "Ticks");
1052 entry_set_int_value(entry, p->dus.ticks[0]);
1054 frame = gtk_frame_new("Write");
1055 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1056 vbox = gtk_hbox_new(TRUE, 3);
1057 gtk_container_add(GTK_CONTAINER(frame), vbox);
1058 entry = new_info_entry_in_frame(vbox, "IOs");
1059 entry_set_int_value(entry, p->dus.ios[1]);
1060 entry = new_info_entry_in_frame(vbox, "Merges");
1061 entry_set_int_value(entry, p->dus.merges[1]);
1062 entry = new_info_entry_in_frame(vbox, "Sectors");
1063 entry_set_int_value(entry, p->dus.sectors[1]);
1064 entry = new_info_entry_in_frame(vbox, "Ticks");
1065 entry_set_int_value(entry, p->dus.ticks[1]);
1067 frame = gtk_frame_new("Shared");
1068 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1069 vbox = gtk_hbox_new(TRUE, 3);
1070 gtk_container_add(GTK_CONTAINER(frame), vbox);
1071 entry = new_info_entry_in_frame(vbox, "IO ticks");
1072 entry_set_int_value(entry, p->dus.io_ticks);
1073 entry = new_info_entry_in_frame(vbox, "Time in queue");
1074 entry_set_int_value(entry, p->dus.time_in_queue);
1078 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1082 sprintf(tmp, "%3.2f%%", util);
1083 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1084 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1086 gtk_widget_show_all(gc->results_widget);
1088 gdk_threads_leave();
1091 extern int sum_stat_clients;
1092 extern struct thread_stat client_ts;
1093 extern struct group_run_stats client_gs;
1095 static int sum_stat_nr;
1097 static void gfio_thread_status_op(struct fio_client *client,
1098 struct fio_net_cmd *cmd)
1100 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1102 gfio_display_ts(client, &p->ts, &p->rs);
1104 if (sum_stat_clients == 1)
1107 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1108 sum_group_stats(&client_gs, &p->rs);
1110 client_ts.members++;
1111 client_ts.groupid = p->ts.groupid;
1113 if (++sum_stat_nr == sum_stat_clients) {
1114 strcpy(client_ts.name, "All clients");
1115 gfio_display_ts(client, &client_ts, &client_gs);
1119 static void gfio_group_stats_op(struct fio_client *client,
1120 struct fio_net_cmd *cmd)
1122 /* We're ignoring group stats for now */
1125 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1128 struct gfio_graphs *g = data;
1130 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1131 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1132 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1133 graph_set_position(g->bandwidth_graph, 0, 0);
1137 static void draw_graph(struct graph *g, cairo_t *cr)
1139 line_graph_draw(g, cr);
1143 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1145 struct gfio_graphs *g = p;
1148 cr = gdk_cairo_create(w->window);
1149 cairo_set_source_rgb(cr, 0, 0, 0);
1150 draw_graph(g->iops_graph, cr);
1151 draw_graph(g->bandwidth_graph, cr);
1158 * Client specific ETA
1160 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1162 struct gfio_client *gc = client->client_data;
1163 struct gui_entry *ge = gc->ge;
1164 static int eta_good;
1171 gdk_threads_enter();
1176 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1177 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1178 eta_to_str(eta_str, je->eta_sec);
1181 sprintf(tmp, "%u", je->nr_running);
1182 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1183 sprintf(tmp, "%u", je->files_open);
1184 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1187 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1188 if (je->m_rate || je->t_rate) {
1191 mr = num2str(je->m_rate, 4, 0, i2p);
1192 tr = num2str(je->t_rate, 4, 0, i2p);
1193 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1194 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1197 } else if (je->m_iops || je->t_iops)
1198 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1200 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1201 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1202 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1203 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1206 if (je->eta_sec != INT_MAX && je->nr_running) {
1210 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1211 strcpy(output, "-.-% done");
1215 sprintf(output, "%3.1f%% done", perc);
1218 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1219 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1221 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1222 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1224 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1225 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1226 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1227 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1229 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1230 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1231 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1232 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1241 char *dst = output + strlen(output);
1243 sprintf(dst, " - %s", eta_str);
1246 gfio_update_thread_status(ge, output, perc);
1247 gdk_threads_leave();
1251 * Update ETA in main window for all clients
1253 static void gfio_update_all_eta(struct jobs_eta *je)
1255 struct gui *ui = &main_ui;
1256 static int eta_good;
1262 gdk_threads_enter();
1267 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1268 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1269 eta_to_str(eta_str, je->eta_sec);
1273 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1274 if (je->m_rate || je->t_rate) {
1277 mr = num2str(je->m_rate, 4, 0, i2p);
1278 tr = num2str(je->t_rate, 4, 0, i2p);
1279 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1280 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1283 } else if (je->m_iops || je->t_iops)
1284 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1286 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1287 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1288 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1289 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1292 entry_set_int_value(ui->eta.jobs, je->nr_running);
1294 if (je->eta_sec != INT_MAX && je->nr_running) {
1298 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1299 strcpy(output, "-.-% done");
1303 sprintf(output, "%3.1f%% done", perc);
1306 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1307 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1309 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1310 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1312 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1313 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1314 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1315 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1317 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1318 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1319 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1320 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1329 char *dst = output + strlen(output);
1331 sprintf(dst, " - %s", eta_str);
1334 gfio_update_thread_status_all(output, perc);
1335 gdk_threads_leave();
1338 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1340 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1341 struct gfio_client *gc = client->client_data;
1342 struct gui_entry *ge = gc->ge;
1343 const char *os, *arch;
1346 os = fio_get_os_string(probe->os);
1350 arch = fio_get_arch_string(probe->arch);
1355 client->name = strdup((char *) probe->hostname);
1357 gdk_threads_enter();
1359 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1360 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1361 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1362 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1363 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1365 gfio_set_connected(ge, 1);
1367 gdk_threads_leave();
1370 static void gfio_update_thread_status(struct gui_entry *ge,
1371 char *status_message, double perc)
1373 static char message[100];
1374 const char *m = message;
1376 strncpy(message, status_message, sizeof(message) - 1);
1377 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1378 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1379 gtk_widget_queue_draw(main_ui.window);
1382 static void gfio_update_thread_status_all(char *status_message, double perc)
1384 struct gui *ui = &main_ui;
1385 static char message[100];
1386 const char *m = message;
1388 strncpy(message, status_message, sizeof(message) - 1);
1389 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1390 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1391 gtk_widget_queue_draw(ui->window);
1394 static void gfio_quit_op(struct fio_client *client)
1396 struct gfio_client *gc = client->client_data;
1398 gdk_threads_enter();
1399 gfio_set_connected(gc->ge, 0);
1400 gdk_threads_leave();
1403 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1405 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1406 struct gfio_client *gc = client->client_data;
1407 struct thread_options *o = &gc->o;
1408 struct gui_entry *ge = gc->ge;
1411 convert_thread_options_to_cpu(o, &p->top);
1413 gdk_threads_enter();
1415 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1417 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1418 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1420 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1421 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1423 sprintf(tmp, "%u", o->iodepth);
1424 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1428 gdk_threads_leave();
1431 static void gfio_client_timed_out(struct fio_client *client)
1433 struct gfio_client *gc = client->client_data;
1434 GtkWidget *dialog, *label, *content;
1437 gdk_threads_enter();
1439 gfio_set_connected(gc->ge, 0);
1440 clear_ge_ui_info(gc->ge);
1442 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1444 dialog = gtk_dialog_new_with_buttons("Timed out!",
1445 GTK_WINDOW(main_ui.window),
1446 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1447 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1449 /* gtk_dialog_get_content_area() is 2.14 and newer */
1450 content = GTK_DIALOG(dialog)->vbox;
1452 label = gtk_label_new((const gchar *) buf);
1453 gtk_container_add(GTK_CONTAINER(content), label);
1454 gtk_widget_show_all(dialog);
1455 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1457 gtk_dialog_run(GTK_DIALOG(dialog));
1458 gtk_widget_destroy(dialog);
1460 gdk_threads_leave();
1463 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1465 struct gfio_client *gc = client->client_data;
1467 gdk_threads_enter();
1469 gfio_set_connected(gc->ge, 0);
1472 entry_set_int_value(gc->err_entry, client->error);
1474 gdk_threads_leave();
1477 struct client_ops gfio_client_ops = {
1478 .text_op = gfio_text_op,
1479 .disk_util = gfio_disk_util_op,
1480 .thread_status = gfio_thread_status_op,
1481 .group_stats = gfio_group_stats_op,
1482 .jobs_eta = gfio_update_client_eta,
1483 .eta = gfio_update_all_eta,
1484 .probe = gfio_probe_op,
1485 .quit = gfio_quit_op,
1486 .add_job = gfio_add_job_op,
1487 .timed_out = gfio_client_timed_out,
1488 .stop = gfio_client_stop,
1489 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
1490 .stay_connected = 1,
1493 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1494 __attribute__((unused)) gpointer data)
1499 static void *job_thread(void *arg)
1501 struct gui *ui = arg;
1503 ui->handler_running = 1;
1504 fio_handle_clients(&gfio_client_ops);
1505 ui->handler_running = 0;
1509 static int send_job_files(struct gui_entry *ge)
1511 struct gfio_client *gc = ge->client;
1514 for (i = 0; i < ge->nr_job_files; i++) {
1515 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1519 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1520 report_error(error);
1521 g_error_free(error);
1526 free(ge->job_files[i]);
1527 ge->job_files[i] = NULL;
1529 while (i < ge->nr_job_files) {
1530 free(ge->job_files[i]);
1531 ge->job_files[i] = NULL;
1538 static void *server_thread(void *arg)
1541 gfio_server_running = 1;
1542 fio_start_server(NULL);
1543 gfio_server_running = 0;
1547 static void gfio_start_server(void)
1549 struct gui *ui = &main_ui;
1551 if (!gfio_server_running) {
1552 gfio_server_running = 1;
1553 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1554 pthread_detach(ui->server_t);
1558 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1561 struct gui_entry *ge = data;
1562 struct gfio_client *gc = ge->client;
1564 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1565 fio_start_client(gc->client);
1568 static void file_open(GtkWidget *w, gpointer data);
1570 static void connect_clicked(GtkWidget *widget, gpointer data)
1572 struct gui_entry *ge = data;
1573 struct gfio_client *gc = ge->client;
1575 if (!ge->connected) {
1578 if (!ge->nr_job_files)
1579 file_open(widget, data);
1580 if (!ge->nr_job_files)
1583 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1584 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1585 ret = fio_client_connect(gc->client);
1587 if (!ge->ui->handler_running)
1588 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1589 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1590 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1594 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1595 report_error(error);
1596 g_error_free(error);
1599 fio_client_terminate(gc->client);
1600 gfio_set_connected(ge, 0);
1601 clear_ge_ui_info(ge);
1605 static void send_clicked(GtkWidget *widget, gpointer data)
1607 struct gui_entry *ge = data;
1609 if (send_job_files(ge)) {
1612 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);
1613 report_error(error);
1614 g_error_free(error);
1616 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1619 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1620 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1623 static GtkWidget *add_button(GtkWidget *buttonbox,
1624 struct button_spec *buttonspec, gpointer data)
1626 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1628 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1629 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1630 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1631 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1636 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1641 for (i = 0; i < nbuttons; i++)
1642 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1645 static void on_info_bar_response(GtkWidget *widget, gint response,
1648 struct gui *ui = &main_ui;
1650 if (response == GTK_RESPONSE_OK) {
1651 gtk_widget_destroy(widget);
1652 ui->error_info_bar = NULL;
1656 void report_error(GError *error)
1658 struct gui *ui = &main_ui;
1660 if (ui->error_info_bar == NULL) {
1661 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1664 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1665 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1668 ui->error_label = gtk_label_new(error->message);
1669 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1670 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1672 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1673 gtk_widget_show_all(ui->vbox);
1676 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1677 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1681 struct connection_widgets
1688 static void hostname_cb(GtkEntry *entry, gpointer data)
1690 struct connection_widgets *cw = data;
1691 int uses_net = 0, is_localhost = 0;
1696 * Check whether to display the 'auto start backend' box
1697 * or not. Show it if we are a localhost and using network,
1698 * or using a socket.
1700 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1701 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1706 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1707 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1708 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1709 !strcmp(text, "ip6-loopback"))
1713 if (!uses_net || is_localhost) {
1714 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1715 gtk_widget_set_sensitive(cw->button, 1);
1717 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1718 gtk_widget_set_sensitive(cw->button, 0);
1722 static int get_connection_details(char **host, int *port, int *type,
1725 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1726 struct connection_widgets cw;
1729 dialog = gtk_dialog_new_with_buttons("Connection details",
1730 GTK_WINDOW(main_ui.window),
1731 GTK_DIALOG_DESTROY_WITH_PARENT,
1732 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1733 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1735 frame = gtk_frame_new("Hostname / socket name");
1736 /* gtk_dialog_get_content_area() is 2.14 and newer */
1737 vbox = GTK_DIALOG(dialog)->vbox;
1738 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1740 box = gtk_vbox_new(FALSE, 6);
1741 gtk_container_add(GTK_CONTAINER(frame), box);
1743 hbox = gtk_hbox_new(TRUE, 10);
1744 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1745 cw.hentry = gtk_entry_new();
1746 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1747 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1749 frame = gtk_frame_new("Port");
1750 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1751 box = gtk_vbox_new(FALSE, 10);
1752 gtk_container_add(GTK_CONTAINER(frame), box);
1754 hbox = gtk_hbox_new(TRUE, 4);
1755 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1756 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1758 frame = gtk_frame_new("Type");
1759 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1760 box = gtk_vbox_new(FALSE, 10);
1761 gtk_container_add(GTK_CONTAINER(frame), box);
1763 hbox = gtk_hbox_new(TRUE, 4);
1764 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1766 cw.combo = gtk_combo_box_new_text();
1767 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1768 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1769 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1770 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1772 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1774 frame = gtk_frame_new("Options");
1775 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1776 box = gtk_vbox_new(FALSE, 10);
1777 gtk_container_add(GTK_CONTAINER(frame), box);
1779 hbox = gtk_hbox_new(TRUE, 4);
1780 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1782 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1783 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1784 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.");
1785 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1788 * Connect edit signal, so we can show/not-show the auto start button
1790 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1791 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1793 gtk_widget_show_all(dialog);
1795 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1796 gtk_widget_destroy(dialog);
1800 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1801 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1803 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1804 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1805 *type = Fio_client_ipv4;
1806 else if (!strncmp(typeentry, "IPv6", 4))
1807 *type = Fio_client_ipv6;
1809 *type = Fio_client_socket;
1812 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1814 gtk_widget_destroy(dialog);
1818 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1820 struct gfio_client *gc;
1822 gc = malloc(sizeof(*gc));
1823 memset(gc, 0, sizeof(*gc));
1825 gc->client = fio_get_client(client);
1829 client->client_data = gc;
1832 static GtkWidget *new_client_page(struct gui_entry *ge);
1834 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1836 struct gui_entry *ge;
1838 ge = malloc(sizeof(*ge));
1839 memset(ge, 0, sizeof(*ge));
1840 INIT_FLIST_HEAD(&ge->list);
1841 flist_add_tail(&ge->list, &ui->list);
1847 * FIXME: need more handling here
1849 static void ge_destroy(GtkWidget *w, gpointer data)
1851 struct gui_entry *ge = data;
1852 struct gfio_client *gc = ge->client;
1855 fio_put_client(gc->client);
1857 flist_del(&ge->list);
1861 static struct gui_entry *get_new_ge_with_tab(const char *name)
1863 struct gui_entry *ge;
1865 ge = alloc_new_gui_entry(&main_ui);
1867 ge->vbox = new_client_page(ge);
1868 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1870 ge->page_label = gtk_label_new(name);
1871 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1873 gtk_widget_show_all(main_ui.window);
1877 static void file_new(GtkWidget *w, gpointer data)
1879 get_new_ge_with_tab("Untitled");
1883 * Return the 'ge' corresponding to the tab. If the active tab is the
1884 * main tab, open a new tab.
1886 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1888 struct flist_head *entry;
1889 struct gui_entry *ge;
1892 return get_new_ge_with_tab("Untitled");
1894 flist_for_each(entry, &main_ui.list) {
1895 ge = flist_entry(entry, struct gui_entry, list);
1896 if (ge->page_num == cur_page)
1903 static void file_open(GtkWidget *w, gpointer data)
1905 struct gui *ui = data;
1907 GSList *filenames, *fn_glist;
1908 GtkFileFilter *filter;
1910 int port, type, server_start;
1911 struct gui_entry *ge;
1915 * Creates new tab if current tab is the main window, or the
1916 * current tab already has a client.
1918 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1919 ge = get_ge_from_page(cur_page);
1921 ge = get_new_ge_with_tab("Untitled");
1923 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1925 dialog = gtk_file_chooser_dialog_new("Open File",
1926 GTK_WINDOW(ui->window),
1927 GTK_FILE_CHOOSER_ACTION_OPEN,
1928 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1929 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1931 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1933 filter = gtk_file_filter_new();
1934 gtk_file_filter_add_pattern(filter, "*.fio");
1935 gtk_file_filter_add_pattern(filter, "*.job");
1936 gtk_file_filter_add_pattern(filter, "*.ini");
1937 gtk_file_filter_add_mime_type(filter, "text/fio");
1938 gtk_file_filter_set_name(filter, "Fio job file");
1939 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1941 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1942 gtk_widget_destroy(dialog);
1946 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1948 gtk_widget_destroy(dialog);
1950 if (get_connection_details(&host, &port, &type, &server_start))
1953 filenames = fn_glist;
1954 while (filenames != NULL) {
1955 struct fio_client *client;
1957 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1958 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1961 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1965 error = g_error_new(g_quark_from_string("fio"), 1,
1966 "Failed to add client %s", host);
1967 report_error(error);
1968 g_error_free(error);
1970 gfio_client_added(ge, client);
1972 g_free(filenames->data);
1973 filenames = g_slist_next(filenames);
1978 gfio_start_server();
1980 g_slist_free(fn_glist);
1983 static void file_save(GtkWidget *w, gpointer data)
1985 struct gui *ui = data;
1988 dialog = gtk_file_chooser_dialog_new("Save File",
1989 GTK_WINDOW(ui->window),
1990 GTK_FILE_CHOOSER_ACTION_SAVE,
1991 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1992 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1995 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1996 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1998 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2001 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2002 // save_job_file(filename);
2005 gtk_widget_destroy(dialog);
2008 static void view_log_destroy(GtkWidget *w, gpointer data)
2010 struct gui *ui = (struct gui *) data;
2012 gtk_widget_ref(ui->log_tree);
2013 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2014 gtk_widget_destroy(w);
2015 ui->log_view = NULL;
2018 static void view_log(GtkWidget *w, gpointer data)
2020 GtkWidget *win, *scroll, *vbox, *box;
2021 struct gui *ui = (struct gui *) data;
2026 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2027 gtk_window_set_title(GTK_WINDOW(win), "Log");
2028 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2030 scroll = gtk_scrolled_window_new(NULL, NULL);
2032 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2034 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2036 box = gtk_hbox_new(TRUE, 0);
2037 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2038 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2039 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2041 vbox = gtk_vbox_new(TRUE, 5);
2042 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2044 gtk_container_add(GTK_CONTAINER(win), vbox);
2045 gtk_widget_show_all(win);
2048 static void __update_graph_limits(struct gfio_graphs *g)
2050 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2051 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2054 static void update_graph_limits(void)
2056 struct flist_head *entry;
2057 struct gui_entry *ge;
2059 __update_graph_limits(&main_ui.graphs);
2061 flist_for_each(entry, &main_ui.list) {
2062 ge = flist_entry(entry, struct gui_entry, list);
2063 __update_graph_limits(&ge->graphs);
2067 static void preferences(GtkWidget *w, gpointer data)
2069 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2070 GtkWidget *hbox, *spin, *entry, *spin_int;
2073 dialog = gtk_dialog_new_with_buttons("Preferences",
2074 GTK_WINDOW(main_ui.window),
2075 GTK_DIALOG_DESTROY_WITH_PARENT,
2076 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2077 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2080 frame = gtk_frame_new("Graphing");
2081 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2082 vbox = gtk_vbox_new(FALSE, 6);
2083 gtk_container_add(GTK_CONTAINER(frame), vbox);
2085 hbox = gtk_hbox_new(FALSE, 5);
2086 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2087 entry = gtk_label_new("Font face to use for graph labels");
2088 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2090 font = gtk_font_button_new();
2091 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2093 box = gtk_vbox_new(FALSE, 6);
2094 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2096 hbox = gtk_hbox_new(FALSE, 5);
2097 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2098 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2099 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2101 spin = create_spinbutton(hbox, 10, 1000000, 100);
2103 box = gtk_vbox_new(FALSE, 6);
2104 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2106 hbox = gtk_hbox_new(FALSE, 5);
2107 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2108 entry = gtk_label_new("Client ETA request interval (msec)");
2109 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2111 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2112 frame = gtk_frame_new("Debug logging");
2113 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2114 vbox = gtk_vbox_new(FALSE, 6);
2115 gtk_container_add(GTK_CONTAINER(frame), vbox);
2117 box = gtk_hbox_new(FALSE, 6);
2118 gtk_container_add(GTK_CONTAINER(vbox), box);
2120 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2122 for (i = 0; i < FD_DEBUG_MAX; i++) {
2124 box = gtk_hbox_new(FALSE, 6);
2125 gtk_container_add(GTK_CONTAINER(vbox), box);
2129 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2130 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2131 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2134 gtk_widget_show_all(dialog);
2136 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2137 gtk_widget_destroy(dialog);
2141 for (i = 0; i < FD_DEBUG_MAX; i++) {
2144 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2146 fio_debug |= (1UL << i);
2149 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2150 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2151 update_graph_limits();
2152 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2154 gtk_widget_destroy(dialog);
2157 static void about_dialog(GtkWidget *w, gpointer data)
2159 const char *authors[] = {
2160 "Jens Axboe <axboe@kernel.dk>",
2161 "Stephen Carmeron <stephenmcameron@gmail.com>",
2164 const char *license[] = {
2165 "Fio is free software; you can redistribute it and/or modify "
2166 "it under the terms of the GNU General Public License as published by "
2167 "the Free Software Foundation; either version 2 of the License, or "
2168 "(at your option) any later version.\n",
2169 "Fio is distributed in the hope that it will be useful, "
2170 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2171 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2172 "GNU General Public License for more details.\n",
2173 "You should have received a copy of the GNU General Public License "
2174 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2175 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2177 char *license_trans;
2179 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2180 license[2], "\n", NULL);
2182 gtk_show_about_dialog(NULL,
2183 "program-name", "gfio",
2184 "comments", "Gtk2 UI for fio",
2185 "license", license_trans,
2186 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2188 "version", fio_version_string,
2189 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2190 "logo-icon-name", "fio",
2192 "wrap-license", TRUE,
2195 g_free(license_trans);
2198 static GtkActionEntry menu_items[] = {
2199 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2200 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2201 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2202 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2203 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2204 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2205 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2206 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2207 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2208 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2210 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2212 static const gchar *ui_string = " \
2214 <menubar name=\"MainMenu\"> \
2215 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2216 <menuitem name=\"New\" action=\"NewFile\" /> \
2217 <separator name=\"Separator1\"/> \
2218 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2219 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2220 <separator name=\"Separator2\"/> \
2221 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2222 <separator name=\"Separator3\"/> \
2223 <menuitem name=\"Quit\" action=\"Quit\" /> \
2225 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2226 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2228 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2229 <menuitem name=\"About\" action=\"About\" /> \
2235 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2238 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2241 action_group = gtk_action_group_new("Menu");
2242 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2244 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2245 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2247 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2248 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2251 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2252 GtkWidget *vbox, GtkUIManager *ui_manager)
2254 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2257 static GtkWidget *new_client_page(struct gui_entry *ge)
2259 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2262 main_vbox = gtk_vbox_new(FALSE, 3);
2264 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2265 ge->topvbox = gtk_vbox_new(FALSE, 3);
2266 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2267 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2269 probe = gtk_frame_new("Job");
2270 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2271 probe_frame = gtk_vbox_new(FALSE, 3);
2272 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2274 probe_box = gtk_hbox_new(FALSE, 3);
2275 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2276 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2277 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2278 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2279 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2281 probe_box = gtk_hbox_new(FALSE, 3);
2282 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2284 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2285 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2286 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2287 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2288 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2289 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2291 probe_box = gtk_hbox_new(FALSE, 3);
2292 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2293 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2294 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2295 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2296 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2299 * Only add this if we have a commit rate
2302 probe_box = gtk_hbox_new(FALSE, 3);
2303 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2305 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2306 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2308 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2309 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2313 * Set up a drawing area and IOPS and bandwidth graphs
2315 gdk_color_parse("white", &white);
2316 ge->graphs.drawing_area = gtk_drawing_area_new();
2317 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2318 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2319 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2320 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2321 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2322 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2323 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2324 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2325 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2326 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2327 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2328 ge->graphs.drawing_area);
2329 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2332 setup_graphs(&ge->graphs);
2335 * Set up alignments for widgets at the bottom of ui,
2336 * align bottom left, expand horizontally but not vertically
2338 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2339 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2340 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2341 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2344 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2347 * Set up thread status progress bar
2349 ge->thread_status_pb = gtk_progress_bar_new();
2350 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2351 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2352 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2358 static GtkWidget *new_main_page(struct gui *ui)
2360 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2363 main_vbox = gtk_vbox_new(FALSE, 3);
2366 * Set up alignments for widgets at the top of ui,
2367 * align top left, expand horizontally but not vertically
2369 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2370 ui->topvbox = gtk_vbox_new(FALSE, 0);
2371 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2372 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2374 probe = gtk_frame_new("Run statistics");
2375 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2376 probe_frame = gtk_vbox_new(FALSE, 3);
2377 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2379 probe_box = gtk_hbox_new(FALSE, 3);
2380 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2381 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2382 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2383 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2384 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2385 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2388 * Only add this if we have a commit rate
2391 probe_box = gtk_hbox_new(FALSE, 3);
2392 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2394 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2395 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2397 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2398 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2402 * Set up a drawing area and IOPS and bandwidth graphs
2404 gdk_color_parse("white", &white);
2405 ui->graphs.drawing_area = gtk_drawing_area_new();
2406 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2407 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2408 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2409 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2410 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2411 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2412 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2413 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2414 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2415 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2416 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2417 ui->graphs.drawing_area);
2418 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2421 setup_graphs(&ui->graphs);
2424 * Set up alignments for widgets at the bottom of ui,
2425 * align bottom left, expand horizontally but not vertically
2427 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2428 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2429 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2430 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2434 * Set up thread status progress bar
2436 ui->thread_status_pb = gtk_progress_bar_new();
2437 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2438 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2439 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2444 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2445 guint page, gpointer data)
2451 static void init_ui(int *argc, char **argv[], struct gui *ui)
2453 GtkSettings *settings;
2454 GtkUIManager *uimanager;
2455 GtkWidget *menu, *vbox;
2457 /* Magical g*thread incantation, you just need this thread stuff.
2458 * Without it, the update that happens in gfio_update_thread_status
2459 * doesn't really happen in a timely fashion, you need expose events
2461 if (!g_thread_supported())
2462 g_thread_init(NULL);
2465 gtk_init(argc, argv);
2466 settings = gtk_settings_get_default();
2467 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2470 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2471 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2472 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2474 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2475 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2477 ui->vbox = gtk_vbox_new(FALSE, 0);
2478 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2480 uimanager = gtk_ui_manager_new();
2481 menu = get_menubar_menu(ui->window, uimanager, ui);
2482 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2484 ui->notebook = gtk_notebook_new();
2485 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2486 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2487 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2488 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2490 vbox = new_main_page(ui);
2492 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2494 gfio_ui_setup_log(ui);
2496 gtk_widget_show_all(ui->window);
2499 int main(int argc, char *argv[], char *envp[])
2501 if (initialize_fio(envp))
2503 if (fio_init_options())
2506 memset(&main_ui, 0, sizeof(main_ui));
2507 INIT_FLIST_HEAD(&main_ui.list);
2509 init_ui(&argc, &argv, &main_ui);
2511 gdk_threads_enter();
2513 gdk_threads_leave();