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);
174 void report_error(GError *error);
176 static struct graph *setup_iops_graph(void)
180 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
181 graph_title(g, "IOPS");
182 graph_x_title(g, "Time (secs)");
183 graph_y_title(g, "IOs / sec");
184 graph_add_label(g, "Read IOPS");
185 graph_add_label(g, "Write IOPS");
186 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
187 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
188 line_graph_set_data_count_limit(g, 100);
192 static struct graph *setup_bandwidth_graph(void)
196 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
197 graph_title(g, "Bandwidth");
198 graph_x_title(g, "Time (secs)");
199 graph_y_title(g, "Kbytes / sec");
200 graph_add_label(g, "Read Bandwidth");
201 graph_add_label(g, "Write Bandwidth");
202 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
203 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
204 line_graph_set_data_count_limit(g, 100);
208 static void setup_graphs(struct gfio_graphs *g)
210 g->iops_graph = setup_iops_graph();
211 g->bandwidth_graph = setup_bandwidth_graph();
214 static void clear_ge_ui_info(struct gui_entry *ge)
216 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
217 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
218 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
219 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
221 /* should we empty it... */
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
225 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
226 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
227 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
228 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
229 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
230 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
231 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
232 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
235 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
237 GtkWidget *entry, *frame;
239 frame = gtk_frame_new(label);
240 entry = gtk_combo_box_new_text();
241 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
242 gtk_container_add(GTK_CONTAINER(frame), entry);
247 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
249 GtkWidget *entry, *frame;
251 frame = gtk_frame_new(label);
252 entry = gtk_entry_new();
253 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
254 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
255 gtk_container_add(GTK_CONTAINER(frame), entry);
260 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
262 GtkWidget *label_widget;
265 frame = gtk_frame_new(label);
266 label_widget = gtk_label_new(NULL);
267 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
268 gtk_container_add(GTK_CONTAINER(frame), label_widget);
273 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
275 GtkWidget *button, *box;
277 box = gtk_hbox_new(FALSE, 3);
278 gtk_container_add(GTK_CONTAINER(hbox), box);
280 button = gtk_spin_button_new_with_range(min, max, 1.0);
281 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
283 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
284 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
289 static void gfio_set_connected(struct gui_entry *ge, int connected)
292 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
294 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
295 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
298 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
299 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
300 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
301 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
305 static void label_set_int_value(GtkWidget *entry, unsigned int val)
309 sprintf(tmp, "%u", val);
310 gtk_label_set_text(GTK_LABEL(entry), tmp);
313 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
317 sprintf(tmp, "%u", val);
318 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
322 #define ALIGN_RIGHT 2
326 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
328 GtkCellRenderer *renderer;
329 GtkTreeViewColumn *col;
330 double xalign = 0.0; /* left as default */
331 PangoAlignment align;
334 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
335 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
337 visible = !(flags & INVISIBLE);
339 renderer = gtk_cell_renderer_text_new();
340 col = gtk_tree_view_column_new();
342 gtk_tree_view_column_set_title(col, title);
343 if (!(flags & UNSORTABLE))
344 gtk_tree_view_column_set_sort_column_id(col, index);
345 gtk_tree_view_column_set_resizable(col, TRUE);
346 gtk_tree_view_column_pack_start(col, renderer, TRUE);
347 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
348 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
350 case PANGO_ALIGN_LEFT:
353 case PANGO_ALIGN_CENTER:
356 case PANGO_ALIGN_RIGHT:
360 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
361 gtk_tree_view_column_set_visible(col, visible);
362 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
366 static void gfio_ui_setup_log(struct gui *ui)
368 GtkTreeSelection *selection;
370 GtkWidget *tree_view;
372 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
374 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
375 gtk_widget_set_can_focus(tree_view, FALSE);
377 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
378 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
379 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
380 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
382 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
383 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
384 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
385 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
387 ui->log_model = model;
388 ui->log_tree = tree_view;
391 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
397 GType types[FIO_IO_U_LIST_MAX_LEN];
398 GtkWidget *tree_view;
399 GtkTreeSelection *selection;
404 for (i = 0; i < len; i++)
405 types[i] = G_TYPE_INT;
407 model = gtk_list_store_newv(len, types);
409 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
410 gtk_widget_set_can_focus(tree_view, FALSE);
412 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
413 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
415 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
416 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
418 for (i = 0; i < len; i++) {
421 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
422 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
425 gtk_list_store_append(model, &iter);
427 for (i = 0; i < len; i++) {
429 ovals[i] = (ovals[i] + 999) / 1000;
430 gtk_list_store_set(model, &iter, i, ovals[i], -1);
436 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
439 unsigned int *io_u_plat = ts->io_u_plat[ddir];
440 unsigned long nr = ts->clat_stat[ddir].samples;
441 fio_fp64_t *plist = ts->percentile_list;
442 unsigned int *ovals, len, minv, maxv, scale_down;
444 GtkWidget *tree_view, *frame, *hbox;
447 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
452 * We default to usecs, but if the value range is such that we
453 * should scale down to msecs, do that.
455 if (minv > 2000 && maxv > 99999) {
463 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
465 sprintf(tmp, "Completion percentiles (%s)", base);
466 frame = gtk_frame_new(tmp);
467 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
469 hbox = gtk_hbox_new(FALSE, 3);
470 gtk_container_add(GTK_CONTAINER(frame), hbox);
472 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
478 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
479 unsigned long max, double mean, double dev)
481 const char *base = "(usec)";
482 GtkWidget *hbox, *label, *frame;
486 if (!usec_to_msec(&min, &max, &mean, &dev))
489 minp = num2str(min, 6, 1, 0);
490 maxp = num2str(max, 6, 1, 0);
492 sprintf(tmp, "%s %s", name, base);
493 frame = gtk_frame_new(tmp);
494 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
496 hbox = gtk_hbox_new(FALSE, 3);
497 gtk_container_add(GTK_CONTAINER(frame), hbox);
499 label = new_info_label_in_frame(hbox, "Minimum");
500 gtk_label_set_text(GTK_LABEL(label), minp);
501 label = new_info_label_in_frame(hbox, "Maximum");
502 gtk_label_set_text(GTK_LABEL(label), maxp);
503 label = new_info_label_in_frame(hbox, "Average");
504 sprintf(tmp, "%5.02f", mean);
505 gtk_label_set_text(GTK_LABEL(label), tmp);
506 label = new_info_label_in_frame(hbox, "Standard deviation");
507 sprintf(tmp, "%5.02f", dev);
508 gtk_label_set_text(GTK_LABEL(label), tmp);
519 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
520 struct thread_stat *ts, int ddir)
522 const char *ddir_label[2] = { "Read", "Write" };
523 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
524 unsigned long min[3], max[3], runt;
525 unsigned long long bw, iops;
526 unsigned int flags = 0;
527 double mean[3], dev[3];
528 char *io_p, *bw_p, *iops_p;
531 if (!ts->runtime[ddir])
534 i2p = is_power_of_2(rs->kb_base);
535 runt = ts->runtime[ddir];
537 bw = (1000 * ts->io_bytes[ddir]) / runt;
538 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
539 bw_p = num2str(bw, 6, 1, i2p);
541 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
542 iops_p = num2str(iops, 6, 1, 0);
544 box = gtk_hbox_new(FALSE, 3);
545 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
547 frame = gtk_frame_new(ddir_label[ddir]);
548 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
550 main_vbox = gtk_vbox_new(FALSE, 3);
551 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
553 box = gtk_hbox_new(FALSE, 3);
554 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
556 label = new_info_label_in_frame(box, "IO");
557 gtk_label_set_text(GTK_LABEL(label), io_p);
558 label = new_info_label_in_frame(box, "Bandwidth");
559 gtk_label_set_text(GTK_LABEL(label), bw_p);
560 label = new_info_label_in_frame(box, "IOPS");
561 gtk_label_set_text(GTK_LABEL(label), iops_p);
562 label = new_info_label_in_frame(box, "Runtime (msec)");
563 label_set_int_value(label, ts->runtime[ddir]);
565 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
566 double p_of_agg = 100.0;
567 const char *bw_str = "KB";
571 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
572 if (p_of_agg > 100.0)
576 if (mean[0] > 999999.9) {
584 sprintf(tmp, "Bandwidth (%s)", bw_str);
585 frame = gtk_frame_new(tmp);
586 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
588 box = gtk_hbox_new(FALSE, 3);
589 gtk_container_add(GTK_CONTAINER(frame), box);
591 label = new_info_label_in_frame(box, "Minimum");
592 label_set_int_value(label, min[0]);
593 label = new_info_label_in_frame(box, "Maximum");
594 label_set_int_value(label, max[0]);
595 label = new_info_label_in_frame(box, "Percentage of jobs");
596 sprintf(tmp, "%3.2f%%", p_of_agg);
597 gtk_label_set_text(GTK_LABEL(label), tmp);
598 label = new_info_label_in_frame(box, "Average");
599 sprintf(tmp, "%5.02f", mean[0]);
600 gtk_label_set_text(GTK_LABEL(label), tmp);
601 label = new_info_label_in_frame(box, "Standard deviation");
602 sprintf(tmp, "%5.02f", dev[0]);
603 gtk_label_set_text(GTK_LABEL(label), tmp);
606 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
608 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
610 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
614 frame = gtk_frame_new("Latency");
615 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
617 vbox = gtk_vbox_new(FALSE, 3);
618 gtk_container_add(GTK_CONTAINER(frame), vbox);
620 if (flags & GFIO_SLAT)
621 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
622 if (flags & GFIO_CLAT)
623 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
624 if (flags & GFIO_LAT)
625 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
628 if (ts->clat_percentiles)
629 gfio_show_clat_percentiles(main_vbox, ts, ddir);
637 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
640 GtkWidget *tree_view;
641 GtkTreeSelection *selection;
648 * Check if all are empty, in which case don't bother
650 for (i = 0, skipped = 0; i < num; i++)
657 types = malloc(num * sizeof(GType));
659 for (i = 0; i < num; i++)
660 types[i] = G_TYPE_STRING;
662 model = gtk_list_store_newv(num, types);
666 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
667 gtk_widget_set_can_focus(tree_view, FALSE);
669 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
670 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
672 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
673 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
675 for (i = 0; i < num; i++)
676 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
678 gtk_list_store_append(model, &iter);
680 for (i = 0; i < num; i++) {
684 sprintf(fbuf, "0.00");
686 sprintf(fbuf, "%3.2f%%", lat[i]);
688 gtk_list_store_set(model, &iter, i, fbuf, -1);
694 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
696 GtkWidget *box, *frame, *tree_view;
697 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
698 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
699 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
700 "250", "500", "750", "1000", };
701 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
702 "250", "500", "750", "1000", "2000",
705 stat_calc_lat_u(ts, io_u_lat_u);
706 stat_calc_lat_m(ts, io_u_lat_m);
708 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
710 frame = gtk_frame_new("Latency buckets (usec)");
711 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
713 box = gtk_hbox_new(FALSE, 3);
714 gtk_container_add(GTK_CONTAINER(frame), box);
715 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
718 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
720 frame = gtk_frame_new("Latency buckets (msec)");
721 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
723 box = gtk_hbox_new(FALSE, 3);
724 gtk_container_add(GTK_CONTAINER(frame), box);
725 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
729 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
731 GtkWidget *box, *frame, *entry;
732 double usr_cpu, sys_cpu;
733 unsigned long runtime;
736 runtime = ts->total_run_time;
738 double runt = (double) runtime;
740 usr_cpu = (double) ts->usr_time * 100 / runt;
741 sys_cpu = (double) ts->sys_time * 100 / runt;
747 frame = gtk_frame_new("OS resources");
748 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
750 box = gtk_hbox_new(FALSE, 3);
751 gtk_container_add(GTK_CONTAINER(frame), box);
753 entry = new_info_entry_in_frame(box, "User CPU");
754 sprintf(tmp, "%3.2f%%", usr_cpu);
755 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
756 entry = new_info_entry_in_frame(box, "System CPU");
757 sprintf(tmp, "%3.2f%%", sys_cpu);
758 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
759 entry = new_info_entry_in_frame(box, "Context switches");
760 entry_set_int_value(entry, ts->ctx);
761 entry = new_info_entry_in_frame(box, "Major faults");
762 entry_set_int_value(entry, ts->majf);
763 entry = new_info_entry_in_frame(box, "Minor faults");
764 entry_set_int_value(entry, ts->minf);
766 static void gfio_add_sc_depths_tree(GtkListStore *model,
767 struct thread_stat *ts, unsigned int len,
770 double io_u_dist[FIO_IO_U_MAP_NR];
772 /* Bits 0, and 3-8 */
773 const int add_mask = 0x1f9;
777 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
779 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
781 gtk_list_store_append(model, &iter);
783 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
785 for (i = 1, j = 0; i < len; i++) {
788 if (!(add_mask & (1UL << (i - 1))))
789 sprintf(fbuf, "0.0%%");
791 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
795 gtk_list_store_set(model, &iter, i, fbuf, -1);
800 static void gfio_add_total_depths_tree(GtkListStore *model,
801 struct thread_stat *ts, unsigned int len)
803 double io_u_dist[FIO_IO_U_MAP_NR];
805 /* Bits 1-6, and 8 */
806 const int add_mask = 0x17e;
809 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
811 gtk_list_store_append(model, &iter);
813 gtk_list_store_set(model, &iter, 0, "Total", -1);
815 for (i = 1, j = 0; i < len; i++) {
818 if (!(add_mask & (1UL << (i - 1))))
819 sprintf(fbuf, "0.0%%");
821 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
825 gtk_list_store_set(model, &iter, i, fbuf, -1);
830 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
832 GtkWidget *frame, *box, *tree_view;
833 GtkTreeSelection *selection;
835 GType types[FIO_IO_U_MAP_NR + 1];
838 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
840 frame = gtk_frame_new("IO depths");
841 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
843 box = gtk_hbox_new(FALSE, 3);
844 gtk_container_add(GTK_CONTAINER(frame), box);
846 for (i = 0; i < NR_LABELS; i++)
847 types[i] = G_TYPE_STRING;
849 model = gtk_list_store_newv(NR_LABELS, types);
851 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
852 gtk_widget_set_can_focus(tree_view, FALSE);
854 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
855 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
857 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
858 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
860 for (i = 0; i < NR_LABELS; i++)
861 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
863 gfio_add_total_depths_tree(model, ts, NR_LABELS);
864 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
865 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
867 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
870 static gboolean results_window_delete(GtkWidget *w, gpointer data)
872 struct gui_entry *ge = (struct gui_entry *) data;
874 gtk_widget_destroy(w);
875 ge->results_window = NULL;
876 ge->results_notebook = NULL;
880 static GtkWidget *get_results_window(struct gui_entry *ge)
882 GtkWidget *win, *notebook;
884 if (ge->results_window)
885 return ge->results_notebook;
887 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
888 gtk_window_set_title(GTK_WINDOW(win), "Results");
889 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
890 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
891 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
893 notebook = gtk_notebook_new();
894 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
895 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
896 gtk_container_add(GTK_CONTAINER(win), notebook);
898 ge->results_window = win;
899 ge->results_notebook = notebook;
900 return ge->results_notebook;
903 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
904 struct group_run_stats *rs)
906 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
907 struct gfio_client *gc = client->client_data;
911 res_win = get_results_window(gc->ge);
913 scroll = gtk_scrolled_window_new(NULL, NULL);
914 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
915 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
917 vbox = gtk_vbox_new(FALSE, 3);
919 box = gtk_hbox_new(FALSE, 0);
920 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
922 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
924 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
926 gc->results_widget = vbox;
928 entry = new_info_entry_in_frame(box, "Name");
929 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
930 if (strlen(ts->description)) {
931 entry = new_info_entry_in_frame(box, "Description");
932 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
934 entry = new_info_entry_in_frame(box, "Group ID");
935 entry_set_int_value(entry, ts->groupid);
936 entry = new_info_entry_in_frame(box, "Jobs");
937 entry_set_int_value(entry, ts->members);
938 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
939 entry_set_int_value(entry, ts->error);
940 entry = new_info_entry_in_frame(box, "PID");
941 entry_set_int_value(entry, ts->pid);
943 if (ts->io_bytes[DDIR_READ])
944 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
945 if (ts->io_bytes[DDIR_WRITE])
946 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
948 gfio_show_latency_buckets(vbox, ts);
949 gfio_show_cpu_usage(vbox, ts);
950 gfio_show_io_depths(vbox, ts);
952 gtk_widget_show_all(gc->ge->results_window);
956 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
958 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
959 struct gui *ui = &main_ui;
963 char tmp[64], timebuf[80];
966 tm = localtime(&sec);
967 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
968 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
972 gtk_list_store_append(ui->log_model, &iter);
973 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
974 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
975 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
976 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
978 if (p->level == FIO_LOG_ERR)
979 view_log(NULL, (gpointer) ui);
984 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
986 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
987 struct gfio_client *gc = client->client_data;
988 GtkWidget *box, *frame, *entry, *vbox;
994 if (!gc->results_widget)
997 if (!gc->disk_util_frame) {
998 gc->disk_util_frame = gtk_frame_new("Disk utilization");
999 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1002 vbox = gtk_vbox_new(FALSE, 3);
1003 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1005 frame = gtk_frame_new((char *) p->dus.name);
1006 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1008 box = gtk_vbox_new(FALSE, 3);
1009 gtk_container_add(GTK_CONTAINER(frame), box);
1011 frame = gtk_frame_new("Read");
1012 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1013 vbox = gtk_hbox_new(TRUE, 3);
1014 gtk_container_add(GTK_CONTAINER(frame), vbox);
1015 entry = new_info_entry_in_frame(vbox, "IOs");
1016 entry_set_int_value(entry, p->dus.ios[0]);
1017 entry = new_info_entry_in_frame(vbox, "Merges");
1018 entry_set_int_value(entry, p->dus.merges[0]);
1019 entry = new_info_entry_in_frame(vbox, "Sectors");
1020 entry_set_int_value(entry, p->dus.sectors[0]);
1021 entry = new_info_entry_in_frame(vbox, "Ticks");
1022 entry_set_int_value(entry, p->dus.ticks[0]);
1024 frame = gtk_frame_new("Write");
1025 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1026 vbox = gtk_hbox_new(TRUE, 3);
1027 gtk_container_add(GTK_CONTAINER(frame), vbox);
1028 entry = new_info_entry_in_frame(vbox, "IOs");
1029 entry_set_int_value(entry, p->dus.ios[1]);
1030 entry = new_info_entry_in_frame(vbox, "Merges");
1031 entry_set_int_value(entry, p->dus.merges[1]);
1032 entry = new_info_entry_in_frame(vbox, "Sectors");
1033 entry_set_int_value(entry, p->dus.sectors[1]);
1034 entry = new_info_entry_in_frame(vbox, "Ticks");
1035 entry_set_int_value(entry, p->dus.ticks[1]);
1037 frame = gtk_frame_new("Shared");
1038 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1039 vbox = gtk_hbox_new(TRUE, 3);
1040 gtk_container_add(GTK_CONTAINER(frame), vbox);
1041 entry = new_info_entry_in_frame(vbox, "IO ticks");
1042 entry_set_int_value(entry, p->dus.io_ticks);
1043 entry = new_info_entry_in_frame(vbox, "Time in queue");
1044 entry_set_int_value(entry, p->dus.time_in_queue);
1048 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1052 sprintf(tmp, "%3.2f%%", util);
1053 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1054 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1056 gtk_widget_show_all(gc->results_widget);
1058 gdk_threads_leave();
1061 extern int sum_stat_clients;
1062 extern struct thread_stat client_ts;
1063 extern struct group_run_stats client_gs;
1065 static int sum_stat_nr;
1067 static void gfio_thread_status_op(struct fio_client *client,
1068 struct fio_net_cmd *cmd)
1070 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1072 gfio_display_ts(client, &p->ts, &p->rs);
1074 if (sum_stat_clients == 1)
1077 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1078 sum_group_stats(&client_gs, &p->rs);
1080 client_ts.members++;
1081 client_ts.groupid = p->ts.groupid;
1083 if (++sum_stat_nr == sum_stat_clients) {
1084 strcpy(client_ts.name, "All clients");
1085 gfio_display_ts(client, &client_ts, &client_gs);
1089 static void gfio_group_stats_op(struct fio_client *client,
1090 struct fio_net_cmd *cmd)
1092 /* We're ignoring group stats for now */
1095 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1098 struct gfio_graphs *g = data;
1100 g->drawing_area_xdim = w->allocation.width;
1101 g->drawing_area_ydim = w->allocation.height;
1105 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1107 struct gfio_graphs *g = p;
1110 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1111 g->drawing_area_ydim);
1112 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1113 g->drawing_area_ydim);
1114 cr = gdk_cairo_create(w->window);
1116 cairo_set_source_rgb(cr, 0, 0, 0);
1119 cairo_translate(cr, 0, 0);
1120 line_graph_draw(g->bandwidth_graph, cr);
1125 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1126 line_graph_draw(g->iops_graph, cr);
1135 * Client specific ETA
1137 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1139 struct gfio_client *gc = client->client_data;
1140 struct gui_entry *ge = gc->ge;
1141 static int eta_good;
1148 gdk_threads_enter();
1153 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1154 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1155 eta_to_str(eta_str, je->eta_sec);
1158 sprintf(tmp, "%u", je->nr_running);
1159 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1160 sprintf(tmp, "%u", je->files_open);
1161 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1164 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1165 if (je->m_rate || je->t_rate) {
1168 mr = num2str(je->m_rate, 4, 0, i2p);
1169 tr = num2str(je->t_rate, 4, 0, i2p);
1170 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1171 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1174 } else if (je->m_iops || je->t_iops)
1175 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1177 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1178 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1179 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1180 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1183 if (je->eta_sec != INT_MAX && je->nr_running) {
1187 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1188 strcpy(output, "-.-% done");
1192 sprintf(output, "%3.1f%% done", perc);
1195 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1196 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1198 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1199 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1201 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1202 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1203 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1204 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1206 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1207 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1208 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1209 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1218 char *dst = output + strlen(output);
1220 sprintf(dst, " - %s", eta_str);
1223 gfio_update_thread_status(ge, output, perc);
1224 gdk_threads_leave();
1228 * Update ETA in main window for all clients
1230 static void gfio_update_all_eta(struct jobs_eta *je)
1232 struct gui *ui = &main_ui;
1233 static int eta_good;
1239 gdk_threads_enter();
1244 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1245 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1246 eta_to_str(eta_str, je->eta_sec);
1250 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1251 if (je->m_rate || je->t_rate) {
1254 mr = num2str(je->m_rate, 4, 0, i2p);
1255 tr = num2str(je->t_rate, 4, 0, i2p);
1256 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1257 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1260 } else if (je->m_iops || je->t_iops)
1261 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1263 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1264 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1265 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1266 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1269 entry_set_int_value(ui->eta.jobs, je->nr_running);
1271 if (je->eta_sec != INT_MAX && je->nr_running) {
1275 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1276 strcpy(output, "-.-% done");
1280 sprintf(output, "%3.1f%% done", perc);
1283 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1284 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1286 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1287 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1289 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1290 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1291 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1292 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1294 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1295 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1296 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1297 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1306 char *dst = output + strlen(output);
1308 sprintf(dst, " - %s", eta_str);
1311 gfio_update_thread_status_all(output, perc);
1312 gdk_threads_leave();
1315 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1317 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1318 struct gfio_client *gc = client->client_data;
1319 struct gui_entry *ge = gc->ge;
1320 const char *os, *arch;
1323 os = fio_get_os_string(probe->os);
1327 arch = fio_get_arch_string(probe->arch);
1332 client->name = strdup((char *) probe->hostname);
1334 gdk_threads_enter();
1336 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1337 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1338 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1339 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1340 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1342 gfio_set_connected(ge, 1);
1344 gdk_threads_leave();
1347 static void gfio_update_thread_status(struct gui_entry *ge,
1348 char *status_message, double perc)
1350 static char message[100];
1351 const char *m = message;
1353 strncpy(message, status_message, sizeof(message) - 1);
1354 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1355 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1356 gtk_widget_queue_draw(main_ui.window);
1359 static void gfio_update_thread_status_all(char *status_message, double perc)
1361 struct gui *ui = &main_ui;
1362 static char message[100];
1363 const char *m = message;
1365 strncpy(message, status_message, sizeof(message) - 1);
1366 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1367 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1368 gtk_widget_queue_draw(ui->window);
1371 static void gfio_quit_op(struct fio_client *client)
1373 struct gfio_client *gc = client->client_data;
1375 gdk_threads_enter();
1376 gfio_set_connected(gc->ge, 0);
1377 gdk_threads_leave();
1380 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1382 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1383 struct gfio_client *gc = client->client_data;
1384 struct thread_options *o = &gc->o;
1385 struct gui_entry *ge = gc->ge;
1388 convert_thread_options_to_cpu(o, &p->top);
1390 gdk_threads_enter();
1392 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1394 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1395 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1397 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1398 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1400 sprintf(tmp, "%u", o->iodepth);
1401 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1405 gdk_threads_leave();
1408 static void gfio_client_timed_out(struct fio_client *client)
1410 struct gfio_client *gc = client->client_data;
1411 GtkWidget *dialog, *label, *content;
1414 gdk_threads_enter();
1416 gfio_set_connected(gc->ge, 0);
1417 clear_ge_ui_info(gc->ge);
1419 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1421 dialog = gtk_dialog_new_with_buttons("Timed out!",
1422 GTK_WINDOW(main_ui.window),
1423 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1424 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1426 /* gtk_dialog_get_content_area() is 2.14 and newer */
1427 content = GTK_DIALOG(dialog)->vbox;
1429 label = gtk_label_new((const gchar *) buf);
1430 gtk_container_add(GTK_CONTAINER(content), label);
1431 gtk_widget_show_all(dialog);
1432 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1434 gtk_dialog_run(GTK_DIALOG(dialog));
1435 gtk_widget_destroy(dialog);
1437 gdk_threads_leave();
1440 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1442 struct gfio_client *gc = client->client_data;
1444 gdk_threads_enter();
1446 gfio_set_connected(gc->ge, 0);
1449 entry_set_int_value(gc->err_entry, client->error);
1451 gdk_threads_leave();
1454 struct client_ops gfio_client_ops = {
1455 .text_op = gfio_text_op,
1456 .disk_util = gfio_disk_util_op,
1457 .thread_status = gfio_thread_status_op,
1458 .group_stats = gfio_group_stats_op,
1459 .jobs_eta = gfio_update_client_eta,
1460 .eta = gfio_update_all_eta,
1461 .probe = gfio_probe_op,
1462 .quit = gfio_quit_op,
1463 .add_job = gfio_add_job_op,
1464 .timed_out = gfio_client_timed_out,
1465 .stop = gfio_client_stop,
1466 .stay_connected = 1,
1469 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1470 __attribute__((unused)) gpointer data)
1475 static void *job_thread(void *arg)
1477 struct gui *ui = arg;
1479 ui->handler_running = 1;
1480 fio_handle_clients(&gfio_client_ops);
1481 ui->handler_running = 0;
1485 static int send_job_files(struct gui_entry *ge)
1487 struct gfio_client *gc = ge->client;
1490 for (i = 0; i < ge->nr_job_files; i++) {
1491 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1495 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1496 report_error(error);
1497 g_error_free(error);
1502 free(ge->job_files[i]);
1503 ge->job_files[i] = NULL;
1505 while (i < ge->nr_job_files) {
1506 free(ge->job_files[i]);
1507 ge->job_files[i] = NULL;
1514 static void *server_thread(void *arg)
1517 gfio_server_running = 1;
1518 fio_start_server(NULL);
1519 gfio_server_running = 0;
1523 static void gfio_start_server(void)
1525 struct gui *ui = &main_ui;
1527 if (!gfio_server_running) {
1528 gfio_server_running = 1;
1529 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1530 pthread_detach(ui->server_t);
1534 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1537 struct gui_entry *ge = data;
1538 struct gfio_client *gc = ge->client;
1540 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1541 fio_start_client(gc->client);
1544 static void file_open(GtkWidget *w, gpointer data);
1546 static void connect_clicked(GtkWidget *widget, gpointer data)
1548 struct gui_entry *ge = data;
1549 struct gfio_client *gc = ge->client;
1551 if (!ge->connected) {
1554 if (!ge->nr_job_files)
1555 file_open(widget, data);
1556 if (!ge->nr_job_files)
1559 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1560 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1561 ret = fio_client_connect(gc->client);
1563 if (!ge->ui->handler_running)
1564 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1565 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1566 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1570 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1571 report_error(error);
1572 g_error_free(error);
1575 fio_client_terminate(gc->client);
1576 gfio_set_connected(ge, 0);
1577 clear_ge_ui_info(ge);
1581 static void send_clicked(GtkWidget *widget, gpointer data)
1583 struct gui_entry *ge = data;
1585 if (send_job_files(ge)) {
1588 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1589 report_error(error);
1590 g_error_free(error);
1592 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1595 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1596 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1599 static GtkWidget *add_button(GtkWidget *buttonbox,
1600 struct button_spec *buttonspec, gpointer data)
1602 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1604 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1605 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1606 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1607 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1612 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1617 for (i = 0; i < nbuttons; i++)
1618 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1621 static void on_info_bar_response(GtkWidget *widget, gint response,
1624 struct gui *ui = &main_ui;
1626 if (response == GTK_RESPONSE_OK) {
1627 gtk_widget_destroy(widget);
1628 ui->error_info_bar = NULL;
1632 void report_error(GError *error)
1634 struct gui *ui = &main_ui;
1636 if (ui->error_info_bar == NULL) {
1637 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1640 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1641 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1644 ui->error_label = gtk_label_new(error->message);
1645 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1646 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1648 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1649 gtk_widget_show_all(ui->vbox);
1652 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1653 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1657 struct connection_widgets
1664 static void hostname_cb(GtkEntry *entry, gpointer data)
1666 struct connection_widgets *cw = data;
1667 int uses_net = 0, is_localhost = 0;
1672 * Check whether to display the 'auto start backend' box
1673 * or not. Show it if we are a localhost and using network,
1674 * or using a socket.
1676 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1677 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1682 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1683 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1684 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1685 !strcmp(text, "ip6-loopback"))
1689 if (!uses_net || is_localhost) {
1690 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1691 gtk_widget_set_sensitive(cw->button, 1);
1693 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1694 gtk_widget_set_sensitive(cw->button, 0);
1698 static int get_connection_details(char **host, int *port, int *type,
1701 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1702 struct connection_widgets cw;
1705 dialog = gtk_dialog_new_with_buttons("Connection details",
1706 GTK_WINDOW(main_ui.window),
1707 GTK_DIALOG_DESTROY_WITH_PARENT,
1708 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1709 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1711 frame = gtk_frame_new("Hostname / socket name");
1712 /* gtk_dialog_get_content_area() is 2.14 and newer */
1713 vbox = GTK_DIALOG(dialog)->vbox;
1714 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1716 box = gtk_vbox_new(FALSE, 6);
1717 gtk_container_add(GTK_CONTAINER(frame), box);
1719 hbox = gtk_hbox_new(TRUE, 10);
1720 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1721 cw.hentry = gtk_entry_new();
1722 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1723 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1725 frame = gtk_frame_new("Port");
1726 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1727 box = gtk_vbox_new(FALSE, 10);
1728 gtk_container_add(GTK_CONTAINER(frame), box);
1730 hbox = gtk_hbox_new(TRUE, 4);
1731 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1732 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1734 frame = gtk_frame_new("Type");
1735 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1736 box = gtk_vbox_new(FALSE, 10);
1737 gtk_container_add(GTK_CONTAINER(frame), box);
1739 hbox = gtk_hbox_new(TRUE, 4);
1740 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1742 cw.combo = gtk_combo_box_new_text();
1743 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1744 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1745 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1746 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1748 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1750 frame = gtk_frame_new("Options");
1751 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1752 box = gtk_vbox_new(FALSE, 10);
1753 gtk_container_add(GTK_CONTAINER(frame), box);
1755 hbox = gtk_hbox_new(TRUE, 4);
1756 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1758 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1759 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1760 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.");
1761 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1764 * Connect edit signal, so we can show/not-show the auto start button
1766 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1767 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1769 gtk_widget_show_all(dialog);
1771 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1772 gtk_widget_destroy(dialog);
1776 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1777 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1779 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1780 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1781 *type = Fio_client_ipv4;
1782 else if (!strncmp(typeentry, "IPv6", 4))
1783 *type = Fio_client_ipv6;
1785 *type = Fio_client_socket;
1788 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1790 gtk_widget_destroy(dialog);
1794 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1796 struct gfio_client *gc;
1798 gc = malloc(sizeof(*gc));
1799 memset(gc, 0, sizeof(*gc));
1801 gc->client = fio_get_client(client);
1805 client->client_data = gc;
1808 static GtkWidget *new_client_page(struct gui_entry *ge);
1810 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1812 struct gui_entry *ge;
1814 ge = malloc(sizeof(*ge));
1815 memset(ge, 0, sizeof(*ge));
1816 INIT_FLIST_HEAD(&ge->list);
1817 flist_add_tail(&ge->list, &ui->list);
1823 * FIXME: need more handling here
1825 static void ge_destroy(GtkWidget *w, gpointer data)
1827 struct gui_entry *ge = data;
1828 struct gfio_client *gc = ge->client;
1831 fio_put_client(gc->client);
1833 flist_del(&ge->list);
1837 static struct gui_entry *get_new_ge_with_tab(const char *name)
1839 struct gui_entry *ge;
1841 ge = alloc_new_gui_entry(&main_ui);
1843 ge->vbox = new_client_page(ge);
1844 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1846 ge->page_label = gtk_label_new(name);
1847 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1849 gtk_widget_show_all(main_ui.window);
1853 static void file_new(GtkWidget *w, gpointer data)
1855 get_new_ge_with_tab("Untitled");
1859 * Return the 'ge' corresponding to the tab. If the active tab is the
1860 * main tab, open a new tab.
1862 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1864 struct flist_head *entry;
1865 struct gui_entry *ge;
1868 return get_new_ge_with_tab("Untitled");
1870 flist_for_each(entry, &main_ui.list) {
1871 ge = flist_entry(entry, struct gui_entry, list);
1872 if (ge->page_num == cur_page)
1879 static void file_open(GtkWidget *w, gpointer data)
1881 struct gui *ui = data;
1883 GSList *filenames, *fn_glist;
1884 GtkFileFilter *filter;
1886 int port, type, server_start;
1887 struct gui_entry *ge;
1891 * Creates new tab if current tab is the main window, or the
1892 * current tab already has a client.
1894 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1895 ge = get_ge_from_page(cur_page);
1897 ge = get_new_ge_with_tab("Untitled");
1899 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1901 dialog = gtk_file_chooser_dialog_new("Open File",
1902 GTK_WINDOW(ui->window),
1903 GTK_FILE_CHOOSER_ACTION_OPEN,
1904 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1905 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1907 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1909 filter = gtk_file_filter_new();
1910 gtk_file_filter_add_pattern(filter, "*.fio");
1911 gtk_file_filter_add_pattern(filter, "*.job");
1912 gtk_file_filter_add_pattern(filter, "*.ini");
1913 gtk_file_filter_add_mime_type(filter, "text/fio");
1914 gtk_file_filter_set_name(filter, "Fio job file");
1915 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1917 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1918 gtk_widget_destroy(dialog);
1922 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1924 gtk_widget_destroy(dialog);
1926 if (get_connection_details(&host, &port, &type, &server_start))
1929 filenames = fn_glist;
1930 while (filenames != NULL) {
1931 struct fio_client *client;
1933 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1934 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1937 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1941 error = g_error_new(g_quark_from_string("fio"), 1,
1942 "Failed to add client %s", host);
1943 report_error(error);
1944 g_error_free(error);
1946 gfio_client_added(ge, client);
1948 g_free(filenames->data);
1949 filenames = g_slist_next(filenames);
1954 gfio_start_server();
1956 g_slist_free(fn_glist);
1959 static void file_save(GtkWidget *w, gpointer data)
1961 struct gui *ui = data;
1964 dialog = gtk_file_chooser_dialog_new("Save File",
1965 GTK_WINDOW(ui->window),
1966 GTK_FILE_CHOOSER_ACTION_SAVE,
1967 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1968 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1971 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1972 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1974 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1977 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1978 // save_job_file(filename);
1981 gtk_widget_destroy(dialog);
1984 static void view_log_destroy(GtkWidget *w, gpointer data)
1986 struct gui *ui = (struct gui *) data;
1988 gtk_widget_ref(ui->log_tree);
1989 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1990 gtk_widget_destroy(w);
1991 ui->log_view = NULL;
1994 static void view_log(GtkWidget *w, gpointer data)
1996 GtkWidget *win, *scroll, *vbox, *box;
1997 struct gui *ui = (struct gui *) data;
2002 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2003 gtk_window_set_title(GTK_WINDOW(win), "Log");
2004 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2006 scroll = gtk_scrolled_window_new(NULL, NULL);
2008 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2010 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2012 box = gtk_hbox_new(TRUE, 0);
2013 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2014 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2015 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2017 vbox = gtk_vbox_new(TRUE, 5);
2018 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2020 gtk_container_add(GTK_CONTAINER(win), vbox);
2021 gtk_widget_show_all(win);
2024 static void preferences(GtkWidget *w, gpointer data)
2026 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2029 dialog = gtk_dialog_new_with_buttons("Preferences",
2030 GTK_WINDOW(main_ui.window),
2031 GTK_DIALOG_DESTROY_WITH_PARENT,
2032 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2033 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2036 frame = gtk_frame_new("Debug logging");
2037 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2039 vbox = gtk_vbox_new(FALSE, 6);
2040 gtk_container_add(GTK_CONTAINER(frame), vbox);
2042 box = gtk_hbox_new(FALSE, 6);
2043 gtk_container_add(GTK_CONTAINER(vbox), box);
2045 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2047 for (i = 0; i < FD_DEBUG_MAX; i++) {
2049 box = gtk_hbox_new(FALSE, 6);
2050 gtk_container_add(GTK_CONTAINER(vbox), box);
2054 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2055 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2056 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2059 frame = gtk_frame_new("Graph font");
2060 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2061 vbox = gtk_vbox_new(FALSE, 6);
2062 gtk_container_add(GTK_CONTAINER(frame), vbox);
2064 font = gtk_font_button_new();
2065 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2067 gtk_widget_show_all(dialog);
2069 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2070 gtk_widget_destroy(dialog);
2074 for (i = 0; i < FD_DEBUG_MAX; i++) {
2077 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2079 fio_debug |= (1UL << i);
2082 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2083 gtk_widget_destroy(dialog);
2086 static void about_dialog(GtkWidget *w, gpointer data)
2088 const char *authors[] = {
2089 "Jens Axboe <axboe@kernel.dk>",
2090 "Stephen Carmeron <stephenmcameron@gmail.com>",
2093 const char *license[] = {
2094 "Fio is free software; you can redistribute it and/or modify "
2095 "it under the terms of the GNU General Public License as published by "
2096 "the Free Software Foundation; either version 2 of the License, or "
2097 "(at your option) any later version.\n",
2098 "Fio is distributed in the hope that it will be useful, "
2099 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2100 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2101 "GNU General Public License for more details.\n",
2102 "You should have received a copy of the GNU General Public License "
2103 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2104 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2106 char *license_trans;
2108 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2109 license[2], "\n", NULL);
2111 gtk_show_about_dialog(NULL,
2112 "program-name", "gfio",
2113 "comments", "Gtk2 UI for fio",
2114 "license", license_trans,
2115 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2117 "version", fio_version_string,
2118 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2119 "logo-icon-name", "fio",
2121 "wrap-license", TRUE,
2124 g_free(license_trans);
2127 static GtkActionEntry menu_items[] = {
2128 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2129 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2130 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2131 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2132 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2133 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2134 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2135 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2136 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2137 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2139 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2141 static const gchar *ui_string = " \
2143 <menubar name=\"MainMenu\"> \
2144 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2145 <menuitem name=\"New\" action=\"NewFile\" /> \
2146 <separator name=\"Separator1\"/> \
2147 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2148 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2149 <separator name=\"Separator2\"/> \
2150 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2151 <separator name=\"Separator3\"/> \
2152 <menuitem name=\"Quit\" action=\"Quit\" /> \
2154 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2155 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2157 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2158 <menuitem name=\"About\" action=\"About\" /> \
2164 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2167 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2170 action_group = gtk_action_group_new("Menu");
2171 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2173 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2174 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2176 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2177 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2180 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2181 GtkWidget *vbox, GtkUIManager *ui_manager)
2183 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2186 static GtkWidget *new_client_page(struct gui_entry *ge)
2188 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2191 main_vbox = gtk_vbox_new(FALSE, 3);
2193 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2194 ge->topvbox = gtk_vbox_new(FALSE, 3);
2195 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2196 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2198 probe = gtk_frame_new("Job");
2199 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2200 probe_frame = gtk_vbox_new(FALSE, 3);
2201 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2203 probe_box = gtk_hbox_new(FALSE, 3);
2204 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2205 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2206 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2207 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2208 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2210 probe_box = gtk_hbox_new(FALSE, 3);
2211 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2213 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2214 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2215 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2216 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2217 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2218 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2220 probe_box = gtk_hbox_new(FALSE, 3);
2221 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2222 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2223 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2224 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2225 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2228 * Only add this if we have a commit rate
2231 probe_box = gtk_hbox_new(FALSE, 3);
2232 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2234 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2235 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2237 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2238 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2242 * Set up a drawing area and IOPS and bandwidth graphs
2244 gdk_color_parse("white", &white);
2245 ge->graphs.drawing_area = gtk_drawing_area_new();
2246 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2247 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2248 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2249 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2250 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2251 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2252 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2253 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2254 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2255 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2256 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2257 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2258 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2259 ge->graphs.drawing_area);
2260 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2263 setup_graphs(&ge->graphs);
2266 * Set up alignments for widgets at the bottom of ui,
2267 * align bottom left, expand horizontally but not vertically
2269 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2270 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2271 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2272 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2275 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2278 * Set up thread status progress bar
2280 ge->thread_status_pb = gtk_progress_bar_new();
2281 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2282 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2283 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2289 static GtkWidget *new_main_page(struct gui *ui)
2291 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2294 main_vbox = gtk_vbox_new(FALSE, 3);
2297 * Set up alignments for widgets at the top of ui,
2298 * align top left, expand horizontally but not vertically
2300 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2301 ui->topvbox = gtk_vbox_new(FALSE, 0);
2302 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2303 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2305 probe = gtk_frame_new("Run statistics");
2306 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2307 probe_frame = gtk_vbox_new(FALSE, 3);
2308 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2310 probe_box = gtk_hbox_new(FALSE, 3);
2311 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2312 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2313 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2314 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2315 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2316 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2319 * Only add this if we have a commit rate
2322 probe_box = gtk_hbox_new(FALSE, 3);
2323 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2325 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2326 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2328 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2329 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2333 * Set up a drawing area and IOPS and bandwidth graphs
2335 gdk_color_parse("white", &white);
2336 ui->graphs.drawing_area = gtk_drawing_area_new();
2337 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2338 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2339 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2340 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2341 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2342 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2343 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2344 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2345 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2346 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2347 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2348 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2349 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2350 ui->graphs.drawing_area);
2351 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2354 setup_graphs(&ui->graphs);
2357 * Set up alignments for widgets at the bottom of ui,
2358 * align bottom left, expand horizontally but not vertically
2360 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2361 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2362 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2363 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2367 * Set up thread status progress bar
2369 ui->thread_status_pb = gtk_progress_bar_new();
2370 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2371 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2372 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2377 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2378 guint page, gpointer data)
2384 static void init_ui(int *argc, char **argv[], struct gui *ui)
2386 GtkSettings *settings;
2387 GtkUIManager *uimanager;
2388 GtkWidget *menu, *vbox;
2390 /* Magical g*thread incantation, you just need this thread stuff.
2391 * Without it, the update that happens in gfio_update_thread_status
2392 * doesn't really happen in a timely fashion, you need expose events
2394 if (!g_thread_supported())
2395 g_thread_init(NULL);
2398 gtk_init(argc, argv);
2399 settings = gtk_settings_get_default();
2400 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2403 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2404 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2405 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2407 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2408 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2410 ui->vbox = gtk_vbox_new(FALSE, 0);
2411 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2413 uimanager = gtk_ui_manager_new();
2414 menu = get_menubar_menu(ui->window, uimanager, ui);
2415 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2417 ui->notebook = gtk_notebook_new();
2418 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2419 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2420 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2421 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2423 vbox = new_main_page(ui);
2425 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2427 gfio_ui_setup_log(ui);
2429 gtk_widget_show_all(ui->window);
2432 int main(int argc, char *argv[], char *envp[])
2434 if (initialize_fio(envp))
2436 if (fio_init_options())
2439 memset(&main_ui, 0, sizeof(main_ui));
2440 INIT_FLIST_HEAD(&main_ui.list);
2442 init_ui(&argc, &argv, &main_ui);
2444 gdk_threads_enter();
2446 gdk_threads_leave();