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 view_log(GtkWidget *w, gpointer data);
40 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
42 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
44 static void connect_clicked(GtkWidget *widget, gpointer data);
45 static void start_job_clicked(GtkWidget *widget, gpointer data);
46 static void send_clicked(GtkWidget *widget, gpointer data);
48 static struct button_spec {
49 const char *buttontext;
51 const char *tooltiptext;
52 const int start_insensitive;
53 } buttonspeclist[] = {
54 #define CONNECT_BUTTON 0
56 #define START_JOB_BUTTON 2
57 { "Connect", connect_clicked, "Connect to host", 0 },
58 { "Send", send_clicked, "Send job description to host", 1 },
59 { "Start Job", start_job_clicked,
60 "Start the current job on the server", 1 },
82 GtkWidget *write_iops;
88 #define DRAWING_AREA_XDIM 1000
89 #define DRAWING_AREA_YDIM 400
90 GtkWidget *drawing_area;
91 int drawing_area_xdim;
92 int drawing_area_ydim;
94 struct graph *iops_graph;
95 struct graph *bandwidth_graph;
99 * Main window widgets and data
106 GtkWidget *bottomalign;
107 GtkWidget *thread_status_pb;
108 GtkWidget *buttonbox;
109 GtkWidget *scrolled_window;
111 GtkWidget *error_info_bar;
112 GtkWidget *error_label;
113 GtkListStore *log_model;
116 struct gfio_graphs graphs;
117 struct probe_widget probe;
118 struct eta_widget eta;
124 struct flist_head list;
131 struct flist_head list;
137 GtkWidget *bottomalign;
138 GtkWidget *thread_status_pb;
139 GtkWidget *buttonbox;
140 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
141 GtkWidget *scrolled_window;
143 GtkWidget *error_info_bar;
144 GtkWidget *error_label;
145 GtkWidget *results_notebook;
146 GtkWidget *results_window;
147 GtkListStore *log_model;
150 struct gfio_graphs graphs;
151 struct probe_widget probe;
152 struct eta_widget eta;
153 GtkWidget *page_label;
157 struct gfio_client *client;
163 struct gui_entry *ge;
164 struct fio_client *client;
165 GtkWidget *results_widget;
166 GtkWidget *disk_util_frame;
167 GtkWidget *err_entry;
168 unsigned int job_added;
169 struct thread_options o;
172 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
173 static void gfio_update_thread_status_all(char *status_message, double perc);
175 static struct graph *setup_iops_graph(void)
179 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
180 graph_title(g, "IOPS");
181 graph_x_title(g, "Time (secs)");
182 graph_y_title(g, "IOs / sec");
183 graph_add_label(g, "Read IOPS");
184 graph_add_label(g, "Write IOPS");
185 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
186 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
187 line_graph_set_data_count_limit(g, 100);
191 static struct graph *setup_bandwidth_graph(void)
195 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
196 graph_title(g, "Bandwidth");
197 graph_x_title(g, "Time (secs)");
198 graph_y_title(g, "Kbytes / sec");
199 graph_add_label(g, "Read Bandwidth");
200 graph_add_label(g, "Write Bandwidth");
201 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
202 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
203 line_graph_set_data_count_limit(g, 100);
207 static void setup_graphs(struct gfio_graphs *g)
209 g->iops_graph = setup_iops_graph();
210 g->bandwidth_graph = setup_bandwidth_graph();
213 static void clear_ge_ui_info(struct gui_entry *ge)
215 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
216 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
217 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
218 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
219 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
220 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
221 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
225 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
226 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
227 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
228 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
231 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
233 GtkWidget *entry, *frame;
235 frame = gtk_frame_new(label);
236 entry = gtk_entry_new();
237 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
238 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
239 gtk_container_add(GTK_CONTAINER(frame), entry);
244 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
246 GtkWidget *label_widget;
249 frame = gtk_frame_new(label);
250 label_widget = gtk_label_new(NULL);
251 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
252 gtk_container_add(GTK_CONTAINER(frame), label_widget);
257 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
259 GtkWidget *button, *box;
261 box = gtk_hbox_new(FALSE, 3);
262 gtk_container_add(GTK_CONTAINER(hbox), box);
264 button = gtk_spin_button_new_with_range(min, max, 1.0);
265 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
267 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
268 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
273 static void gfio_set_connected(struct gui_entry *ge, int connected)
276 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
278 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
279 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
282 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
283 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
284 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
285 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
289 static void label_set_int_value(GtkWidget *entry, unsigned int val)
293 sprintf(tmp, "%u", val);
294 gtk_label_set_text(GTK_LABEL(entry), tmp);
297 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
301 sprintf(tmp, "%u", val);
302 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
306 #define ALIGN_RIGHT 2
310 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
312 GtkCellRenderer *renderer;
313 GtkTreeViewColumn *col;
314 double xalign = 0.0; /* left as default */
315 PangoAlignment align;
318 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
319 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
321 visible = !(flags & INVISIBLE);
323 renderer = gtk_cell_renderer_text_new();
324 col = gtk_tree_view_column_new();
326 gtk_tree_view_column_set_title(col, title);
327 if (!(flags & UNSORTABLE))
328 gtk_tree_view_column_set_sort_column_id(col, index);
329 gtk_tree_view_column_set_resizable(col, TRUE);
330 gtk_tree_view_column_pack_start(col, renderer, TRUE);
331 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
332 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
334 case PANGO_ALIGN_LEFT:
337 case PANGO_ALIGN_CENTER:
340 case PANGO_ALIGN_RIGHT:
344 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
345 gtk_tree_view_column_set_visible(col, visible);
346 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
350 static void gfio_ui_setup_log(struct gui *ui)
352 GtkTreeSelection *selection;
354 GtkWidget *tree_view;
356 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
358 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
359 gtk_widget_set_can_focus(tree_view, FALSE);
361 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
362 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
363 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
364 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
366 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
367 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
368 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
369 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
371 ui->log_model = model;
372 ui->log_tree = tree_view;
375 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
381 GType types[FIO_IO_U_LIST_MAX_LEN];
382 GtkWidget *tree_view;
383 GtkTreeSelection *selection;
388 for (i = 0; i < len; i++)
389 types[i] = G_TYPE_INT;
391 model = gtk_list_store_newv(len, types);
393 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
394 gtk_widget_set_can_focus(tree_view, FALSE);
396 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
397 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
399 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
400 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
402 for (i = 0; i < len; i++) {
405 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
406 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
409 gtk_list_store_append(model, &iter);
411 for (i = 0; i < len; i++) {
413 ovals[i] = (ovals[i] + 999) / 1000;
414 gtk_list_store_set(model, &iter, i, ovals[i], -1);
420 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
423 unsigned int *io_u_plat = ts->io_u_plat[ddir];
424 unsigned long nr = ts->clat_stat[ddir].samples;
425 fio_fp64_t *plist = ts->percentile_list;
426 unsigned int *ovals, len, minv, maxv, scale_down;
428 GtkWidget *tree_view, *frame, *hbox;
431 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
436 * We default to usecs, but if the value range is such that we
437 * should scale down to msecs, do that.
439 if (minv > 2000 && maxv > 99999) {
447 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
449 sprintf(tmp, "Completion percentiles (%s)", base);
450 frame = gtk_frame_new(tmp);
451 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
453 hbox = gtk_hbox_new(FALSE, 3);
454 gtk_container_add(GTK_CONTAINER(frame), hbox);
456 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
462 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
463 unsigned long max, double mean, double dev)
465 const char *base = "(usec)";
466 GtkWidget *hbox, *label, *frame;
470 if (!usec_to_msec(&min, &max, &mean, &dev))
473 minp = num2str(min, 6, 1, 0);
474 maxp = num2str(max, 6, 1, 0);
476 sprintf(tmp, "%s %s", name, base);
477 frame = gtk_frame_new(tmp);
478 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
480 hbox = gtk_hbox_new(FALSE, 3);
481 gtk_container_add(GTK_CONTAINER(frame), hbox);
483 label = new_info_label_in_frame(hbox, "Minimum");
484 gtk_label_set_text(GTK_LABEL(label), minp);
485 label = new_info_label_in_frame(hbox, "Maximum");
486 gtk_label_set_text(GTK_LABEL(label), maxp);
487 label = new_info_label_in_frame(hbox, "Average");
488 sprintf(tmp, "%5.02f", mean);
489 gtk_label_set_text(GTK_LABEL(label), tmp);
490 label = new_info_label_in_frame(hbox, "Standard deviation");
491 sprintf(tmp, "%5.02f", dev);
492 gtk_label_set_text(GTK_LABEL(label), tmp);
503 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
504 struct thread_stat *ts, int ddir)
506 const char *ddir_label[2] = { "Read", "Write" };
507 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
508 unsigned long min[3], max[3], runt;
509 unsigned long long bw, iops;
510 unsigned int flags = 0;
511 double mean[3], dev[3];
512 char *io_p, *bw_p, *iops_p;
515 if (!ts->runtime[ddir])
518 i2p = is_power_of_2(rs->kb_base);
519 runt = ts->runtime[ddir];
521 bw = (1000 * ts->io_bytes[ddir]) / runt;
522 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
523 bw_p = num2str(bw, 6, 1, i2p);
525 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
526 iops_p = num2str(iops, 6, 1, 0);
528 box = gtk_hbox_new(FALSE, 3);
529 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
531 frame = gtk_frame_new(ddir_label[ddir]);
532 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
534 main_vbox = gtk_vbox_new(FALSE, 3);
535 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
537 box = gtk_hbox_new(FALSE, 3);
538 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
540 label = new_info_label_in_frame(box, "IO");
541 gtk_label_set_text(GTK_LABEL(label), io_p);
542 label = new_info_label_in_frame(box, "Bandwidth");
543 gtk_label_set_text(GTK_LABEL(label), bw_p);
544 label = new_info_label_in_frame(box, "IOPS");
545 gtk_label_set_text(GTK_LABEL(label), iops_p);
546 label = new_info_label_in_frame(box, "Runtime (msec)");
547 label_set_int_value(label, ts->runtime[ddir]);
549 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
550 double p_of_agg = 100.0;
551 const char *bw_str = "KB";
555 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
556 if (p_of_agg > 100.0)
560 if (mean[0] > 999999.9) {
568 sprintf(tmp, "Bandwidth (%s)", bw_str);
569 frame = gtk_frame_new(tmp);
570 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
572 box = gtk_hbox_new(FALSE, 3);
573 gtk_container_add(GTK_CONTAINER(frame), box);
575 label = new_info_label_in_frame(box, "Minimum");
576 label_set_int_value(label, min[0]);
577 label = new_info_label_in_frame(box, "Maximum");
578 label_set_int_value(label, max[0]);
579 label = new_info_label_in_frame(box, "Percentage of jobs");
580 sprintf(tmp, "%3.2f%%", p_of_agg);
581 gtk_label_set_text(GTK_LABEL(label), tmp);
582 label = new_info_label_in_frame(box, "Average");
583 sprintf(tmp, "%5.02f", mean[0]);
584 gtk_label_set_text(GTK_LABEL(label), tmp);
585 label = new_info_label_in_frame(box, "Standard deviation");
586 sprintf(tmp, "%5.02f", dev[0]);
587 gtk_label_set_text(GTK_LABEL(label), tmp);
590 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
592 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
594 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
598 frame = gtk_frame_new("Latency");
599 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
601 vbox = gtk_vbox_new(FALSE, 3);
602 gtk_container_add(GTK_CONTAINER(frame), vbox);
604 if (flags & GFIO_SLAT)
605 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
606 if (flags & GFIO_CLAT)
607 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
608 if (flags & GFIO_LAT)
609 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
612 if (ts->clat_percentiles)
613 gfio_show_clat_percentiles(main_vbox, ts, ddir);
621 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
624 GtkWidget *tree_view;
625 GtkTreeSelection *selection;
632 * Check if all are empty, in which case don't bother
634 for (i = 0, skipped = 0; i < num; i++)
641 types = malloc(num * sizeof(GType));
643 for (i = 0; i < num; i++)
644 types[i] = G_TYPE_STRING;
646 model = gtk_list_store_newv(num, types);
650 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
651 gtk_widget_set_can_focus(tree_view, FALSE);
653 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
654 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
656 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
657 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
659 for (i = 0; i < num; i++)
660 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
662 gtk_list_store_append(model, &iter);
664 for (i = 0; i < num; i++) {
668 sprintf(fbuf, "0.00");
670 sprintf(fbuf, "%3.2f%%", lat[i]);
672 gtk_list_store_set(model, &iter, i, fbuf, -1);
678 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
680 GtkWidget *box, *frame, *tree_view;
681 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
682 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
683 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
684 "250", "500", "750", "1000", };
685 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
686 "250", "500", "750", "1000", "2000",
689 stat_calc_lat_u(ts, io_u_lat_u);
690 stat_calc_lat_m(ts, io_u_lat_m);
692 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
694 frame = gtk_frame_new("Latency buckets (usec)");
695 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
697 box = gtk_hbox_new(FALSE, 3);
698 gtk_container_add(GTK_CONTAINER(frame), box);
699 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
702 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
704 frame = gtk_frame_new("Latency buckets (msec)");
705 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
707 box = gtk_hbox_new(FALSE, 3);
708 gtk_container_add(GTK_CONTAINER(frame), box);
709 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
713 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
715 GtkWidget *box, *frame, *entry;
716 double usr_cpu, sys_cpu;
717 unsigned long runtime;
720 runtime = ts->total_run_time;
722 double runt = (double) runtime;
724 usr_cpu = (double) ts->usr_time * 100 / runt;
725 sys_cpu = (double) ts->sys_time * 100 / runt;
731 frame = gtk_frame_new("OS resources");
732 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
734 box = gtk_hbox_new(FALSE, 3);
735 gtk_container_add(GTK_CONTAINER(frame), box);
737 entry = new_info_entry_in_frame(box, "User CPU");
738 sprintf(tmp, "%3.2f%%", usr_cpu);
739 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
740 entry = new_info_entry_in_frame(box, "System CPU");
741 sprintf(tmp, "%3.2f%%", sys_cpu);
742 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
743 entry = new_info_entry_in_frame(box, "Context switches");
744 entry_set_int_value(entry, ts->ctx);
745 entry = new_info_entry_in_frame(box, "Major faults");
746 entry_set_int_value(entry, ts->majf);
747 entry = new_info_entry_in_frame(box, "Minor faults");
748 entry_set_int_value(entry, ts->minf);
750 static void gfio_add_sc_depths_tree(GtkListStore *model,
751 struct thread_stat *ts, unsigned int len,
754 double io_u_dist[FIO_IO_U_MAP_NR];
756 /* Bits 0, and 3-8 */
757 const int add_mask = 0x1f9;
761 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
763 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
765 gtk_list_store_append(model, &iter);
767 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
769 for (i = 1, j = 0; i < len; i++) {
772 if (!(add_mask & (1UL << (i - 1))))
773 sprintf(fbuf, "0.0%%");
775 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
779 gtk_list_store_set(model, &iter, i, fbuf, -1);
784 static void gfio_add_total_depths_tree(GtkListStore *model,
785 struct thread_stat *ts, unsigned int len)
787 double io_u_dist[FIO_IO_U_MAP_NR];
789 /* Bits 1-6, and 8 */
790 const int add_mask = 0x17e;
793 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
795 gtk_list_store_append(model, &iter);
797 gtk_list_store_set(model, &iter, 0, "Total", -1);
799 for (i = 1, j = 0; i < len; i++) {
802 if (!(add_mask & (1UL << (i - 1))))
803 sprintf(fbuf, "0.0%%");
805 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
809 gtk_list_store_set(model, &iter, i, fbuf, -1);
814 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
816 GtkWidget *frame, *box, *tree_view;
817 GtkTreeSelection *selection;
819 GType types[FIO_IO_U_MAP_NR + 1];
822 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
824 frame = gtk_frame_new("IO depths");
825 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
827 box = gtk_hbox_new(FALSE, 3);
828 gtk_container_add(GTK_CONTAINER(frame), box);
830 for (i = 0; i < NR_LABELS; i++)
831 types[i] = G_TYPE_STRING;
833 model = gtk_list_store_newv(NR_LABELS, types);
835 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
836 gtk_widget_set_can_focus(tree_view, FALSE);
838 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
839 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
841 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
842 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
844 for (i = 0; i < NR_LABELS; i++)
845 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
847 gfio_add_total_depths_tree(model, ts, NR_LABELS);
848 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
849 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
851 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
854 static gboolean results_window_delete(GtkWidget *w, gpointer data)
856 struct gui_entry *ge = (struct gui_entry *) data;
858 gtk_widget_destroy(w);
859 ge->results_window = NULL;
860 ge->results_notebook = NULL;
864 static GtkWidget *get_results_window(struct gui_entry *ge)
866 GtkWidget *win, *notebook;
868 if (ge->results_window)
869 return ge->results_notebook;
871 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
872 gtk_window_set_title(GTK_WINDOW(win), "Results");
873 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
874 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
875 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
877 notebook = gtk_notebook_new();
878 gtk_container_add(GTK_CONTAINER(win), notebook);
880 ge->results_window = win;
881 ge->results_notebook = notebook;
882 return ge->results_notebook;
885 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
886 struct group_run_stats *rs)
888 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
889 struct gfio_client *gc = client->client_data;
893 res_win = get_results_window(gc->ge);
895 scroll = gtk_scrolled_window_new(NULL, NULL);
896 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
897 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
899 vbox = gtk_vbox_new(FALSE, 3);
901 box = gtk_hbox_new(FALSE, 0);
902 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
904 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
906 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
908 gc->results_widget = vbox;
910 entry = new_info_entry_in_frame(box, "Name");
911 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
912 if (strlen(ts->description)) {
913 entry = new_info_entry_in_frame(box, "Description");
914 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
916 entry = new_info_entry_in_frame(box, "Group ID");
917 entry_set_int_value(entry, ts->groupid);
918 entry = new_info_entry_in_frame(box, "Jobs");
919 entry_set_int_value(entry, ts->members);
920 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
921 entry_set_int_value(entry, ts->error);
922 entry = new_info_entry_in_frame(box, "PID");
923 entry_set_int_value(entry, ts->pid);
925 if (ts->io_bytes[DDIR_READ])
926 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
927 if (ts->io_bytes[DDIR_WRITE])
928 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
930 gfio_show_latency_buckets(vbox, ts);
931 gfio_show_cpu_usage(vbox, ts);
932 gfio_show_io_depths(vbox, ts);
934 gtk_widget_show_all(gc->ge->results_window);
938 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
940 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
941 struct gui *ui = &main_ui;
945 char tmp[64], timebuf[80];
948 tm = localtime(&sec);
949 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
950 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
954 gtk_list_store_append(ui->log_model, &iter);
955 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
956 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
957 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
958 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
960 if (p->level == FIO_LOG_ERR)
961 view_log(NULL, (gpointer) ui);
966 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
968 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
969 struct gfio_client *gc = client->client_data;
970 GtkWidget *box, *frame, *entry, *vbox;
976 if (!gc->results_widget)
979 if (!gc->disk_util_frame) {
980 gc->disk_util_frame = gtk_frame_new("Disk utilization");
981 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
984 vbox = gtk_vbox_new(FALSE, 3);
985 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
987 frame = gtk_frame_new((char *) p->dus.name);
988 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
990 box = gtk_vbox_new(FALSE, 3);
991 gtk_container_add(GTK_CONTAINER(frame), box);
993 frame = gtk_frame_new("Read");
994 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
995 vbox = gtk_hbox_new(TRUE, 3);
996 gtk_container_add(GTK_CONTAINER(frame), vbox);
997 entry = new_info_entry_in_frame(vbox, "IOs");
998 entry_set_int_value(entry, p->dus.ios[0]);
999 entry = new_info_entry_in_frame(vbox, "Merges");
1000 entry_set_int_value(entry, p->dus.merges[0]);
1001 entry = new_info_entry_in_frame(vbox, "Sectors");
1002 entry_set_int_value(entry, p->dus.sectors[0]);
1003 entry = new_info_entry_in_frame(vbox, "Ticks");
1004 entry_set_int_value(entry, p->dus.ticks[0]);
1006 frame = gtk_frame_new("Write");
1007 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1008 vbox = gtk_hbox_new(TRUE, 3);
1009 gtk_container_add(GTK_CONTAINER(frame), vbox);
1010 entry = new_info_entry_in_frame(vbox, "IOs");
1011 entry_set_int_value(entry, p->dus.ios[1]);
1012 entry = new_info_entry_in_frame(vbox, "Merges");
1013 entry_set_int_value(entry, p->dus.merges[1]);
1014 entry = new_info_entry_in_frame(vbox, "Sectors");
1015 entry_set_int_value(entry, p->dus.sectors[1]);
1016 entry = new_info_entry_in_frame(vbox, "Ticks");
1017 entry_set_int_value(entry, p->dus.ticks[1]);
1019 frame = gtk_frame_new("Shared");
1020 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1021 vbox = gtk_hbox_new(TRUE, 3);
1022 gtk_container_add(GTK_CONTAINER(frame), vbox);
1023 entry = new_info_entry_in_frame(vbox, "IO ticks");
1024 entry_set_int_value(entry, p->dus.io_ticks);
1025 entry = new_info_entry_in_frame(vbox, "Time in queue");
1026 entry_set_int_value(entry, p->dus.time_in_queue);
1030 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1034 sprintf(tmp, "%3.2f%%", util);
1035 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1036 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1038 gtk_widget_show_all(gc->results_widget);
1040 gdk_threads_leave();
1043 extern int sum_stat_clients;
1044 extern struct thread_stat client_ts;
1045 extern struct group_run_stats client_gs;
1047 static int sum_stat_nr;
1049 static void gfio_thread_status_op(struct fio_client *client,
1050 struct fio_net_cmd *cmd)
1052 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1054 gfio_display_ts(client, &p->ts, &p->rs);
1056 if (sum_stat_clients == 1)
1059 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1060 sum_group_stats(&client_gs, &p->rs);
1062 client_ts.members++;
1063 client_ts.groupid = p->ts.groupid;
1065 if (++sum_stat_nr == sum_stat_clients) {
1066 strcpy(client_ts.name, "All clients");
1067 gfio_display_ts(client, &client_ts, &client_gs);
1071 static void gfio_group_stats_op(struct fio_client *client,
1072 struct fio_net_cmd *cmd)
1074 /* We're ignoring group stats for now */
1077 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1080 struct gfio_graphs *g = data;
1082 g->drawing_area_xdim = w->allocation.width;
1083 g->drawing_area_ydim = w->allocation.height;
1087 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1089 struct gfio_graphs *g = p;
1092 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1093 g->drawing_area_ydim);
1094 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1095 g->drawing_area_ydim);
1096 cr = gdk_cairo_create(w->window);
1098 cairo_set_source_rgb(cr, 0, 0, 0);
1101 cairo_translate(cr, 0, 0);
1102 line_graph_draw(g->bandwidth_graph, cr);
1107 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1108 line_graph_draw(g->iops_graph, cr);
1117 * Client specific ETA
1119 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1121 struct gfio_client *gc = client->client_data;
1122 struct gui_entry *ge = gc->ge;
1123 static int eta_good;
1130 gdk_threads_enter();
1135 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1136 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1137 eta_to_str(eta_str, je->eta_sec);
1140 sprintf(tmp, "%u", je->nr_running);
1141 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1142 sprintf(tmp, "%u", je->files_open);
1143 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1146 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1147 if (je->m_rate || je->t_rate) {
1150 mr = num2str(je->m_rate, 4, 0, i2p);
1151 tr = num2str(je->t_rate, 4, 0, i2p);
1152 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1153 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1156 } else if (je->m_iops || je->t_iops)
1157 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1159 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1160 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1161 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1162 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1165 if (je->eta_sec != INT_MAX && je->nr_running) {
1169 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1170 strcpy(output, "-.-% done");
1174 sprintf(output, "%3.1f%% done", perc);
1177 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1178 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1180 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1181 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1183 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1185 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1188 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1189 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1190 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1191 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1200 char *dst = output + strlen(output);
1202 sprintf(dst, " - %s", eta_str);
1205 gfio_update_thread_status(ge, output, perc);
1206 gdk_threads_leave();
1210 * Update ETA in main window for all clients
1212 static void gfio_update_all_eta(struct jobs_eta *je)
1214 struct gui *ui = &main_ui;
1215 static int eta_good;
1221 gdk_threads_enter();
1226 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1227 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1228 eta_to_str(eta_str, je->eta_sec);
1232 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1233 if (je->m_rate || je->t_rate) {
1236 mr = num2str(je->m_rate, 4, 0, i2p);
1237 tr = num2str(je->t_rate, 4, 0, i2p);
1238 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1239 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1242 } else if (je->m_iops || je->t_iops)
1243 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1245 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1246 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1247 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1248 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1251 if (je->eta_sec != INT_MAX && je->nr_running) {
1255 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1256 strcpy(output, "-.-% done");
1260 sprintf(output, "%3.1f%% done", perc);
1263 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1264 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1266 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1267 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1269 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1270 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1271 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1272 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1274 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1275 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1276 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1277 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1286 char *dst = output + strlen(output);
1288 sprintf(dst, " - %s", eta_str);
1291 gfio_update_thread_status_all(output, perc);
1292 gdk_threads_leave();
1295 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1297 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1298 struct gfio_client *gc = client->client_data;
1299 struct gui_entry *ge = gc->ge;
1300 const char *os, *arch;
1303 os = fio_get_os_string(probe->os);
1307 arch = fio_get_arch_string(probe->arch);
1312 client->name = strdup((char *) probe->hostname);
1314 gdk_threads_enter();
1316 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1317 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1318 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1319 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1320 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1322 gfio_set_connected(ge, 1);
1324 gdk_threads_leave();
1327 static void gfio_update_thread_status(struct gui_entry *ge,
1328 char *status_message, double perc)
1330 static char message[100];
1331 const char *m = message;
1333 strncpy(message, status_message, sizeof(message) - 1);
1334 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1335 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1336 gtk_widget_queue_draw(main_ui.window);
1339 static void gfio_update_thread_status_all(char *status_message, double perc)
1341 struct gui *ui = &main_ui;
1342 static char message[100];
1343 const char *m = message;
1345 strncpy(message, status_message, sizeof(message) - 1);
1346 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1347 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1348 gtk_widget_queue_draw(ui->window);
1351 static void gfio_quit_op(struct fio_client *client)
1353 struct gfio_client *gc = client->client_data;
1355 gdk_threads_enter();
1356 gfio_set_connected(gc->ge, 0);
1357 gdk_threads_leave();
1360 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1362 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1363 struct gfio_client *gc = client->client_data;
1364 struct thread_options *o = &gc->o;
1365 struct gui_entry *ge = gc->ge;
1368 convert_thread_options_to_cpu(o, &p->top);
1370 gdk_threads_enter();
1372 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1374 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1375 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1376 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1378 sprintf(tmp, "%u", o->iodepth);
1379 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1383 gdk_threads_leave();
1386 static void gfio_client_timed_out(struct fio_client *client)
1388 struct gfio_client *gc = client->client_data;
1389 GtkWidget *dialog, *label, *content;
1392 gdk_threads_enter();
1394 gfio_set_connected(gc->ge, 0);
1395 clear_ge_ui_info(gc->ge);
1397 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1399 dialog = gtk_dialog_new_with_buttons("Timed out!",
1400 GTK_WINDOW(main_ui.window),
1401 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1402 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1404 /* gtk_dialog_get_content_area() is 2.14 and newer */
1405 content = GTK_DIALOG(dialog)->vbox;
1407 label = gtk_label_new((const gchar *) buf);
1408 gtk_container_add(GTK_CONTAINER(content), label);
1409 gtk_widget_show_all(dialog);
1410 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1412 gtk_dialog_run(GTK_DIALOG(dialog));
1413 gtk_widget_destroy(dialog);
1415 gdk_threads_leave();
1418 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1420 struct gfio_client *gc = client->client_data;
1422 gdk_threads_enter();
1424 gfio_set_connected(gc->ge, 0);
1427 entry_set_int_value(gc->err_entry, client->error);
1429 gdk_threads_leave();
1432 struct client_ops gfio_client_ops = {
1433 .text_op = gfio_text_op,
1434 .disk_util = gfio_disk_util_op,
1435 .thread_status = gfio_thread_status_op,
1436 .group_stats = gfio_group_stats_op,
1437 .jobs_eta = gfio_update_client_eta,
1438 .eta = gfio_update_all_eta,
1439 .probe = gfio_probe_op,
1440 .quit = gfio_quit_op,
1441 .add_job = gfio_add_job_op,
1442 .timed_out = gfio_client_timed_out,
1443 .stop = gfio_client_stop,
1444 .stay_connected = 1,
1447 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1448 __attribute__((unused)) gpointer data)
1453 static void *job_thread(void *arg)
1455 struct gui *ui = arg;
1457 ui->handler_running = 1;
1458 fio_handle_clients(&gfio_client_ops);
1459 ui->handler_running = 0;
1463 static int send_job_files(struct gui_entry *ge)
1465 struct gfio_client *gc = ge->client;
1468 for (i = 0; i < ge->nr_job_files; i++) {
1469 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1473 free(ge->job_files[i]);
1474 ge->job_files[i] = NULL;
1476 while (i < ge->nr_job_files) {
1477 free(ge->job_files[i]);
1478 ge->job_files[i] = NULL;
1485 static void *server_thread(void *arg)
1488 gfio_server_running = 1;
1489 fio_start_server(NULL);
1490 gfio_server_running = 0;
1494 static void gfio_start_server(void)
1496 struct gui *ui = &main_ui;
1498 if (!gfio_server_running) {
1499 gfio_server_running = 1;
1500 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1501 pthread_detach(ui->server_t);
1505 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1508 struct gui_entry *ge = data;
1509 struct gfio_client *gc = ge->client;
1511 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1512 fio_start_client(gc->client);
1515 static void file_open(GtkWidget *w, gpointer data);
1517 static void connect_clicked(GtkWidget *widget, gpointer data)
1519 struct gui_entry *ge = data;
1520 struct gfio_client *gc = ge->client;
1522 if (!ge->connected) {
1523 if (!ge->nr_job_files)
1524 file_open(widget, data);
1525 if (!ge->nr_job_files)
1528 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1529 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1530 if (!fio_client_connect(gc->client)) {
1531 if (!ge->ui->handler_running)
1532 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1533 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1534 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1537 fio_client_terminate(gc->client);
1538 gfio_set_connected(ge, 0);
1539 clear_ge_ui_info(ge);
1543 static void send_clicked(GtkWidget *widget, gpointer data)
1545 struct gui_entry *ge = data;
1547 if (send_job_files(ge)) {
1548 printf("Yeah, I didn't really like those options too much.\n");
1549 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1552 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1553 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1556 static GtkWidget *add_button(GtkWidget *buttonbox,
1557 struct button_spec *buttonspec, gpointer data)
1559 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1561 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1562 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1563 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1564 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1569 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1574 for (i = 0; i < nbuttons; i++)
1575 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1578 static void on_info_bar_response(GtkWidget *widget, gint response,
1581 struct gui *ui = &main_ui;
1583 if (response == GTK_RESPONSE_OK) {
1584 gtk_widget_destroy(widget);
1585 ui->error_info_bar = NULL;
1589 void report_error(GError *error)
1591 struct gui *ui = &main_ui;
1593 if (ui->error_info_bar == NULL) {
1594 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1597 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1598 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1601 ui->error_label = gtk_label_new(error->message);
1602 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1603 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1605 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1606 gtk_widget_show_all(ui->vbox);
1609 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1610 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1614 struct connection_widgets
1621 static void hostname_cb(GtkEntry *entry, gpointer data)
1623 struct connection_widgets *cw = data;
1624 int uses_net = 0, is_localhost = 0;
1629 * Check whether to display the 'auto start backend' box
1630 * or not. Show it if we are a localhost and using network,
1631 * or using a socket.
1633 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1634 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1639 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1640 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1641 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1642 !strcmp(text, "ip6-loopback"))
1646 if (!uses_net || is_localhost) {
1647 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1648 gtk_widget_set_sensitive(cw->button, 1);
1650 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1651 gtk_widget_set_sensitive(cw->button, 0);
1655 static int get_connection_details(char **host, int *port, int *type,
1658 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1659 struct connection_widgets cw;
1662 dialog = gtk_dialog_new_with_buttons("Connection details",
1663 GTK_WINDOW(main_ui.window),
1664 GTK_DIALOG_DESTROY_WITH_PARENT,
1665 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1666 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1668 frame = gtk_frame_new("Hostname / socket name");
1669 /* gtk_dialog_get_content_area() is 2.14 and newer */
1670 vbox = GTK_DIALOG(dialog)->vbox;
1671 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1673 box = gtk_vbox_new(FALSE, 6);
1674 gtk_container_add(GTK_CONTAINER(frame), box);
1676 hbox = gtk_hbox_new(TRUE, 10);
1677 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1678 cw.hentry = gtk_entry_new();
1679 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1680 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1682 frame = gtk_frame_new("Port");
1683 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1684 box = gtk_vbox_new(FALSE, 10);
1685 gtk_container_add(GTK_CONTAINER(frame), box);
1687 hbox = gtk_hbox_new(TRUE, 4);
1688 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1689 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1691 frame = gtk_frame_new("Type");
1692 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1693 box = gtk_vbox_new(FALSE, 10);
1694 gtk_container_add(GTK_CONTAINER(frame), box);
1696 hbox = gtk_hbox_new(TRUE, 4);
1697 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1699 cw.combo = gtk_combo_box_new_text();
1700 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1701 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1702 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1703 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1705 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1707 frame = gtk_frame_new("Options");
1708 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1709 box = gtk_vbox_new(FALSE, 10);
1710 gtk_container_add(GTK_CONTAINER(frame), box);
1712 hbox = gtk_hbox_new(TRUE, 4);
1713 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1715 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1716 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1717 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.");
1718 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1721 * Connect edit signal, so we can show/not-show the auto start button
1723 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1724 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1726 gtk_widget_show_all(dialog);
1728 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1729 gtk_widget_destroy(dialog);
1733 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1734 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1736 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1737 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1738 *type = Fio_client_ipv4;
1739 else if (!strncmp(typeentry, "IPv6", 4))
1740 *type = Fio_client_ipv6;
1742 *type = Fio_client_socket;
1745 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1747 gtk_widget_destroy(dialog);
1751 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1753 struct gfio_client *gc;
1755 gc = malloc(sizeof(*gc));
1756 memset(gc, 0, sizeof(*gc));
1758 gc->client = client;
1762 client->client_data = gc;
1765 static GtkWidget *new_client_page(struct gui_entry *ge);
1767 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1769 struct gui_entry *ge;
1771 ge = malloc(sizeof(*ge));
1772 memset(ge, 0, sizeof(*ge));
1773 INIT_FLIST_HEAD(&ge->list);
1774 flist_add_tail(&ge->list, &ui->list);
1780 * FIXME: need more handling here
1782 static void ge_destroy(GtkWidget *w, gpointer data)
1784 struct gui_entry *ge = data;
1786 flist_del(&ge->list);
1790 static struct gui_entry *get_new_ge_with_tab(const char *name)
1792 struct gui_entry *ge;
1794 ge = alloc_new_gui_entry(&main_ui);
1796 ge->vbox = new_client_page(ge);
1797 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1799 ge->page_label = gtk_label_new(name);
1800 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1802 gtk_widget_show_all(main_ui.window);
1806 static void file_new(GtkWidget *w, gpointer data)
1808 get_new_ge_with_tab("Untitled");
1812 * Return the 'ge' corresponding to the tab. If the active tab is the
1813 * main tab, open a new tab.
1815 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1817 struct flist_head *entry;
1818 struct gui_entry *ge;
1821 return get_new_ge_with_tab("Untitled");
1823 flist_for_each(entry, &main_ui.list) {
1824 ge = flist_entry(entry, struct gui_entry, list);
1825 if (ge->page_num == cur_page)
1832 static void file_open(GtkWidget *w, gpointer data)
1834 struct gui *ui = data;
1836 GSList *filenames, *fn_glist;
1837 GtkFileFilter *filter;
1839 int port, type, server_start;
1840 struct gui_entry *ge;
1844 * Creates new tab if current tab is the main window, or the
1845 * current tab already has a client.
1847 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1848 ge = get_ge_from_page(cur_page);
1850 ge = get_new_ge_with_tab("Untitled");
1852 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1854 dialog = gtk_file_chooser_dialog_new("Open File",
1855 GTK_WINDOW(ui->window),
1856 GTK_FILE_CHOOSER_ACTION_OPEN,
1857 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1858 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1860 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1862 filter = gtk_file_filter_new();
1863 gtk_file_filter_add_pattern(filter, "*.fio");
1864 gtk_file_filter_add_pattern(filter, "*.job");
1865 gtk_file_filter_add_pattern(filter, "*.ini");
1866 gtk_file_filter_add_mime_type(filter, "text/fio");
1867 gtk_file_filter_set_name(filter, "Fio job file");
1868 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1870 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1871 gtk_widget_destroy(dialog);
1875 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1877 gtk_widget_destroy(dialog);
1879 if (get_connection_details(&host, &port, &type, &server_start))
1882 filenames = fn_glist;
1883 while (filenames != NULL) {
1884 struct fio_client *client;
1886 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1887 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1890 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1894 error = g_error_new(g_quark_from_string("fio"), 1,
1895 "Failed to add client %s", host);
1896 report_error(error);
1897 g_error_free(error);
1899 gfio_client_added(ge, client);
1901 g_free(filenames->data);
1902 filenames = g_slist_next(filenames);
1907 gfio_start_server();
1909 g_slist_free(fn_glist);
1912 static void file_save(GtkWidget *w, gpointer data)
1914 struct gui *ui = data;
1917 dialog = gtk_file_chooser_dialog_new("Save File",
1918 GTK_WINDOW(ui->window),
1919 GTK_FILE_CHOOSER_ACTION_SAVE,
1920 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1921 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1924 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1925 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1927 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1930 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1931 // save_job_file(filename);
1934 gtk_widget_destroy(dialog);
1937 static void view_log_destroy(GtkWidget *w, gpointer data)
1939 struct gui *ui = (struct gui *) data;
1941 gtk_widget_ref(ui->log_tree);
1942 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1943 gtk_widget_destroy(w);
1944 ui->log_view = NULL;
1947 static void view_log(GtkWidget *w, gpointer data)
1949 GtkWidget *win, *scroll, *vbox, *box;
1950 struct gui *ui = (struct gui *) data;
1955 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1956 gtk_window_set_title(GTK_WINDOW(win), "Log");
1957 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1959 scroll = gtk_scrolled_window_new(NULL, NULL);
1961 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1963 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1965 box = gtk_hbox_new(TRUE, 0);
1966 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1967 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1968 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1970 vbox = gtk_vbox_new(TRUE, 5);
1971 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1973 gtk_container_add(GTK_CONTAINER(win), vbox);
1974 gtk_widget_show_all(win);
1977 static void preferences(GtkWidget *w, gpointer data)
1979 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1982 dialog = gtk_dialog_new_with_buttons("Preferences",
1983 GTK_WINDOW(main_ui.window),
1984 GTK_DIALOG_DESTROY_WITH_PARENT,
1985 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1986 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1989 frame = gtk_frame_new("Debug logging");
1990 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1992 vbox = gtk_vbox_new(FALSE, 6);
1993 gtk_container_add(GTK_CONTAINER(frame), vbox);
1995 box = gtk_hbox_new(FALSE, 6);
1996 gtk_container_add(GTK_CONTAINER(vbox), box);
1998 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2000 for (i = 0; i < FD_DEBUG_MAX; i++) {
2002 box = gtk_hbox_new(FALSE, 6);
2003 gtk_container_add(GTK_CONTAINER(vbox), box);
2007 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2008 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2009 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2012 frame = gtk_frame_new("Graph font");
2013 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2014 vbox = gtk_vbox_new(FALSE, 6);
2015 gtk_container_add(GTK_CONTAINER(frame), vbox);
2017 font = gtk_font_button_new();
2018 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2020 gtk_widget_show_all(dialog);
2022 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2023 gtk_widget_destroy(dialog);
2027 for (i = 0; i < FD_DEBUG_MAX; i++) {
2030 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2032 fio_debug |= (1UL << i);
2035 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2036 gtk_widget_destroy(dialog);
2039 static void about_dialog(GtkWidget *w, gpointer data)
2041 const char *authors[] = {
2042 "Jens Axboe <axboe@kernel.dk>",
2043 "Stephen Carmeron <stephenmcameron@gmail.com>",
2046 const char *license[] = {
2047 "Fio is free software; you can redistribute it and/or modify "
2048 "it under the terms of the GNU General Public License as published by "
2049 "the Free Software Foundation; either version 2 of the License, or "
2050 "(at your option) any later version.\n",
2051 "Fio is distributed in the hope that it will be useful, "
2052 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2053 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2054 "GNU General Public License for more details.\n",
2055 "You should have received a copy of the GNU General Public License "
2056 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2057 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2059 char *license_trans;
2061 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2062 license[2], "\n", NULL);
2064 gtk_show_about_dialog(NULL,
2065 "program-name", "gfio",
2066 "comments", "Gtk2 UI for fio",
2067 "license", license_trans,
2068 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2070 "version", fio_version_string,
2071 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2072 "logo-icon-name", "fio",
2074 "wrap-license", TRUE,
2077 g_free(license_trans);
2080 static GtkActionEntry menu_items[] = {
2081 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2082 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2083 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2084 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2085 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2086 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2087 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2088 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2089 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2090 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2092 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2094 static const gchar *ui_string = " \
2096 <menubar name=\"MainMenu\"> \
2097 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2098 <menuitem name=\"New\" action=\"NewFile\" /> \
2099 <separator name=\"Separator1\"/> \
2100 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2101 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2102 <separator name=\"Separator2\"/> \
2103 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2104 <separator name=\"Separator3\"/> \
2105 <menuitem name=\"Quit\" action=\"Quit\" /> \
2107 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2108 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2110 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2111 <menuitem name=\"About\" action=\"About\" /> \
2117 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2120 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2123 action_group = gtk_action_group_new("Menu");
2124 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2126 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2127 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2129 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2130 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2133 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2134 GtkWidget *vbox, GtkUIManager *ui_manager)
2136 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2139 static GtkWidget *new_client_page(struct gui_entry *ge)
2141 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2144 main_vbox = gtk_vbox_new(FALSE, 3);
2146 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2147 ge->topvbox = gtk_vbox_new(FALSE, 3);
2148 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2149 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2151 probe = gtk_frame_new("Job");
2152 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2153 probe_frame = gtk_vbox_new(FALSE, 3);
2154 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2156 probe_box = gtk_hbox_new(FALSE, 3);
2157 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2158 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2159 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2160 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2161 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2163 probe_box = gtk_hbox_new(FALSE, 3);
2164 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2166 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2167 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2168 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2169 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2170 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2171 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2173 probe_box = gtk_hbox_new(FALSE, 3);
2174 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2175 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2176 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2177 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2178 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2181 * Only add this if we have a commit rate
2184 probe_box = gtk_hbox_new(FALSE, 3);
2185 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2187 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2188 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2190 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2191 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2195 * Set up a drawing area and IOPS and bandwidth graphs
2197 gdk_color_parse("white", &white);
2198 ge->graphs.drawing_area = gtk_drawing_area_new();
2199 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2200 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2201 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2202 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2203 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2204 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2205 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2206 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2207 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2208 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2209 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2210 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2211 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2212 ge->graphs.drawing_area);
2213 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2216 setup_graphs(&ge->graphs);
2219 * Set up alignments for widgets at the bottom of ui,
2220 * align bottom left, expand horizontally but not vertically
2222 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2223 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2224 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2225 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2228 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2231 * Set up thread status progress bar
2233 ge->thread_status_pb = gtk_progress_bar_new();
2234 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2235 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2236 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2242 static GtkWidget *new_main_page(struct gui *ui)
2244 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2247 main_vbox = gtk_vbox_new(FALSE, 3);
2250 * Set up alignments for widgets at the top of ui,
2251 * align top left, expand horizontally but not vertically
2253 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2254 ui->topvbox = gtk_vbox_new(FALSE, 0);
2255 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2256 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2258 probe = gtk_frame_new("Run statistics");
2259 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2260 probe_frame = gtk_vbox_new(FALSE, 3);
2261 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2263 probe_box = gtk_hbox_new(FALSE, 3);
2264 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2265 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2266 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2267 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2268 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2271 * Only add this if we have a commit rate
2274 probe_box = gtk_hbox_new(FALSE, 3);
2275 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2277 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2278 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2280 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2281 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2285 * Set up a drawing area and IOPS and bandwidth graphs
2287 gdk_color_parse("white", &white);
2288 ui->graphs.drawing_area = gtk_drawing_area_new();
2289 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2290 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2291 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2292 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2293 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2294 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2295 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2296 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2297 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2298 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2299 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2300 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2301 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2302 ui->graphs.drawing_area);
2303 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2306 setup_graphs(&ui->graphs);
2309 * Set up alignments for widgets at the bottom of ui,
2310 * align bottom left, expand horizontally but not vertically
2312 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2313 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2314 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2315 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2319 * Set up thread status progress bar
2321 ui->thread_status_pb = gtk_progress_bar_new();
2322 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2323 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2324 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2329 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2330 guint page, gpointer data)
2336 static void init_ui(int *argc, char **argv[], struct gui *ui)
2338 GtkSettings *settings;
2339 GtkUIManager *uimanager;
2340 GtkWidget *menu, *vbox;
2342 /* Magical g*thread incantation, you just need this thread stuff.
2343 * Without it, the update that happens in gfio_update_thread_status
2344 * doesn't really happen in a timely fashion, you need expose events
2346 if (!g_thread_supported())
2347 g_thread_init(NULL);
2350 gtk_init(argc, argv);
2351 settings = gtk_settings_get_default();
2352 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2355 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2356 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2357 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2359 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2360 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2362 ui->vbox = gtk_vbox_new(FALSE, 0);
2363 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2365 uimanager = gtk_ui_manager_new();
2366 menu = get_menubar_menu(ui->window, uimanager, ui);
2367 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2369 ui->notebook = gtk_notebook_new();
2370 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2371 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2373 vbox = new_main_page(ui);
2375 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2377 gfio_ui_setup_log(ui);
2379 gtk_widget_show_all(ui->window);
2382 int main(int argc, char *argv[], char *envp[])
2384 if (initialize_fio(envp))
2386 if (fio_init_options())
2389 memset(&main_ui, 0, sizeof(main_ui));
2390 INIT_FLIST_HEAD(&main_ui.list);
2392 init_ui(&argc, &argv, &main_ui);
2394 gdk_threads_enter();
2396 gdk_threads_leave();