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;
122 struct flist_head list;
129 struct flist_head list;
135 GtkWidget *bottomalign;
136 GtkWidget *thread_status_pb;
137 GtkWidget *buttonbox;
138 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
139 GtkWidget *scrolled_window;
141 GtkWidget *error_info_bar;
142 GtkWidget *error_label;
143 GtkWidget *results_notebook;
144 GtkWidget *results_window;
145 GtkListStore *log_model;
148 struct gfio_graphs graphs;
149 struct probe_widget probe;
150 struct eta_widget eta;
151 GtkWidget *page_label;
156 struct gfio_client *client;
162 struct gui_entry *ge;
163 struct fio_client *client;
164 GtkWidget *results_widget;
165 GtkWidget *disk_util_frame;
166 GtkWidget *err_entry;
167 unsigned int job_added;
168 struct thread_options o;
171 static struct graph *setup_iops_graph(void)
175 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
176 graph_title(g, "IOPS");
177 graph_x_title(g, "Time (secs)");
178 graph_y_title(g, "IOs / sec");
179 graph_add_label(g, "Read IOPS");
180 graph_add_label(g, "Write IOPS");
181 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
182 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
183 line_graph_set_data_count_limit(g, 100);
187 static struct graph *setup_bandwidth_graph(void)
191 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
192 graph_title(g, "Bandwidth");
193 graph_x_title(g, "Time (secs)");
194 graph_y_title(g, "Kbytes / sec");
195 graph_add_label(g, "Read Bandwidth");
196 graph_add_label(g, "Write Bandwidth");
197 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
198 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
199 line_graph_set_data_count_limit(g, 100);
203 static void setup_graphs(struct gfio_graphs *g)
205 g->iops_graph = setup_iops_graph();
206 g->bandwidth_graph = setup_bandwidth_graph();
209 static void clear_ge_ui_info(struct gui_entry *ge)
211 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
212 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
213 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
214 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
215 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
216 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
217 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
218 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
219 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
220 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
221 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
227 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
229 GtkWidget *entry, *frame;
231 frame = gtk_frame_new(label);
232 entry = gtk_entry_new();
233 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
234 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
235 gtk_container_add(GTK_CONTAINER(frame), entry);
240 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
242 GtkWidget *label_widget;
245 frame = gtk_frame_new(label);
246 label_widget = gtk_label_new(NULL);
247 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
248 gtk_container_add(GTK_CONTAINER(frame), label_widget);
253 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
255 GtkWidget *button, *box;
257 box = gtk_hbox_new(FALSE, 3);
258 gtk_container_add(GTK_CONTAINER(hbox), box);
260 button = gtk_spin_button_new_with_range(min, max, 1.0);
261 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
263 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
264 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
269 static void gfio_set_connected(struct gui_entry *ge, int connected)
272 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
274 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
275 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
278 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
279 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
280 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
281 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
285 static void label_set_int_value(GtkWidget *entry, unsigned int val)
289 sprintf(tmp, "%u", val);
290 gtk_label_set_text(GTK_LABEL(entry), tmp);
293 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
297 sprintf(tmp, "%u", val);
298 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
302 #define ALIGN_RIGHT 2
306 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
308 GtkCellRenderer *renderer;
309 GtkTreeViewColumn *col;
310 double xalign = 0.0; /* left as default */
311 PangoAlignment align;
314 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
315 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
317 visible = !(flags & INVISIBLE);
319 renderer = gtk_cell_renderer_text_new();
320 col = gtk_tree_view_column_new();
322 gtk_tree_view_column_set_title(col, title);
323 if (!(flags & UNSORTABLE))
324 gtk_tree_view_column_set_sort_column_id(col, index);
325 gtk_tree_view_column_set_resizable(col, TRUE);
326 gtk_tree_view_column_pack_start(col, renderer, TRUE);
327 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
328 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
330 case PANGO_ALIGN_LEFT:
333 case PANGO_ALIGN_CENTER:
336 case PANGO_ALIGN_RIGHT:
340 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
341 gtk_tree_view_column_set_visible(col, visible);
342 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
346 static void gfio_ui_setup_log(struct gui *ui)
348 GtkTreeSelection *selection;
350 GtkWidget *tree_view;
352 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
354 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
355 gtk_widget_set_can_focus(tree_view, FALSE);
357 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
358 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
359 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
360 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
362 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
363 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
364 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
365 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
367 ui->log_model = model;
368 ui->log_tree = tree_view;
371 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
377 GType types[FIO_IO_U_LIST_MAX_LEN];
378 GtkWidget *tree_view;
379 GtkTreeSelection *selection;
384 for (i = 0; i < len; i++)
385 types[i] = G_TYPE_INT;
387 model = gtk_list_store_newv(len, types);
389 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
390 gtk_widget_set_can_focus(tree_view, FALSE);
392 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
393 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
395 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
396 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
398 for (i = 0; i < len; i++) {
401 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
402 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
405 gtk_list_store_append(model, &iter);
407 for (i = 0; i < len; i++) {
409 ovals[i] = (ovals[i] + 999) / 1000;
410 gtk_list_store_set(model, &iter, i, ovals[i], -1);
416 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
419 unsigned int *io_u_plat = ts->io_u_plat[ddir];
420 unsigned long nr = ts->clat_stat[ddir].samples;
421 fio_fp64_t *plist = ts->percentile_list;
422 unsigned int *ovals, len, minv, maxv, scale_down;
424 GtkWidget *tree_view, *frame, *hbox;
427 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
432 * We default to usecs, but if the value range is such that we
433 * should scale down to msecs, do that.
435 if (minv > 2000 && maxv > 99999) {
443 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
445 sprintf(tmp, "Completion percentiles (%s)", base);
446 frame = gtk_frame_new(tmp);
447 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
449 hbox = gtk_hbox_new(FALSE, 3);
450 gtk_container_add(GTK_CONTAINER(frame), hbox);
452 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
458 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
459 unsigned long max, double mean, double dev)
461 const char *base = "(usec)";
462 GtkWidget *hbox, *label, *frame;
466 if (!usec_to_msec(&min, &max, &mean, &dev))
469 minp = num2str(min, 6, 1, 0);
470 maxp = num2str(max, 6, 1, 0);
472 sprintf(tmp, "%s %s", name, base);
473 frame = gtk_frame_new(tmp);
474 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
476 hbox = gtk_hbox_new(FALSE, 3);
477 gtk_container_add(GTK_CONTAINER(frame), hbox);
479 label = new_info_label_in_frame(hbox, "Minimum");
480 gtk_label_set_text(GTK_LABEL(label), minp);
481 label = new_info_label_in_frame(hbox, "Maximum");
482 gtk_label_set_text(GTK_LABEL(label), maxp);
483 label = new_info_label_in_frame(hbox, "Average");
484 sprintf(tmp, "%5.02f", mean);
485 gtk_label_set_text(GTK_LABEL(label), tmp);
486 label = new_info_label_in_frame(hbox, "Standard deviation");
487 sprintf(tmp, "%5.02f", dev);
488 gtk_label_set_text(GTK_LABEL(label), tmp);
499 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
500 struct thread_stat *ts, int ddir)
502 const char *ddir_label[2] = { "Read", "Write" };
503 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
504 unsigned long min[3], max[3], runt;
505 unsigned long long bw, iops;
506 unsigned int flags = 0;
507 double mean[3], dev[3];
508 char *io_p, *bw_p, *iops_p;
511 if (!ts->runtime[ddir])
514 i2p = is_power_of_2(rs->kb_base);
515 runt = ts->runtime[ddir];
517 bw = (1000 * ts->io_bytes[ddir]) / runt;
518 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
519 bw_p = num2str(bw, 6, 1, i2p);
521 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
522 iops_p = num2str(iops, 6, 1, 0);
524 box = gtk_hbox_new(FALSE, 3);
525 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
527 frame = gtk_frame_new(ddir_label[ddir]);
528 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
530 main_vbox = gtk_vbox_new(FALSE, 3);
531 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
533 box = gtk_hbox_new(FALSE, 3);
534 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
536 label = new_info_label_in_frame(box, "IO");
537 gtk_label_set_text(GTK_LABEL(label), io_p);
538 label = new_info_label_in_frame(box, "Bandwidth");
539 gtk_label_set_text(GTK_LABEL(label), bw_p);
540 label = new_info_label_in_frame(box, "IOPS");
541 gtk_label_set_text(GTK_LABEL(label), iops_p);
542 label = new_info_label_in_frame(box, "Runtime (msec)");
543 label_set_int_value(label, ts->runtime[ddir]);
545 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
546 double p_of_agg = 100.0;
547 const char *bw_str = "KB";
551 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
552 if (p_of_agg > 100.0)
556 if (mean[0] > 999999.9) {
564 sprintf(tmp, "Bandwidth (%s)", bw_str);
565 frame = gtk_frame_new(tmp);
566 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
568 box = gtk_hbox_new(FALSE, 3);
569 gtk_container_add(GTK_CONTAINER(frame), box);
571 label = new_info_label_in_frame(box, "Minimum");
572 label_set_int_value(label, min[0]);
573 label = new_info_label_in_frame(box, "Maximum");
574 label_set_int_value(label, max[0]);
575 label = new_info_label_in_frame(box, "Percentage of jobs");
576 sprintf(tmp, "%3.2f%%", p_of_agg);
577 gtk_label_set_text(GTK_LABEL(label), tmp);
578 label = new_info_label_in_frame(box, "Average");
579 sprintf(tmp, "%5.02f", mean[0]);
580 gtk_label_set_text(GTK_LABEL(label), tmp);
581 label = new_info_label_in_frame(box, "Standard deviation");
582 sprintf(tmp, "%5.02f", dev[0]);
583 gtk_label_set_text(GTK_LABEL(label), tmp);
586 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
588 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
590 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
594 frame = gtk_frame_new("Latency");
595 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
597 vbox = gtk_vbox_new(FALSE, 3);
598 gtk_container_add(GTK_CONTAINER(frame), vbox);
600 if (flags & GFIO_SLAT)
601 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
602 if (flags & GFIO_CLAT)
603 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
604 if (flags & GFIO_LAT)
605 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
608 if (ts->clat_percentiles)
609 gfio_show_clat_percentiles(main_vbox, ts, ddir);
617 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
620 GtkWidget *tree_view;
621 GtkTreeSelection *selection;
628 * Check if all are empty, in which case don't bother
630 for (i = 0, skipped = 0; i < num; i++)
637 types = malloc(num * sizeof(GType));
639 for (i = 0; i < num; i++)
640 types[i] = G_TYPE_STRING;
642 model = gtk_list_store_newv(num, types);
646 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
647 gtk_widget_set_can_focus(tree_view, FALSE);
649 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
650 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
652 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
653 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
655 for (i = 0; i < num; i++)
656 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
658 gtk_list_store_append(model, &iter);
660 for (i = 0; i < num; i++) {
664 sprintf(fbuf, "0.00");
666 sprintf(fbuf, "%3.2f%%", lat[i]);
668 gtk_list_store_set(model, &iter, i, fbuf, -1);
674 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
676 GtkWidget *box, *frame, *tree_view;
677 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
678 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
679 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
680 "250", "500", "750", "1000", };
681 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
682 "250", "500", "750", "1000", "2000",
685 stat_calc_lat_u(ts, io_u_lat_u);
686 stat_calc_lat_m(ts, io_u_lat_m);
688 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
690 frame = gtk_frame_new("Latency buckets (usec)");
691 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
693 box = gtk_hbox_new(FALSE, 3);
694 gtk_container_add(GTK_CONTAINER(frame), box);
695 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
698 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
700 frame = gtk_frame_new("Latency buckets (msec)");
701 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
703 box = gtk_hbox_new(FALSE, 3);
704 gtk_container_add(GTK_CONTAINER(frame), box);
705 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
709 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
711 GtkWidget *box, *frame, *entry;
712 double usr_cpu, sys_cpu;
713 unsigned long runtime;
716 runtime = ts->total_run_time;
718 double runt = (double) runtime;
720 usr_cpu = (double) ts->usr_time * 100 / runt;
721 sys_cpu = (double) ts->sys_time * 100 / runt;
727 frame = gtk_frame_new("OS resources");
728 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
730 box = gtk_hbox_new(FALSE, 3);
731 gtk_container_add(GTK_CONTAINER(frame), box);
733 entry = new_info_entry_in_frame(box, "User CPU");
734 sprintf(tmp, "%3.2f%%", usr_cpu);
735 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
736 entry = new_info_entry_in_frame(box, "System CPU");
737 sprintf(tmp, "%3.2f%%", sys_cpu);
738 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
739 entry = new_info_entry_in_frame(box, "Context switches");
740 entry_set_int_value(entry, ts->ctx);
741 entry = new_info_entry_in_frame(box, "Major faults");
742 entry_set_int_value(entry, ts->majf);
743 entry = new_info_entry_in_frame(box, "Minor faults");
744 entry_set_int_value(entry, ts->minf);
746 static void gfio_add_sc_depths_tree(GtkListStore *model,
747 struct thread_stat *ts, unsigned int len,
750 double io_u_dist[FIO_IO_U_MAP_NR];
752 /* Bits 0, and 3-8 */
753 const int add_mask = 0x1f9;
757 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
759 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
761 gtk_list_store_append(model, &iter);
763 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
765 for (i = 1, j = 0; i < len; i++) {
768 if (!(add_mask & (1UL << (i - 1))))
769 sprintf(fbuf, "0.0%%");
771 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
775 gtk_list_store_set(model, &iter, i, fbuf, -1);
780 static void gfio_add_total_depths_tree(GtkListStore *model,
781 struct thread_stat *ts, unsigned int len)
783 double io_u_dist[FIO_IO_U_MAP_NR];
785 /* Bits 1-6, and 8 */
786 const int add_mask = 0x17e;
789 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
791 gtk_list_store_append(model, &iter);
793 gtk_list_store_set(model, &iter, 0, "Total", -1);
795 for (i = 1, j = 0; i < len; i++) {
798 if (!(add_mask & (1UL << (i - 1))))
799 sprintf(fbuf, "0.0%%");
801 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
805 gtk_list_store_set(model, &iter, i, fbuf, -1);
810 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
812 GtkWidget *frame, *box, *tree_view;
813 GtkTreeSelection *selection;
815 GType types[FIO_IO_U_MAP_NR + 1];
818 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
820 frame = gtk_frame_new("IO depths");
821 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
823 box = gtk_hbox_new(FALSE, 3);
824 gtk_container_add(GTK_CONTAINER(frame), box);
826 for (i = 0; i < NR_LABELS; i++)
827 types[i] = G_TYPE_STRING;
829 model = gtk_list_store_newv(NR_LABELS, types);
831 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
832 gtk_widget_set_can_focus(tree_view, FALSE);
834 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
835 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
837 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
838 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
840 for (i = 0; i < NR_LABELS; i++)
841 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
843 gfio_add_total_depths_tree(model, ts, NR_LABELS);
844 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
845 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
847 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
850 static gboolean results_window_delete(GtkWidget *w, gpointer data)
852 struct gui_entry *ge = (struct gui_entry *) data;
854 gtk_widget_destroy(w);
855 ge->results_window = NULL;
856 ge->results_notebook = NULL;
860 static GtkWidget *get_results_window(struct gui_entry *ge)
862 GtkWidget *win, *notebook;
864 if (ge->results_window)
865 return ge->results_notebook;
867 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
868 gtk_window_set_title(GTK_WINDOW(win), "Results");
869 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
870 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
871 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
873 notebook = gtk_notebook_new();
874 gtk_container_add(GTK_CONTAINER(win), notebook);
876 ge->results_window = win;
877 ge->results_notebook = notebook;
878 return ge->results_notebook;
881 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
882 struct group_run_stats *rs)
884 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
885 struct gfio_client *gc = client->client_data;
889 res_win = get_results_window(gc->ge);
891 scroll = gtk_scrolled_window_new(NULL, NULL);
892 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
893 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
895 vbox = gtk_vbox_new(FALSE, 3);
897 box = gtk_hbox_new(FALSE, 0);
898 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
900 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
902 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
904 gc->results_widget = vbox;
906 entry = new_info_entry_in_frame(box, "Name");
907 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
908 if (strlen(ts->description)) {
909 entry = new_info_entry_in_frame(box, "Description");
910 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
912 entry = new_info_entry_in_frame(box, "Group ID");
913 entry_set_int_value(entry, ts->groupid);
914 entry = new_info_entry_in_frame(box, "Jobs");
915 entry_set_int_value(entry, ts->members);
916 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
917 entry_set_int_value(entry, ts->error);
918 entry = new_info_entry_in_frame(box, "PID");
919 entry_set_int_value(entry, ts->pid);
921 if (ts->io_bytes[DDIR_READ])
922 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
923 if (ts->io_bytes[DDIR_WRITE])
924 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
926 gfio_show_latency_buckets(vbox, ts);
927 gfio_show_cpu_usage(vbox, ts);
928 gfio_show_io_depths(vbox, ts);
930 gtk_widget_show_all(gc->ge->results_window);
934 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
936 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
937 struct gui *ui = &main_ui;
941 char tmp[64], timebuf[80];
944 tm = localtime(&sec);
945 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
946 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
950 gtk_list_store_append(ui->log_model, &iter);
951 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
952 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
953 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
954 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
956 if (p->level == FIO_LOG_ERR)
957 view_log(NULL, (gpointer) ui);
962 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
964 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
965 struct gfio_client *gc = client->client_data;
966 GtkWidget *box, *frame, *entry, *vbox;
972 if (!gc->results_widget)
975 if (!gc->disk_util_frame) {
976 gc->disk_util_frame = gtk_frame_new("Disk utilization");
977 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
980 vbox = gtk_vbox_new(FALSE, 3);
981 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
983 frame = gtk_frame_new((char *) p->dus.name);
984 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
986 box = gtk_vbox_new(FALSE, 3);
987 gtk_container_add(GTK_CONTAINER(frame), box);
989 frame = gtk_frame_new("Read");
990 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
991 vbox = gtk_hbox_new(TRUE, 3);
992 gtk_container_add(GTK_CONTAINER(frame), vbox);
993 entry = new_info_entry_in_frame(vbox, "IOs");
994 entry_set_int_value(entry, p->dus.ios[0]);
995 entry = new_info_entry_in_frame(vbox, "Merges");
996 entry_set_int_value(entry, p->dus.merges[0]);
997 entry = new_info_entry_in_frame(vbox, "Sectors");
998 entry_set_int_value(entry, p->dus.sectors[0]);
999 entry = new_info_entry_in_frame(vbox, "Ticks");
1000 entry_set_int_value(entry, p->dus.ticks[0]);
1002 frame = gtk_frame_new("Write");
1003 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1004 vbox = gtk_hbox_new(TRUE, 3);
1005 gtk_container_add(GTK_CONTAINER(frame), vbox);
1006 entry = new_info_entry_in_frame(vbox, "IOs");
1007 entry_set_int_value(entry, p->dus.ios[1]);
1008 entry = new_info_entry_in_frame(vbox, "Merges");
1009 entry_set_int_value(entry, p->dus.merges[1]);
1010 entry = new_info_entry_in_frame(vbox, "Sectors");
1011 entry_set_int_value(entry, p->dus.sectors[1]);
1012 entry = new_info_entry_in_frame(vbox, "Ticks");
1013 entry_set_int_value(entry, p->dus.ticks[1]);
1015 frame = gtk_frame_new("Shared");
1016 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1017 vbox = gtk_hbox_new(TRUE, 3);
1018 gtk_container_add(GTK_CONTAINER(frame), vbox);
1019 entry = new_info_entry_in_frame(vbox, "IO ticks");
1020 entry_set_int_value(entry, p->dus.io_ticks);
1021 entry = new_info_entry_in_frame(vbox, "Time in queue");
1022 entry_set_int_value(entry, p->dus.time_in_queue);
1026 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1030 sprintf(tmp, "%3.2f%%", util);
1031 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1032 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1034 gtk_widget_show_all(gc->results_widget);
1036 gdk_threads_leave();
1039 extern int sum_stat_clients;
1040 extern struct thread_stat client_ts;
1041 extern struct group_run_stats client_gs;
1043 static int sum_stat_nr;
1045 static void gfio_thread_status_op(struct fio_client *client,
1046 struct fio_net_cmd *cmd)
1048 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1050 gfio_display_ts(client, &p->ts, &p->rs);
1052 if (sum_stat_clients == 1)
1055 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1056 sum_group_stats(&client_gs, &p->rs);
1058 client_ts.members++;
1059 client_ts.groupid = p->ts.groupid;
1061 if (++sum_stat_nr == sum_stat_clients) {
1062 strcpy(client_ts.name, "All clients");
1063 gfio_display_ts(client, &client_ts, &client_gs);
1067 static void gfio_group_stats_op(struct fio_client *client,
1068 struct fio_net_cmd *cmd)
1070 /* We're ignoring group stats for now */
1073 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1076 struct gfio_graphs *g = data;
1078 g->drawing_area_xdim = w->allocation.width;
1079 g->drawing_area_ydim = w->allocation.height;
1083 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1085 struct gfio_graphs *g = p;
1088 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1089 g->drawing_area_ydim);
1090 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1091 g->drawing_area_ydim);
1092 cr = gdk_cairo_create(w->window);
1094 cairo_set_source_rgb(cr, 0, 0, 0);
1097 cairo_translate(cr, 0, 0);
1098 line_graph_draw(g->bandwidth_graph, cr);
1103 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1104 line_graph_draw(g->iops_graph, cr);
1113 * Client specific ETA
1115 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1117 struct gfio_client *gc = client->client_data;
1118 struct gui_entry *ge = gc->ge;
1119 static int eta_good;
1126 gdk_threads_enter();
1131 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1132 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1133 eta_to_str(eta_str, je->eta_sec);
1136 sprintf(tmp, "%u", je->nr_running);
1137 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1138 sprintf(tmp, "%u", je->files_open);
1139 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1142 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1143 if (je->m_rate || je->t_rate) {
1146 mr = num2str(je->m_rate, 4, 0, i2p);
1147 tr = num2str(je->t_rate, 4, 0, i2p);
1148 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1149 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1152 } else if (je->m_iops || je->t_iops)
1153 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1155 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1156 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1157 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1158 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1161 if (je->eta_sec != INT_MAX && je->nr_running) {
1165 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1166 strcpy(output, "-.-% done");
1170 sprintf(output, "%3.1f%% done", perc);
1173 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1174 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1176 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1177 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1179 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1180 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1181 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1182 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1184 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1185 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1186 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1187 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1196 char *dst = output + strlen(output);
1198 sprintf(dst, " - %s", eta_str);
1201 gfio_update_thread_status(output, perc);
1202 gdk_threads_leave();
1206 * Update ETA in main window for all clients
1208 static void gfio_update_all_eta(struct jobs_eta *je)
1210 struct gui *ui = &main_ui;
1211 static int eta_good;
1217 gdk_threads_enter();
1222 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1223 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1224 eta_to_str(eta_str, je->eta_sec);
1228 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1229 if (je->m_rate || je->t_rate) {
1232 mr = num2str(je->m_rate, 4, 0, i2p);
1233 tr = num2str(je->t_rate, 4, 0, i2p);
1234 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1235 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1238 } else if (je->m_iops || je->t_iops)
1239 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1241 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1242 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1243 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1244 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1247 if (je->eta_sec != INT_MAX && je->nr_running) {
1251 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1252 strcpy(output, "-.-% done");
1256 sprintf(output, "%3.1f%% done", perc);
1259 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1260 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1262 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1263 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1265 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1266 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1267 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1268 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1270 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1271 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1272 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1273 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1282 char *dst = output + strlen(output);
1284 sprintf(dst, " - %s", eta_str);
1287 gfio_update_thread_status(output, perc);
1288 gdk_threads_leave();
1291 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1293 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1294 struct gfio_client *gc = client->client_data;
1295 struct gui_entry *ge = gc->ge;
1296 const char *os, *arch;
1299 os = fio_get_os_string(probe->os);
1303 arch = fio_get_arch_string(probe->arch);
1308 client->name = strdup((char *) probe->hostname);
1310 gdk_threads_enter();
1312 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1313 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1314 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1315 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1316 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1318 gfio_set_connected(ge, 1);
1320 gdk_threads_leave();
1323 static void gfio_update_thread_status(char *status_message, double perc)
1325 struct gui *ui = &main_ui;
1326 static char message[100];
1327 const char *m = message;
1329 strncpy(message, status_message, sizeof(message) - 1);
1330 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1331 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1332 gtk_widget_queue_draw(ui->window);
1335 static void gfio_quit_op(struct fio_client *client)
1337 struct gfio_client *gc = client->client_data;
1339 gdk_threads_enter();
1340 gfio_set_connected(gc->ge, 0);
1341 gdk_threads_leave();
1344 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1346 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1347 struct gfio_client *gc = client->client_data;
1348 struct thread_options *o = &gc->o;
1349 struct gui_entry *ge = gc->ge;
1352 convert_thread_options_to_cpu(o, &p->top);
1354 gdk_threads_enter();
1356 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1358 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1359 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1360 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1362 sprintf(tmp, "%u", o->iodepth);
1363 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1367 gdk_threads_leave();
1370 static void gfio_client_timed_out(struct fio_client *client)
1372 struct gfio_client *gc = client->client_data;
1373 GtkWidget *dialog, *label, *content;
1376 gdk_threads_enter();
1378 gfio_set_connected(gc->ge, 0);
1379 clear_ge_ui_info(gc->ge);
1381 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1383 dialog = gtk_dialog_new_with_buttons("Timed out!",
1384 GTK_WINDOW(main_ui.window),
1385 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1386 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1388 /* gtk_dialog_get_content_area() is 2.14 and newer */
1389 content = GTK_DIALOG(dialog)->vbox;
1391 label = gtk_label_new((const gchar *) buf);
1392 gtk_container_add(GTK_CONTAINER(content), label);
1393 gtk_widget_show_all(dialog);
1394 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1396 gtk_dialog_run(GTK_DIALOG(dialog));
1397 gtk_widget_destroy(dialog);
1399 gdk_threads_leave();
1402 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1404 struct gfio_client *gc = client->client_data;
1406 gdk_threads_enter();
1408 gfio_set_connected(gc->ge, 0);
1411 entry_set_int_value(gc->err_entry, client->error);
1413 gdk_threads_leave();
1416 struct client_ops gfio_client_ops = {
1417 .text_op = gfio_text_op,
1418 .disk_util = gfio_disk_util_op,
1419 .thread_status = gfio_thread_status_op,
1420 .group_stats = gfio_group_stats_op,
1421 .jobs_eta = gfio_update_client_eta,
1422 .eta = gfio_update_all_eta,
1423 .probe = gfio_probe_op,
1424 .quit = gfio_quit_op,
1425 .add_job = gfio_add_job_op,
1426 .timed_out = gfio_client_timed_out,
1427 .stop = gfio_client_stop,
1428 .stay_connected = 1,
1431 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1432 __attribute__((unused)) gpointer data)
1437 static void *job_thread(void *arg)
1439 fio_handle_clients(&gfio_client_ops);
1443 static int send_job_files(struct gui_entry *ge)
1447 for (i = 0; i < ge->nr_job_files; i++) {
1448 ret = fio_clients_send_ini(ge->job_files[i]);
1452 free(ge->job_files[i]);
1453 ge->job_files[i] = NULL;
1455 while (i < ge->nr_job_files) {
1456 free(ge->job_files[i]);
1457 ge->job_files[i] = NULL;
1464 static void *server_thread(void *arg)
1467 gfio_server_running = 1;
1468 fio_start_server(NULL);
1469 gfio_server_running = 0;
1473 static void gfio_start_server(void)
1475 struct gui *ui = &main_ui;
1477 if (!gfio_server_running) {
1478 gfio_server_running = 1;
1479 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1480 pthread_detach(ui->server_t);
1484 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1487 struct gui_entry *ge = data;
1488 struct gfio_client *gc = ge->client;
1490 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1491 fio_start_client(gc->client);
1494 static void file_open(GtkWidget *w, gpointer data);
1496 static void connect_clicked(GtkWidget *widget, gpointer data)
1498 struct gui_entry *ge = data;
1499 struct gfio_client *gc = ge->client;
1501 if (!ge->connected) {
1502 if (!ge->nr_job_files)
1503 file_open(widget, data);
1504 if (!ge->nr_job_files)
1507 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1508 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1509 if (!fio_client_connect(gc->client)) {
1510 pthread_create(&ge->t, NULL, job_thread, NULL);
1511 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1512 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1515 fio_client_terminate(gc->client);
1516 gfio_set_connected(ge, 0);
1517 clear_ge_ui_info(ge);
1521 static void send_clicked(GtkWidget *widget, gpointer data)
1523 struct gui_entry *ge = data;
1525 if (send_job_files(ge)) {
1526 printf("Yeah, I didn't really like those options too much.\n");
1527 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1530 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1531 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1534 static GtkWidget *add_button(GtkWidget *buttonbox,
1535 struct button_spec *buttonspec, gpointer data)
1537 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1539 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1540 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1541 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1542 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1547 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1552 for (i = 0; i < nbuttons; i++)
1553 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1556 static void on_info_bar_response(GtkWidget *widget, gint response,
1559 struct gui *ui = &main_ui;
1561 if (response == GTK_RESPONSE_OK) {
1562 gtk_widget_destroy(widget);
1563 ui->error_info_bar = NULL;
1567 void report_error(GError *error)
1569 struct gui *ui = &main_ui;
1571 if (ui->error_info_bar == NULL) {
1572 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1575 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1576 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1579 ui->error_label = gtk_label_new(error->message);
1580 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1581 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1583 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1584 gtk_widget_show_all(ui->vbox);
1587 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1588 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1592 struct connection_widgets
1599 static void hostname_cb(GtkEntry *entry, gpointer data)
1601 struct connection_widgets *cw = data;
1602 int uses_net = 0, is_localhost = 0;
1607 * Check whether to display the 'auto start backend' box
1608 * or not. Show it if we are a localhost and using network,
1609 * or using a socket.
1611 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1612 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1617 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1618 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1619 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1620 !strcmp(text, "ip6-loopback"))
1624 if (!uses_net || is_localhost) {
1625 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1626 gtk_widget_set_sensitive(cw->button, 1);
1628 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1629 gtk_widget_set_sensitive(cw->button, 0);
1633 static int get_connection_details(char **host, int *port, int *type,
1636 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1637 struct connection_widgets cw;
1640 dialog = gtk_dialog_new_with_buttons("Connection details",
1641 GTK_WINDOW(main_ui.window),
1642 GTK_DIALOG_DESTROY_WITH_PARENT,
1643 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1644 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1646 frame = gtk_frame_new("Hostname / socket name");
1647 /* gtk_dialog_get_content_area() is 2.14 and newer */
1648 vbox = GTK_DIALOG(dialog)->vbox;
1649 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1651 box = gtk_vbox_new(FALSE, 6);
1652 gtk_container_add(GTK_CONTAINER(frame), box);
1654 hbox = gtk_hbox_new(TRUE, 10);
1655 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1656 cw.hentry = gtk_entry_new();
1657 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1658 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1660 frame = gtk_frame_new("Port");
1661 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1662 box = gtk_vbox_new(FALSE, 10);
1663 gtk_container_add(GTK_CONTAINER(frame), box);
1665 hbox = gtk_hbox_new(TRUE, 4);
1666 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1667 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1669 frame = gtk_frame_new("Type");
1670 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1671 box = gtk_vbox_new(FALSE, 10);
1672 gtk_container_add(GTK_CONTAINER(frame), box);
1674 hbox = gtk_hbox_new(TRUE, 4);
1675 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1677 cw.combo = gtk_combo_box_new_text();
1678 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1679 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1680 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1681 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1683 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1685 frame = gtk_frame_new("Options");
1686 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1687 box = gtk_vbox_new(FALSE, 10);
1688 gtk_container_add(GTK_CONTAINER(frame), box);
1690 hbox = gtk_hbox_new(TRUE, 4);
1691 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1693 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1694 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1695 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.");
1696 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1699 * Connect edit signal, so we can show/not-show the auto start button
1701 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1702 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1704 gtk_widget_show_all(dialog);
1706 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1707 gtk_widget_destroy(dialog);
1711 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1712 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1714 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1715 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1716 *type = Fio_client_ipv4;
1717 else if (!strncmp(typeentry, "IPv6", 4))
1718 *type = Fio_client_ipv6;
1720 *type = Fio_client_socket;
1723 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1725 gtk_widget_destroy(dialog);
1729 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1731 struct gfio_client *gc;
1733 gc = malloc(sizeof(*gc));
1734 memset(gc, 0, sizeof(*gc));
1736 gc->client = client;
1740 client->client_data = gc;
1743 static GtkWidget *new_client_page(struct gui_entry *ge);
1745 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1747 struct gui_entry *ge;
1749 ge = malloc(sizeof(*ge));
1750 memset(ge, 0, sizeof(*ge));
1751 INIT_FLIST_HEAD(&ge->list);
1752 flist_add_tail(&ge->list, &ui->list);
1758 * FIXME: need more handling here
1760 static void ge_destroy(GtkWidget *w, gpointer data)
1762 struct gui_entry *ge = data;
1764 flist_del(&ge->list);
1768 static struct gui_entry *get_new_ge_with_tab(const char *name)
1770 struct gui_entry *ge;
1772 ge = alloc_new_gui_entry(&main_ui);
1774 ge->vbox = new_client_page(ge);
1775 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1777 ge->page_label = gtk_label_new(name);
1778 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1780 gtk_widget_show_all(main_ui.window);
1784 static void file_new(GtkWidget *w, gpointer data)
1786 get_new_ge_with_tab("Untitled");
1790 * Return the 'ge' corresponding to the tab. If the active tab is the
1791 * main tab, open a new tab.
1793 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1795 struct flist_head *entry;
1796 struct gui_entry *ge;
1799 return get_new_ge_with_tab("Untitled");
1801 flist_for_each(entry, &main_ui.list) {
1802 ge = flist_entry(entry, struct gui_entry, list);
1803 if (ge->page_num == cur_page)
1810 static void file_open(GtkWidget *w, gpointer data)
1812 struct gui *ui = data;
1814 GSList *filenames, *fn_glist;
1815 GtkFileFilter *filter;
1817 int port, type, server_start;
1818 struct gui_entry *ge;
1822 * Creates new tab if current tab is the main window, or the
1823 * current tab already has a client.
1825 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1826 ge = get_ge_from_page(cur_page);
1828 ge = get_new_ge_with_tab("Untitled");
1830 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1832 dialog = gtk_file_chooser_dialog_new("Open File",
1833 GTK_WINDOW(ui->window),
1834 GTK_FILE_CHOOSER_ACTION_OPEN,
1835 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1836 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1838 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1840 filter = gtk_file_filter_new();
1841 gtk_file_filter_add_pattern(filter, "*.fio");
1842 gtk_file_filter_add_pattern(filter, "*.job");
1843 gtk_file_filter_add_pattern(filter, "*.ini");
1844 gtk_file_filter_add_mime_type(filter, "text/fio");
1845 gtk_file_filter_set_name(filter, "Fio job file");
1846 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1848 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1849 gtk_widget_destroy(dialog);
1853 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1855 gtk_widget_destroy(dialog);
1857 if (get_connection_details(&host, &port, &type, &server_start))
1860 filenames = fn_glist;
1861 while (filenames != NULL) {
1862 struct fio_client *client;
1864 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1865 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1868 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1872 error = g_error_new(g_quark_from_string("fio"), 1,
1873 "Failed to add client %s", host);
1874 report_error(error);
1875 g_error_free(error);
1877 gfio_client_added(ge, client);
1879 g_free(filenames->data);
1880 filenames = g_slist_next(filenames);
1885 gfio_start_server();
1887 g_slist_free(fn_glist);
1890 static void file_save(GtkWidget *w, gpointer data)
1892 struct gui *ui = data;
1895 dialog = gtk_file_chooser_dialog_new("Save File",
1896 GTK_WINDOW(ui->window),
1897 GTK_FILE_CHOOSER_ACTION_SAVE,
1898 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1899 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1902 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1903 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1905 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1908 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1909 // save_job_file(filename);
1912 gtk_widget_destroy(dialog);
1915 static void view_log_destroy(GtkWidget *w, gpointer data)
1917 struct gui *ui = (struct gui *) data;
1919 gtk_widget_ref(ui->log_tree);
1920 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1921 gtk_widget_destroy(w);
1922 ui->log_view = NULL;
1925 static void view_log(GtkWidget *w, gpointer data)
1927 GtkWidget *win, *scroll, *vbox, *box;
1928 struct gui *ui = (struct gui *) data;
1933 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1934 gtk_window_set_title(GTK_WINDOW(win), "Log");
1935 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1937 scroll = gtk_scrolled_window_new(NULL, NULL);
1939 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1941 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1943 box = gtk_hbox_new(TRUE, 0);
1944 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1945 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1946 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1948 vbox = gtk_vbox_new(TRUE, 5);
1949 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1951 gtk_container_add(GTK_CONTAINER(win), vbox);
1952 gtk_widget_show_all(win);
1955 static void preferences(GtkWidget *w, gpointer data)
1957 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1960 dialog = gtk_dialog_new_with_buttons("Preferences",
1961 GTK_WINDOW(main_ui.window),
1962 GTK_DIALOG_DESTROY_WITH_PARENT,
1963 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1964 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1967 frame = gtk_frame_new("Debug logging");
1968 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1970 vbox = gtk_vbox_new(FALSE, 6);
1971 gtk_container_add(GTK_CONTAINER(frame), vbox);
1973 box = gtk_hbox_new(FALSE, 6);
1974 gtk_container_add(GTK_CONTAINER(vbox), box);
1976 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1978 for (i = 0; i < FD_DEBUG_MAX; i++) {
1980 box = gtk_hbox_new(FALSE, 6);
1981 gtk_container_add(GTK_CONTAINER(vbox), box);
1985 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1986 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1987 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1990 frame = gtk_frame_new("Graph font");
1991 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 font = gtk_font_button_new();
1996 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
1998 gtk_widget_show_all(dialog);
2000 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2001 gtk_widget_destroy(dialog);
2005 for (i = 0; i < FD_DEBUG_MAX; i++) {
2008 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2010 fio_debug |= (1UL << i);
2013 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2014 gtk_widget_destroy(dialog);
2017 static void about_dialog(GtkWidget *w, gpointer data)
2019 const char *authors[] = {
2020 "Jens Axboe <axboe@kernel.dk>",
2021 "Stephen Carmeron <stephenmcameron@gmail.com>",
2024 const char *license[] = {
2025 "Fio is free software; you can redistribute it and/or modify "
2026 "it under the terms of the GNU General Public License as published by "
2027 "the Free Software Foundation; either version 2 of the License, or "
2028 "(at your option) any later version.\n",
2029 "Fio is distributed in the hope that it will be useful, "
2030 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2031 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2032 "GNU General Public License for more details.\n",
2033 "You should have received a copy of the GNU General Public License "
2034 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2035 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2037 char *license_trans;
2039 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2040 license[2], "\n", NULL);
2042 gtk_show_about_dialog(NULL,
2043 "program-name", "gfio",
2044 "comments", "Gtk2 UI for fio",
2045 "license", license_trans,
2046 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2048 "version", fio_version_string,
2049 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2050 "logo-icon-name", "fio",
2052 "wrap-license", TRUE,
2055 g_free(license_trans);
2058 static GtkActionEntry menu_items[] = {
2059 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2060 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2061 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2062 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2063 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2064 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2065 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2066 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2067 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2068 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2070 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2072 static const gchar *ui_string = " \
2074 <menubar name=\"MainMenu\"> \
2075 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2076 <menuitem name=\"New\" action=\"NewFile\" /> \
2077 <separator name=\"Separator1\"/> \
2078 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2079 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2080 <separator name=\"Separator2\"/> \
2081 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2082 <separator name=\"Separator3\"/> \
2083 <menuitem name=\"Quit\" action=\"Quit\" /> \
2085 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2086 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2088 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2089 <menuitem name=\"About\" action=\"About\" /> \
2095 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2098 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2101 action_group = gtk_action_group_new("Menu");
2102 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2104 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2105 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2107 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2108 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2111 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2112 GtkWidget *vbox, GtkUIManager *ui_manager)
2114 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2117 static GtkWidget *new_client_page(struct gui_entry *ge)
2119 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2122 main_vbox = gtk_vbox_new(FALSE, 3);
2124 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2125 ge->topvbox = gtk_vbox_new(FALSE, 3);
2126 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2127 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2129 probe = gtk_frame_new("Job");
2130 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2131 probe_frame = gtk_vbox_new(FALSE, 3);
2132 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2134 probe_box = gtk_hbox_new(FALSE, 3);
2135 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2136 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2137 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2138 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2139 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2141 probe_box = gtk_hbox_new(FALSE, 3);
2142 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2144 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2145 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2146 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2147 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2148 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2149 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2151 probe_box = gtk_hbox_new(FALSE, 3);
2152 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2153 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2154 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2155 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2156 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2159 * Only add this if we have a commit rate
2162 probe_box = gtk_hbox_new(FALSE, 3);
2163 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2165 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2166 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2168 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2169 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2173 * Set up a drawing area and IOPS and bandwidth graphs
2175 gdk_color_parse("white", &white);
2176 ge->graphs.drawing_area = gtk_drawing_area_new();
2177 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2178 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2179 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2180 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2181 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2182 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2183 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2184 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2185 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2186 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2187 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2188 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2189 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2190 ge->graphs.drawing_area);
2191 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2194 setup_graphs(&ge->graphs);
2197 * Set up alignments for widgets at the bottom of ui,
2198 * align bottom left, expand horizontally but not vertically
2200 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2201 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2202 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2203 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2206 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2209 * Set up thread status progress bar
2211 ge->thread_status_pb = gtk_progress_bar_new();
2212 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2213 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2214 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2220 static GtkWidget *new_main_page(struct gui *ui)
2222 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2225 main_vbox = gtk_vbox_new(FALSE, 3);
2228 * Set up alignments for widgets at the top of ui,
2229 * align top left, expand horizontally but not vertically
2231 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2232 ui->topvbox = gtk_vbox_new(FALSE, 0);
2233 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2234 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2236 probe = gtk_frame_new("Run statistics");
2237 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2238 probe_frame = gtk_vbox_new(FALSE, 3);
2239 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2241 probe_box = gtk_hbox_new(FALSE, 3);
2242 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2243 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2244 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2245 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2246 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2249 * Only add this if we have a commit rate
2252 probe_box = gtk_hbox_new(FALSE, 3);
2253 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2255 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2256 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2258 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2259 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2263 * Set up a drawing area and IOPS and bandwidth graphs
2265 gdk_color_parse("white", &white);
2266 ui->graphs.drawing_area = gtk_drawing_area_new();
2267 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2268 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2269 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2270 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2271 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2272 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2273 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2274 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2275 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2276 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2277 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2278 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2279 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2280 ui->graphs.drawing_area);
2281 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2284 setup_graphs(&ui->graphs);
2287 * Set up alignments for widgets at the bottom of ui,
2288 * align bottom left, expand horizontally but not vertically
2290 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2291 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2292 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2293 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2297 * Set up thread status progress bar
2299 ui->thread_status_pb = gtk_progress_bar_new();
2300 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2301 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2302 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2307 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2308 guint page, gpointer data)
2314 static void init_ui(int *argc, char **argv[], struct gui *ui)
2316 GtkSettings *settings;
2317 GtkUIManager *uimanager;
2318 GtkWidget *menu, *vbox;
2320 /* Magical g*thread incantation, you just need this thread stuff.
2321 * Without it, the update that happens in gfio_update_thread_status
2322 * doesn't really happen in a timely fashion, you need expose events
2324 if (!g_thread_supported())
2325 g_thread_init(NULL);
2328 gtk_init(argc, argv);
2329 settings = gtk_settings_get_default();
2330 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2333 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2334 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2335 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2337 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2338 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2340 ui->vbox = gtk_vbox_new(FALSE, 0);
2341 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2343 uimanager = gtk_ui_manager_new();
2344 menu = get_menubar_menu(ui->window, uimanager, ui);
2345 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2347 ui->notebook = gtk_notebook_new();
2348 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2349 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2351 vbox = new_main_page(ui);
2353 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2355 gfio_ui_setup_log(ui);
2357 gtk_widget_show_all(ui->window);
2360 int main(int argc, char *argv[], char *envp[])
2362 if (initialize_fio(envp))
2364 if (fio_init_options())
2367 memset(&main_ui, 0, sizeof(main_ui));
2368 INIT_FLIST_HEAD(&main_ui.list);
2370 init_ui(&argc, &argv, &main_ui);
2372 gdk_threads_enter();
2374 gdk_threads_leave();