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;
38 static void gfio_update_thread_status(char *status_message, double perc);
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 int drawing_area_xdim;
93 int drawing_area_ydim;
95 struct graph *iops_graph;
96 struct graph *bandwidth_graph;
100 * Main window widgets and data
107 GtkWidget *bottomalign;
108 GtkWidget *thread_status_pb;
109 GtkWidget *buttonbox;
110 GtkWidget *scrolled_window;
112 GtkWidget *error_info_bar;
113 GtkWidget *error_label;
114 GtkListStore *log_model;
117 struct gfio_graphs graphs;
118 struct probe_widget probe;
119 struct eta_widget eta;
125 struct flist_head list;
132 struct flist_head list;
138 GtkWidget *bottomalign;
139 GtkWidget *thread_status_pb;
140 GtkWidget *buttonbox;
141 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
142 GtkWidget *scrolled_window;
144 GtkWidget *error_info_bar;
145 GtkWidget *error_label;
146 GtkWidget *results_notebook;
147 GtkWidget *results_window;
148 GtkListStore *log_model;
151 struct gfio_graphs graphs;
152 struct probe_widget probe;
153 struct eta_widget eta;
154 GtkWidget *page_label;
158 struct gfio_client *client;
164 struct gui_entry *ge;
165 struct fio_client *client;
166 GtkWidget *results_widget;
167 GtkWidget *disk_util_frame;
168 GtkWidget *err_entry;
169 unsigned int job_added;
170 struct thread_options o;
173 static struct graph *setup_iops_graph(void)
177 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
178 graph_title(g, "IOPS");
179 graph_x_title(g, "Time (secs)");
180 graph_y_title(g, "IOs / sec");
181 graph_add_label(g, "Read IOPS");
182 graph_add_label(g, "Write IOPS");
183 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
184 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
185 line_graph_set_data_count_limit(g, 100);
189 static struct graph *setup_bandwidth_graph(void)
193 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
194 graph_title(g, "Bandwidth");
195 graph_x_title(g, "Time (secs)");
196 graph_y_title(g, "Kbytes / sec");
197 graph_add_label(g, "Read Bandwidth");
198 graph_add_label(g, "Write Bandwidth");
199 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
200 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
201 line_graph_set_data_count_limit(g, 100);
205 static void setup_graphs(struct gfio_graphs *g)
207 g->iops_graph = setup_iops_graph();
208 g->bandwidth_graph = setup_bandwidth_graph();
211 static void clear_ge_ui_info(struct gui_entry *ge)
213 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
214 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
215 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
216 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
217 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
218 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
219 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
220 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
221 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
225 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
226 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
229 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
231 GtkWidget *entry, *frame;
233 frame = gtk_frame_new(label);
234 entry = gtk_entry_new();
235 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
236 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
237 gtk_container_add(GTK_CONTAINER(frame), entry);
242 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
244 GtkWidget *label_widget;
247 frame = gtk_frame_new(label);
248 label_widget = gtk_label_new(NULL);
249 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
250 gtk_container_add(GTK_CONTAINER(frame), label_widget);
255 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
257 GtkWidget *button, *box;
259 box = gtk_hbox_new(FALSE, 3);
260 gtk_container_add(GTK_CONTAINER(hbox), box);
262 button = gtk_spin_button_new_with_range(min, max, 1.0);
263 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
265 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
266 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
271 static void gfio_set_connected(struct gui_entry *ge, int connected)
274 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
276 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
277 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
280 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
281 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
282 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
283 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
287 static void label_set_int_value(GtkWidget *entry, unsigned int val)
291 sprintf(tmp, "%u", val);
292 gtk_label_set_text(GTK_LABEL(entry), tmp);
295 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
299 sprintf(tmp, "%u", val);
300 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
304 #define ALIGN_RIGHT 2
308 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
310 GtkCellRenderer *renderer;
311 GtkTreeViewColumn *col;
312 double xalign = 0.0; /* left as default */
313 PangoAlignment align;
316 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
317 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
319 visible = !(flags & INVISIBLE);
321 renderer = gtk_cell_renderer_text_new();
322 col = gtk_tree_view_column_new();
324 gtk_tree_view_column_set_title(col, title);
325 if (!(flags & UNSORTABLE))
326 gtk_tree_view_column_set_sort_column_id(col, index);
327 gtk_tree_view_column_set_resizable(col, TRUE);
328 gtk_tree_view_column_pack_start(col, renderer, TRUE);
329 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
330 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
332 case PANGO_ALIGN_LEFT:
335 case PANGO_ALIGN_CENTER:
338 case PANGO_ALIGN_RIGHT:
342 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
343 gtk_tree_view_column_set_visible(col, visible);
344 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
348 static void gfio_ui_setup_log(struct gui *ui)
350 GtkTreeSelection *selection;
352 GtkWidget *tree_view;
354 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
356 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
357 gtk_widget_set_can_focus(tree_view, FALSE);
359 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
360 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
361 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
362 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
364 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
365 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
366 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
367 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
369 ui->log_model = model;
370 ui->log_tree = tree_view;
373 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
379 GType types[FIO_IO_U_LIST_MAX_LEN];
380 GtkWidget *tree_view;
381 GtkTreeSelection *selection;
386 for (i = 0; i < len; i++)
387 types[i] = G_TYPE_INT;
389 model = gtk_list_store_newv(len, types);
391 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
392 gtk_widget_set_can_focus(tree_view, FALSE);
394 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
395 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
397 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
398 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
400 for (i = 0; i < len; i++) {
403 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
404 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
407 gtk_list_store_append(model, &iter);
409 for (i = 0; i < len; i++) {
411 ovals[i] = (ovals[i] + 999) / 1000;
412 gtk_list_store_set(model, &iter, i, ovals[i], -1);
418 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
421 unsigned int *io_u_plat = ts->io_u_plat[ddir];
422 unsigned long nr = ts->clat_stat[ddir].samples;
423 fio_fp64_t *plist = ts->percentile_list;
424 unsigned int *ovals, len, minv, maxv, scale_down;
426 GtkWidget *tree_view, *frame, *hbox;
429 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
434 * We default to usecs, but if the value range is such that we
435 * should scale down to msecs, do that.
437 if (minv > 2000 && maxv > 99999) {
445 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
447 sprintf(tmp, "Completion percentiles (%s)", base);
448 frame = gtk_frame_new(tmp);
449 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
451 hbox = gtk_hbox_new(FALSE, 3);
452 gtk_container_add(GTK_CONTAINER(frame), hbox);
454 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
460 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
461 unsigned long max, double mean, double dev)
463 const char *base = "(usec)";
464 GtkWidget *hbox, *label, *frame;
468 if (!usec_to_msec(&min, &max, &mean, &dev))
471 minp = num2str(min, 6, 1, 0);
472 maxp = num2str(max, 6, 1, 0);
474 sprintf(tmp, "%s %s", name, base);
475 frame = gtk_frame_new(tmp);
476 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
478 hbox = gtk_hbox_new(FALSE, 3);
479 gtk_container_add(GTK_CONTAINER(frame), hbox);
481 label = new_info_label_in_frame(hbox, "Minimum");
482 gtk_label_set_text(GTK_LABEL(label), minp);
483 label = new_info_label_in_frame(hbox, "Maximum");
484 gtk_label_set_text(GTK_LABEL(label), maxp);
485 label = new_info_label_in_frame(hbox, "Average");
486 sprintf(tmp, "%5.02f", mean);
487 gtk_label_set_text(GTK_LABEL(label), tmp);
488 label = new_info_label_in_frame(hbox, "Standard deviation");
489 sprintf(tmp, "%5.02f", dev);
490 gtk_label_set_text(GTK_LABEL(label), tmp);
501 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
502 struct thread_stat *ts, int ddir)
504 const char *ddir_label[2] = { "Read", "Write" };
505 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
506 unsigned long min[3], max[3], runt;
507 unsigned long long bw, iops;
508 unsigned int flags = 0;
509 double mean[3], dev[3];
510 char *io_p, *bw_p, *iops_p;
513 if (!ts->runtime[ddir])
516 i2p = is_power_of_2(rs->kb_base);
517 runt = ts->runtime[ddir];
519 bw = (1000 * ts->io_bytes[ddir]) / runt;
520 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
521 bw_p = num2str(bw, 6, 1, i2p);
523 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
524 iops_p = num2str(iops, 6, 1, 0);
526 box = gtk_hbox_new(FALSE, 3);
527 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
529 frame = gtk_frame_new(ddir_label[ddir]);
530 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
532 main_vbox = gtk_vbox_new(FALSE, 3);
533 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
535 box = gtk_hbox_new(FALSE, 3);
536 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
538 label = new_info_label_in_frame(box, "IO");
539 gtk_label_set_text(GTK_LABEL(label), io_p);
540 label = new_info_label_in_frame(box, "Bandwidth");
541 gtk_label_set_text(GTK_LABEL(label), bw_p);
542 label = new_info_label_in_frame(box, "IOPS");
543 gtk_label_set_text(GTK_LABEL(label), iops_p);
544 label = new_info_label_in_frame(box, "Runtime (msec)");
545 label_set_int_value(label, ts->runtime[ddir]);
547 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
548 double p_of_agg = 100.0;
549 const char *bw_str = "KB";
553 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
554 if (p_of_agg > 100.0)
558 if (mean[0] > 999999.9) {
566 sprintf(tmp, "Bandwidth (%s)", bw_str);
567 frame = gtk_frame_new(tmp);
568 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
570 box = gtk_hbox_new(FALSE, 3);
571 gtk_container_add(GTK_CONTAINER(frame), box);
573 label = new_info_label_in_frame(box, "Minimum");
574 label_set_int_value(label, min[0]);
575 label = new_info_label_in_frame(box, "Maximum");
576 label_set_int_value(label, max[0]);
577 label = new_info_label_in_frame(box, "Percentage of jobs");
578 sprintf(tmp, "%3.2f%%", p_of_agg);
579 gtk_label_set_text(GTK_LABEL(label), tmp);
580 label = new_info_label_in_frame(box, "Average");
581 sprintf(tmp, "%5.02f", mean[0]);
582 gtk_label_set_text(GTK_LABEL(label), tmp);
583 label = new_info_label_in_frame(box, "Standard deviation");
584 sprintf(tmp, "%5.02f", dev[0]);
585 gtk_label_set_text(GTK_LABEL(label), tmp);
588 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
590 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
592 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
596 frame = gtk_frame_new("Latency");
597 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
599 vbox = gtk_vbox_new(FALSE, 3);
600 gtk_container_add(GTK_CONTAINER(frame), vbox);
602 if (flags & GFIO_SLAT)
603 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
604 if (flags & GFIO_CLAT)
605 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
606 if (flags & GFIO_LAT)
607 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
610 if (ts->clat_percentiles)
611 gfio_show_clat_percentiles(main_vbox, ts, ddir);
619 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
622 GtkWidget *tree_view;
623 GtkTreeSelection *selection;
630 * Check if all are empty, in which case don't bother
632 for (i = 0, skipped = 0; i < num; i++)
639 types = malloc(num * sizeof(GType));
641 for (i = 0; i < num; i++)
642 types[i] = G_TYPE_STRING;
644 model = gtk_list_store_newv(num, types);
648 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
649 gtk_widget_set_can_focus(tree_view, FALSE);
651 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
652 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
654 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
655 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
657 for (i = 0; i < num; i++)
658 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
660 gtk_list_store_append(model, &iter);
662 for (i = 0; i < num; i++) {
666 sprintf(fbuf, "0.00");
668 sprintf(fbuf, "%3.2f%%", lat[i]);
670 gtk_list_store_set(model, &iter, i, fbuf, -1);
676 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
678 GtkWidget *box, *frame, *tree_view;
679 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
680 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
681 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
682 "250", "500", "750", "1000", };
683 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
684 "250", "500", "750", "1000", "2000",
687 stat_calc_lat_u(ts, io_u_lat_u);
688 stat_calc_lat_m(ts, io_u_lat_m);
690 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
692 frame = gtk_frame_new("Latency buckets (usec)");
693 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
695 box = gtk_hbox_new(FALSE, 3);
696 gtk_container_add(GTK_CONTAINER(frame), box);
697 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
700 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
702 frame = gtk_frame_new("Latency buckets (msec)");
703 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
705 box = gtk_hbox_new(FALSE, 3);
706 gtk_container_add(GTK_CONTAINER(frame), box);
707 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
711 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
713 GtkWidget *box, *frame, *entry;
714 double usr_cpu, sys_cpu;
715 unsigned long runtime;
718 runtime = ts->total_run_time;
720 double runt = (double) runtime;
722 usr_cpu = (double) ts->usr_time * 100 / runt;
723 sys_cpu = (double) ts->sys_time * 100 / runt;
729 frame = gtk_frame_new("OS resources");
730 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
732 box = gtk_hbox_new(FALSE, 3);
733 gtk_container_add(GTK_CONTAINER(frame), box);
735 entry = new_info_entry_in_frame(box, "User CPU");
736 sprintf(tmp, "%3.2f%%", usr_cpu);
737 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
738 entry = new_info_entry_in_frame(box, "System CPU");
739 sprintf(tmp, "%3.2f%%", sys_cpu);
740 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
741 entry = new_info_entry_in_frame(box, "Context switches");
742 entry_set_int_value(entry, ts->ctx);
743 entry = new_info_entry_in_frame(box, "Major faults");
744 entry_set_int_value(entry, ts->majf);
745 entry = new_info_entry_in_frame(box, "Minor faults");
746 entry_set_int_value(entry, ts->minf);
748 static void gfio_add_sc_depths_tree(GtkListStore *model,
749 struct thread_stat *ts, unsigned int len,
752 double io_u_dist[FIO_IO_U_MAP_NR];
754 /* Bits 0, and 3-8 */
755 const int add_mask = 0x1f9;
759 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
761 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
763 gtk_list_store_append(model, &iter);
765 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
767 for (i = 1, j = 0; i < len; i++) {
770 if (!(add_mask & (1UL << (i - 1))))
771 sprintf(fbuf, "0.0%%");
773 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
777 gtk_list_store_set(model, &iter, i, fbuf, -1);
782 static void gfio_add_total_depths_tree(GtkListStore *model,
783 struct thread_stat *ts, unsigned int len)
785 double io_u_dist[FIO_IO_U_MAP_NR];
787 /* Bits 1-6, and 8 */
788 const int add_mask = 0x17e;
791 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
793 gtk_list_store_append(model, &iter);
795 gtk_list_store_set(model, &iter, 0, "Total", -1);
797 for (i = 1, j = 0; i < len; i++) {
800 if (!(add_mask & (1UL << (i - 1))))
801 sprintf(fbuf, "0.0%%");
803 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
807 gtk_list_store_set(model, &iter, i, fbuf, -1);
812 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
814 GtkWidget *frame, *box, *tree_view;
815 GtkTreeSelection *selection;
817 GType types[FIO_IO_U_MAP_NR + 1];
820 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
822 frame = gtk_frame_new("IO depths");
823 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
825 box = gtk_hbox_new(FALSE, 3);
826 gtk_container_add(GTK_CONTAINER(frame), box);
828 for (i = 0; i < NR_LABELS; i++)
829 types[i] = G_TYPE_STRING;
831 model = gtk_list_store_newv(NR_LABELS, types);
833 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
834 gtk_widget_set_can_focus(tree_view, FALSE);
836 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
837 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
839 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
840 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
842 for (i = 0; i < NR_LABELS; i++)
843 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
845 gfio_add_total_depths_tree(model, ts, NR_LABELS);
846 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
847 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
849 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
852 static gboolean results_window_delete(GtkWidget *w, gpointer data)
854 struct gui_entry *ge = (struct gui_entry *) data;
856 gtk_widget_destroy(w);
857 ge->results_window = NULL;
858 ge->results_notebook = NULL;
862 static GtkWidget *get_results_window(struct gui_entry *ge)
864 GtkWidget *win, *notebook;
866 if (ge->results_window)
867 return ge->results_notebook;
869 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
870 gtk_window_set_title(GTK_WINDOW(win), "Results");
871 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
872 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
873 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
875 notebook = gtk_notebook_new();
876 gtk_container_add(GTK_CONTAINER(win), notebook);
878 ge->results_window = win;
879 ge->results_notebook = notebook;
880 return ge->results_notebook;
883 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
884 struct group_run_stats *rs)
886 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
887 struct gfio_client *gc = client->client_data;
891 res_win = get_results_window(gc->ge);
893 scroll = gtk_scrolled_window_new(NULL, NULL);
894 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
895 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
897 vbox = gtk_vbox_new(FALSE, 3);
899 box = gtk_hbox_new(FALSE, 0);
900 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
902 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
904 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
906 gc->results_widget = vbox;
908 entry = new_info_entry_in_frame(box, "Name");
909 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
910 if (strlen(ts->description)) {
911 entry = new_info_entry_in_frame(box, "Description");
912 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
914 entry = new_info_entry_in_frame(box, "Group ID");
915 entry_set_int_value(entry, ts->groupid);
916 entry = new_info_entry_in_frame(box, "Jobs");
917 entry_set_int_value(entry, ts->members);
918 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
919 entry_set_int_value(entry, ts->error);
920 entry = new_info_entry_in_frame(box, "PID");
921 entry_set_int_value(entry, ts->pid);
923 if (ts->io_bytes[DDIR_READ])
924 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
925 if (ts->io_bytes[DDIR_WRITE])
926 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
928 gfio_show_latency_buckets(vbox, ts);
929 gfio_show_cpu_usage(vbox, ts);
930 gfio_show_io_depths(vbox, ts);
932 gtk_widget_show_all(gc->ge->results_window);
936 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
938 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
939 struct gui *ui = &main_ui;
943 char tmp[64], timebuf[80];
946 tm = localtime(&sec);
947 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
948 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
952 gtk_list_store_append(ui->log_model, &iter);
953 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
954 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
955 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
956 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
958 if (p->level == FIO_LOG_ERR)
959 view_log(NULL, (gpointer) ui);
964 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
966 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
967 struct gfio_client *gc = client->client_data;
968 GtkWidget *box, *frame, *entry, *vbox;
974 if (!gc->results_widget)
977 if (!gc->disk_util_frame) {
978 gc->disk_util_frame = gtk_frame_new("Disk utilization");
979 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
982 vbox = gtk_vbox_new(FALSE, 3);
983 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
985 frame = gtk_frame_new((char *) p->dus.name);
986 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
988 box = gtk_vbox_new(FALSE, 3);
989 gtk_container_add(GTK_CONTAINER(frame), box);
991 frame = gtk_frame_new("Read");
992 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
993 vbox = gtk_hbox_new(TRUE, 3);
994 gtk_container_add(GTK_CONTAINER(frame), vbox);
995 entry = new_info_entry_in_frame(vbox, "IOs");
996 entry_set_int_value(entry, p->dus.ios[0]);
997 entry = new_info_entry_in_frame(vbox, "Merges");
998 entry_set_int_value(entry, p->dus.merges[0]);
999 entry = new_info_entry_in_frame(vbox, "Sectors");
1000 entry_set_int_value(entry, p->dus.sectors[0]);
1001 entry = new_info_entry_in_frame(vbox, "Ticks");
1002 entry_set_int_value(entry, p->dus.ticks[0]);
1004 frame = gtk_frame_new("Write");
1005 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1006 vbox = gtk_hbox_new(TRUE, 3);
1007 gtk_container_add(GTK_CONTAINER(frame), vbox);
1008 entry = new_info_entry_in_frame(vbox, "IOs");
1009 entry_set_int_value(entry, p->dus.ios[1]);
1010 entry = new_info_entry_in_frame(vbox, "Merges");
1011 entry_set_int_value(entry, p->dus.merges[1]);
1012 entry = new_info_entry_in_frame(vbox, "Sectors");
1013 entry_set_int_value(entry, p->dus.sectors[1]);
1014 entry = new_info_entry_in_frame(vbox, "Ticks");
1015 entry_set_int_value(entry, p->dus.ticks[1]);
1017 frame = gtk_frame_new("Shared");
1018 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1019 vbox = gtk_hbox_new(TRUE, 3);
1020 gtk_container_add(GTK_CONTAINER(frame), vbox);
1021 entry = new_info_entry_in_frame(vbox, "IO ticks");
1022 entry_set_int_value(entry, p->dus.io_ticks);
1023 entry = new_info_entry_in_frame(vbox, "Time in queue");
1024 entry_set_int_value(entry, p->dus.time_in_queue);
1028 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1032 sprintf(tmp, "%3.2f%%", util);
1033 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1034 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1036 gtk_widget_show_all(gc->results_widget);
1038 gdk_threads_leave();
1041 extern int sum_stat_clients;
1042 extern struct thread_stat client_ts;
1043 extern struct group_run_stats client_gs;
1045 static int sum_stat_nr;
1047 static void gfio_thread_status_op(struct fio_client *client,
1048 struct fio_net_cmd *cmd)
1050 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1052 gfio_display_ts(client, &p->ts, &p->rs);
1054 if (sum_stat_clients == 1)
1057 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1058 sum_group_stats(&client_gs, &p->rs);
1060 client_ts.members++;
1061 client_ts.groupid = p->ts.groupid;
1063 if (++sum_stat_nr == sum_stat_clients) {
1064 strcpy(client_ts.name, "All clients");
1065 gfio_display_ts(client, &client_ts, &client_gs);
1069 static void gfio_group_stats_op(struct fio_client *client,
1070 struct fio_net_cmd *cmd)
1072 /* We're ignoring group stats for now */
1075 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1078 struct gfio_graphs *g = data;
1080 g->drawing_area_xdim = w->allocation.width;
1081 g->drawing_area_ydim = w->allocation.height;
1085 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1087 struct gfio_graphs *g = p;
1090 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1091 g->drawing_area_ydim);
1092 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1093 g->drawing_area_ydim);
1094 cr = gdk_cairo_create(w->window);
1096 cairo_set_source_rgb(cr, 0, 0, 0);
1099 cairo_translate(cr, 0, 0);
1100 line_graph_draw(g->bandwidth_graph, cr);
1105 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1106 line_graph_draw(g->iops_graph, cr);
1115 * Client specific ETA
1117 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1119 struct gfio_client *gc = client->client_data;
1120 struct gui_entry *ge = gc->ge;
1121 static int eta_good;
1128 gdk_threads_enter();
1133 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1134 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1135 eta_to_str(eta_str, je->eta_sec);
1138 sprintf(tmp, "%u", je->nr_running);
1139 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1140 sprintf(tmp, "%u", je->files_open);
1141 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1144 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1145 if (je->m_rate || je->t_rate) {
1148 mr = num2str(je->m_rate, 4, 0, i2p);
1149 tr = num2str(je->t_rate, 4, 0, i2p);
1150 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1151 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1154 } else if (je->m_iops || je->t_iops)
1155 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1157 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1158 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1159 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1160 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1163 if (je->eta_sec != INT_MAX && je->nr_running) {
1167 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1168 strcpy(output, "-.-% done");
1172 sprintf(output, "%3.1f%% done", perc);
1175 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1176 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1178 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1179 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1181 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1182 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1183 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1184 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1186 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1187 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1188 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1189 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1198 char *dst = output + strlen(output);
1200 sprintf(dst, " - %s", eta_str);
1203 gfio_update_thread_status(output, perc);
1204 gdk_threads_leave();
1208 * Update ETA in main window for all clients
1210 static void gfio_update_all_eta(struct jobs_eta *je)
1212 struct gui *ui = &main_ui;
1213 static int eta_good;
1219 gdk_threads_enter();
1224 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1225 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1226 eta_to_str(eta_str, je->eta_sec);
1230 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1231 if (je->m_rate || je->t_rate) {
1234 mr = num2str(je->m_rate, 4, 0, i2p);
1235 tr = num2str(je->t_rate, 4, 0, i2p);
1236 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1237 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1240 } else if (je->m_iops || je->t_iops)
1241 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1243 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1244 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1245 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1246 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1249 if (je->eta_sec != INT_MAX && je->nr_running) {
1253 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1254 strcpy(output, "-.-% done");
1258 sprintf(output, "%3.1f%% done", perc);
1261 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1262 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1264 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1265 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1267 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1268 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1269 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1270 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1272 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1273 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1274 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1275 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1284 char *dst = output + strlen(output);
1286 sprintf(dst, " - %s", eta_str);
1289 gfio_update_thread_status(output, perc);
1290 gdk_threads_leave();
1293 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1295 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1296 struct gfio_client *gc = client->client_data;
1297 struct gui_entry *ge = gc->ge;
1298 const char *os, *arch;
1301 os = fio_get_os_string(probe->os);
1305 arch = fio_get_arch_string(probe->arch);
1310 client->name = strdup((char *) probe->hostname);
1312 gdk_threads_enter();
1314 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1315 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1316 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1317 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1318 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1320 gfio_set_connected(ge, 1);
1322 gdk_threads_leave();
1325 static void gfio_update_thread_status(char *status_message, double perc)
1327 struct gui *ui = &main_ui;
1328 static char message[100];
1329 const char *m = message;
1331 strncpy(message, status_message, sizeof(message) - 1);
1332 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1333 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1334 gtk_widget_queue_draw(ui->window);
1337 static void gfio_quit_op(struct fio_client *client)
1339 struct gfio_client *gc = client->client_data;
1341 gdk_threads_enter();
1342 gfio_set_connected(gc->ge, 0);
1343 gdk_threads_leave();
1346 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1348 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1349 struct gfio_client *gc = client->client_data;
1350 struct thread_options *o = &gc->o;
1351 struct gui_entry *ge = gc->ge;
1354 convert_thread_options_to_cpu(o, &p->top);
1356 gdk_threads_enter();
1358 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1360 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1361 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1362 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1364 sprintf(tmp, "%u", o->iodepth);
1365 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1369 gdk_threads_leave();
1372 static void gfio_client_timed_out(struct fio_client *client)
1374 struct gfio_client *gc = client->client_data;
1375 GtkWidget *dialog, *label, *content;
1378 gdk_threads_enter();
1380 gfio_set_connected(gc->ge, 0);
1381 clear_ge_ui_info(gc->ge);
1383 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1385 dialog = gtk_dialog_new_with_buttons("Timed out!",
1386 GTK_WINDOW(main_ui.window),
1387 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1388 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1390 /* gtk_dialog_get_content_area() is 2.14 and newer */
1391 content = GTK_DIALOG(dialog)->vbox;
1393 label = gtk_label_new((const gchar *) buf);
1394 gtk_container_add(GTK_CONTAINER(content), label);
1395 gtk_widget_show_all(dialog);
1396 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1398 gtk_dialog_run(GTK_DIALOG(dialog));
1399 gtk_widget_destroy(dialog);
1401 gdk_threads_leave();
1404 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1406 struct gfio_client *gc = client->client_data;
1408 gdk_threads_enter();
1410 gfio_set_connected(gc->ge, 0);
1413 entry_set_int_value(gc->err_entry, client->error);
1415 gdk_threads_leave();
1418 struct client_ops gfio_client_ops = {
1419 .text_op = gfio_text_op,
1420 .disk_util = gfio_disk_util_op,
1421 .thread_status = gfio_thread_status_op,
1422 .group_stats = gfio_group_stats_op,
1423 .jobs_eta = gfio_update_client_eta,
1424 .eta = gfio_update_all_eta,
1425 .probe = gfio_probe_op,
1426 .quit = gfio_quit_op,
1427 .add_job = gfio_add_job_op,
1428 .timed_out = gfio_client_timed_out,
1429 .stop = gfio_client_stop,
1430 .stay_connected = 1,
1433 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1434 __attribute__((unused)) gpointer data)
1439 static void *job_thread(void *arg)
1441 struct gui *ui = arg;
1443 ui->handler_running = 1;
1444 fio_handle_clients(&gfio_client_ops);
1445 ui->handler_running = 0;
1449 static int send_job_files(struct gui_entry *ge)
1453 for (i = 0; i < ge->nr_job_files; i++) {
1454 ret = fio_clients_send_ini(ge->job_files[i]);
1458 free(ge->job_files[i]);
1459 ge->job_files[i] = NULL;
1461 while (i < ge->nr_job_files) {
1462 free(ge->job_files[i]);
1463 ge->job_files[i] = NULL;
1470 static void *server_thread(void *arg)
1473 gfio_server_running = 1;
1474 fio_start_server(NULL);
1475 gfio_server_running = 0;
1479 static void gfio_start_server(void)
1481 struct gui *ui = &main_ui;
1483 if (!gfio_server_running) {
1484 gfio_server_running = 1;
1485 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1486 pthread_detach(ui->server_t);
1490 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1493 struct gui_entry *ge = data;
1494 struct gfio_client *gc = ge->client;
1496 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1497 fio_start_client(gc->client);
1500 static void file_open(GtkWidget *w, gpointer data);
1502 static void connect_clicked(GtkWidget *widget, gpointer data)
1504 struct gui_entry *ge = data;
1505 struct gfio_client *gc = ge->client;
1507 if (!ge->connected) {
1508 if (!ge->nr_job_files)
1509 file_open(widget, data);
1510 if (!ge->nr_job_files)
1513 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1514 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1515 if (!fio_client_connect(gc->client)) {
1516 if (!ge->ui->handler_running)
1517 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1518 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1519 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1522 fio_client_terminate(gc->client);
1523 gfio_set_connected(ge, 0);
1524 clear_ge_ui_info(ge);
1528 static void send_clicked(GtkWidget *widget, gpointer data)
1530 struct gui_entry *ge = data;
1532 if (send_job_files(ge)) {
1533 printf("Yeah, I didn't really like those options too much.\n");
1534 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1537 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1538 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1541 static GtkWidget *add_button(GtkWidget *buttonbox,
1542 struct button_spec *buttonspec, gpointer data)
1544 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1546 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1547 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1548 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1549 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1554 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1559 for (i = 0; i < nbuttons; i++)
1560 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1563 static void on_info_bar_response(GtkWidget *widget, gint response,
1566 struct gui *ui = &main_ui;
1568 if (response == GTK_RESPONSE_OK) {
1569 gtk_widget_destroy(widget);
1570 ui->error_info_bar = NULL;
1574 void report_error(GError *error)
1576 struct gui *ui = &main_ui;
1578 if (ui->error_info_bar == NULL) {
1579 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1582 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1583 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1586 ui->error_label = gtk_label_new(error->message);
1587 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1588 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1590 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1591 gtk_widget_show_all(ui->vbox);
1594 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1595 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1599 struct connection_widgets
1606 static void hostname_cb(GtkEntry *entry, gpointer data)
1608 struct connection_widgets *cw = data;
1609 int uses_net = 0, is_localhost = 0;
1614 * Check whether to display the 'auto start backend' box
1615 * or not. Show it if we are a localhost and using network,
1616 * or using a socket.
1618 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1619 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1624 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1625 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1626 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1627 !strcmp(text, "ip6-loopback"))
1631 if (!uses_net || is_localhost) {
1632 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1633 gtk_widget_set_sensitive(cw->button, 1);
1635 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1636 gtk_widget_set_sensitive(cw->button, 0);
1640 static int get_connection_details(char **host, int *port, int *type,
1643 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1644 struct connection_widgets cw;
1647 dialog = gtk_dialog_new_with_buttons("Connection details",
1648 GTK_WINDOW(main_ui.window),
1649 GTK_DIALOG_DESTROY_WITH_PARENT,
1650 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1651 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1653 frame = gtk_frame_new("Hostname / socket name");
1654 /* gtk_dialog_get_content_area() is 2.14 and newer */
1655 vbox = GTK_DIALOG(dialog)->vbox;
1656 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1658 box = gtk_vbox_new(FALSE, 6);
1659 gtk_container_add(GTK_CONTAINER(frame), box);
1661 hbox = gtk_hbox_new(TRUE, 10);
1662 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1663 cw.hentry = gtk_entry_new();
1664 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1665 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1667 frame = gtk_frame_new("Port");
1668 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1669 box = gtk_vbox_new(FALSE, 10);
1670 gtk_container_add(GTK_CONTAINER(frame), box);
1672 hbox = gtk_hbox_new(TRUE, 4);
1673 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1674 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1676 frame = gtk_frame_new("Type");
1677 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1678 box = gtk_vbox_new(FALSE, 10);
1679 gtk_container_add(GTK_CONTAINER(frame), box);
1681 hbox = gtk_hbox_new(TRUE, 4);
1682 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1684 cw.combo = gtk_combo_box_new_text();
1685 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1686 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1687 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1688 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1690 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1692 frame = gtk_frame_new("Options");
1693 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1694 box = gtk_vbox_new(FALSE, 10);
1695 gtk_container_add(GTK_CONTAINER(frame), box);
1697 hbox = gtk_hbox_new(TRUE, 4);
1698 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1700 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1701 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1702 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.");
1703 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1706 * Connect edit signal, so we can show/not-show the auto start button
1708 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1709 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1711 gtk_widget_show_all(dialog);
1713 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1714 gtk_widget_destroy(dialog);
1718 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1719 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1721 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1722 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1723 *type = Fio_client_ipv4;
1724 else if (!strncmp(typeentry, "IPv6", 4))
1725 *type = Fio_client_ipv6;
1727 *type = Fio_client_socket;
1730 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1732 gtk_widget_destroy(dialog);
1736 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1738 struct gfio_client *gc;
1740 gc = malloc(sizeof(*gc));
1741 memset(gc, 0, sizeof(*gc));
1743 gc->client = client;
1747 client->client_data = gc;
1750 static GtkWidget *new_client_page(struct gui_entry *ge);
1752 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1754 struct gui_entry *ge;
1756 ge = malloc(sizeof(*ge));
1757 memset(ge, 0, sizeof(*ge));
1758 INIT_FLIST_HEAD(&ge->list);
1759 flist_add_tail(&ge->list, &ui->list);
1765 * FIXME: need more handling here
1767 static void ge_destroy(GtkWidget *w, gpointer data)
1769 struct gui_entry *ge = data;
1771 flist_del(&ge->list);
1775 static struct gui_entry *get_new_ge_with_tab(const char *name)
1777 struct gui_entry *ge;
1779 ge = alloc_new_gui_entry(&main_ui);
1781 ge->vbox = new_client_page(ge);
1782 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1784 ge->page_label = gtk_label_new(name);
1785 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1787 gtk_widget_show_all(main_ui.window);
1791 static void file_new(GtkWidget *w, gpointer data)
1793 get_new_ge_with_tab("Untitled");
1797 * Return the 'ge' corresponding to the tab. If the active tab is the
1798 * main tab, open a new tab.
1800 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1802 struct flist_head *entry;
1803 struct gui_entry *ge;
1806 return get_new_ge_with_tab("Untitled");
1808 flist_for_each(entry, &main_ui.list) {
1809 ge = flist_entry(entry, struct gui_entry, list);
1810 if (ge->page_num == cur_page)
1817 static void file_open(GtkWidget *w, gpointer data)
1819 struct gui *ui = data;
1821 GSList *filenames, *fn_glist;
1822 GtkFileFilter *filter;
1824 int port, type, server_start;
1825 struct gui_entry *ge;
1829 * Creates new tab if current tab is the main window, or the
1830 * current tab already has a client.
1832 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1833 ge = get_ge_from_page(cur_page);
1835 ge = get_new_ge_with_tab("Untitled");
1837 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1839 dialog = gtk_file_chooser_dialog_new("Open File",
1840 GTK_WINDOW(ui->window),
1841 GTK_FILE_CHOOSER_ACTION_OPEN,
1842 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1843 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1845 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1847 filter = gtk_file_filter_new();
1848 gtk_file_filter_add_pattern(filter, "*.fio");
1849 gtk_file_filter_add_pattern(filter, "*.job");
1850 gtk_file_filter_add_pattern(filter, "*.ini");
1851 gtk_file_filter_add_mime_type(filter, "text/fio");
1852 gtk_file_filter_set_name(filter, "Fio job file");
1853 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1855 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1856 gtk_widget_destroy(dialog);
1860 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1862 gtk_widget_destroy(dialog);
1864 if (get_connection_details(&host, &port, &type, &server_start))
1867 filenames = fn_glist;
1868 while (filenames != NULL) {
1869 struct fio_client *client;
1871 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1872 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1875 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1879 error = g_error_new(g_quark_from_string("fio"), 1,
1880 "Failed to add client %s", host);
1881 report_error(error);
1882 g_error_free(error);
1884 gfio_client_added(ge, client);
1886 g_free(filenames->data);
1887 filenames = g_slist_next(filenames);
1892 gfio_start_server();
1894 g_slist_free(fn_glist);
1897 static void file_save(GtkWidget *w, gpointer data)
1899 struct gui *ui = data;
1902 dialog = gtk_file_chooser_dialog_new("Save File",
1903 GTK_WINDOW(ui->window),
1904 GTK_FILE_CHOOSER_ACTION_SAVE,
1905 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1906 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1909 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1910 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1912 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1915 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1916 // save_job_file(filename);
1919 gtk_widget_destroy(dialog);
1922 static void view_log_destroy(GtkWidget *w, gpointer data)
1924 struct gui *ui = (struct gui *) data;
1926 gtk_widget_ref(ui->log_tree);
1927 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1928 gtk_widget_destroy(w);
1929 ui->log_view = NULL;
1932 static void view_log(GtkWidget *w, gpointer data)
1934 GtkWidget *win, *scroll, *vbox, *box;
1935 struct gui *ui = (struct gui *) data;
1940 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1941 gtk_window_set_title(GTK_WINDOW(win), "Log");
1942 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1944 scroll = gtk_scrolled_window_new(NULL, NULL);
1946 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1948 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1950 box = gtk_hbox_new(TRUE, 0);
1951 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1952 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1953 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1955 vbox = gtk_vbox_new(TRUE, 5);
1956 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1958 gtk_container_add(GTK_CONTAINER(win), vbox);
1959 gtk_widget_show_all(win);
1962 static void preferences(GtkWidget *w, gpointer data)
1964 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1967 dialog = gtk_dialog_new_with_buttons("Preferences",
1968 GTK_WINDOW(main_ui.window),
1969 GTK_DIALOG_DESTROY_WITH_PARENT,
1970 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1971 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1974 frame = gtk_frame_new("Debug logging");
1975 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1977 vbox = gtk_vbox_new(FALSE, 6);
1978 gtk_container_add(GTK_CONTAINER(frame), vbox);
1980 box = gtk_hbox_new(FALSE, 6);
1981 gtk_container_add(GTK_CONTAINER(vbox), box);
1983 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1985 for (i = 0; i < FD_DEBUG_MAX; i++) {
1987 box = gtk_hbox_new(FALSE, 6);
1988 gtk_container_add(GTK_CONTAINER(vbox), box);
1992 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1993 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1994 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1997 frame = gtk_frame_new("Graph font");
1998 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1999 vbox = gtk_vbox_new(FALSE, 6);
2000 gtk_container_add(GTK_CONTAINER(frame), vbox);
2002 font = gtk_font_button_new();
2003 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2005 gtk_widget_show_all(dialog);
2007 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2008 gtk_widget_destroy(dialog);
2012 for (i = 0; i < FD_DEBUG_MAX; i++) {
2015 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2017 fio_debug |= (1UL << i);
2020 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2021 gtk_widget_destroy(dialog);
2024 static void about_dialog(GtkWidget *w, gpointer data)
2026 const char *authors[] = {
2027 "Jens Axboe <axboe@kernel.dk>",
2028 "Stephen Carmeron <stephenmcameron@gmail.com>",
2031 const char *license[] = {
2032 "Fio is free software; you can redistribute it and/or modify "
2033 "it under the terms of the GNU General Public License as published by "
2034 "the Free Software Foundation; either version 2 of the License, or "
2035 "(at your option) any later version.\n",
2036 "Fio is distributed in the hope that it will be useful, "
2037 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2038 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2039 "GNU General Public License for more details.\n",
2040 "You should have received a copy of the GNU General Public License "
2041 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2042 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2044 char *license_trans;
2046 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2047 license[2], "\n", NULL);
2049 gtk_show_about_dialog(NULL,
2050 "program-name", "gfio",
2051 "comments", "Gtk2 UI for fio",
2052 "license", license_trans,
2053 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2055 "version", fio_version_string,
2056 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2057 "logo-icon-name", "fio",
2059 "wrap-license", TRUE,
2062 g_free(license_trans);
2065 static GtkActionEntry menu_items[] = {
2066 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2067 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2068 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2069 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2070 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2071 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2072 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2073 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2074 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2075 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2077 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2079 static const gchar *ui_string = " \
2081 <menubar name=\"MainMenu\"> \
2082 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2083 <menuitem name=\"New\" action=\"NewFile\" /> \
2084 <separator name=\"Separator1\"/> \
2085 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2086 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2087 <separator name=\"Separator2\"/> \
2088 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2089 <separator name=\"Separator3\"/> \
2090 <menuitem name=\"Quit\" action=\"Quit\" /> \
2092 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2093 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2095 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2096 <menuitem name=\"About\" action=\"About\" /> \
2102 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2105 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2108 action_group = gtk_action_group_new("Menu");
2109 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2111 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2112 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2114 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2115 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2118 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2119 GtkWidget *vbox, GtkUIManager *ui_manager)
2121 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2124 static GtkWidget *new_client_page(struct gui_entry *ge)
2126 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2129 main_vbox = gtk_vbox_new(FALSE, 3);
2131 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2132 ge->topvbox = gtk_vbox_new(FALSE, 3);
2133 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2134 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2136 probe = gtk_frame_new("Job");
2137 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2138 probe_frame = gtk_vbox_new(FALSE, 3);
2139 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2141 probe_box = gtk_hbox_new(FALSE, 3);
2142 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2143 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2144 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2145 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2146 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2148 probe_box = gtk_hbox_new(FALSE, 3);
2149 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2151 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2152 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2153 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2154 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2155 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2156 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2158 probe_box = gtk_hbox_new(FALSE, 3);
2159 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2160 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2161 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2162 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2163 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2166 * Only add this if we have a commit rate
2169 probe_box = gtk_hbox_new(FALSE, 3);
2170 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2172 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2173 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2175 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2176 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2180 * Set up a drawing area and IOPS and bandwidth graphs
2182 gdk_color_parse("white", &white);
2183 ge->graphs.drawing_area = gtk_drawing_area_new();
2184 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2185 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2186 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2187 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2188 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2189 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2190 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2191 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2192 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2193 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2194 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2195 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2196 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2197 ge->graphs.drawing_area);
2198 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2201 setup_graphs(&ge->graphs);
2204 * Set up alignments for widgets at the bottom of ui,
2205 * align bottom left, expand horizontally but not vertically
2207 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2208 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2209 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2210 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2213 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2216 * Set up thread status progress bar
2218 ge->thread_status_pb = gtk_progress_bar_new();
2219 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2220 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2221 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2227 static GtkWidget *new_main_page(struct gui *ui)
2229 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2232 main_vbox = gtk_vbox_new(FALSE, 3);
2235 * Set up alignments for widgets at the top of ui,
2236 * align top left, expand horizontally but not vertically
2238 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2239 ui->topvbox = gtk_vbox_new(FALSE, 0);
2240 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2241 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2243 probe = gtk_frame_new("Run statistics");
2244 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2245 probe_frame = gtk_vbox_new(FALSE, 3);
2246 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2248 probe_box = gtk_hbox_new(FALSE, 3);
2249 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2250 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2251 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2252 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2253 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2256 * Only add this if we have a commit rate
2259 probe_box = gtk_hbox_new(FALSE, 3);
2260 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2262 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2263 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2265 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2266 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2270 * Set up a drawing area and IOPS and bandwidth graphs
2272 gdk_color_parse("white", &white);
2273 ui->graphs.drawing_area = gtk_drawing_area_new();
2274 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2275 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2276 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2277 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2278 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2279 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2280 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2281 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2282 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2283 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2284 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2285 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2286 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2287 ui->graphs.drawing_area);
2288 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2291 setup_graphs(&ui->graphs);
2294 * Set up alignments for widgets at the bottom of ui,
2295 * align bottom left, expand horizontally but not vertically
2297 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2298 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2299 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2300 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2304 * Set up thread status progress bar
2306 ui->thread_status_pb = gtk_progress_bar_new();
2307 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2308 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2309 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2314 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2315 guint page, gpointer data)
2321 static void init_ui(int *argc, char **argv[], struct gui *ui)
2323 GtkSettings *settings;
2324 GtkUIManager *uimanager;
2325 GtkWidget *menu, *vbox;
2327 /* Magical g*thread incantation, you just need this thread stuff.
2328 * Without it, the update that happens in gfio_update_thread_status
2329 * doesn't really happen in a timely fashion, you need expose events
2331 if (!g_thread_supported())
2332 g_thread_init(NULL);
2335 gtk_init(argc, argv);
2336 settings = gtk_settings_get_default();
2337 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2340 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2341 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2342 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2344 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2345 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2347 ui->vbox = gtk_vbox_new(FALSE, 0);
2348 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2350 uimanager = gtk_ui_manager_new();
2351 menu = get_menubar_menu(ui->window, uimanager, ui);
2352 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2354 ui->notebook = gtk_notebook_new();
2355 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2356 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2358 vbox = new_main_page(ui);
2360 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2362 gfio_ui_setup_log(ui);
2364 gtk_widget_show_all(ui->window);
2367 int main(int argc, char *argv[], char *envp[])
2369 if (initialize_fio(envp))
2371 if (fio_init_options())
2374 memset(&main_ui, 0, sizeof(main_ui));
2375 INIT_FLIST_HEAD(&main_ui.list);
2377 init_ui(&argc, &argv, &main_ui);
2379 gdk_threads_enter();
2381 gdk_threads_leave();