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), "");
220 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
221 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
225 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
226 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
227 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
228 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
229 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
232 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
234 GtkWidget *entry, *frame;
236 frame = gtk_frame_new(label);
237 entry = gtk_entry_new();
238 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
239 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
240 gtk_container_add(GTK_CONTAINER(frame), entry);
245 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
247 GtkWidget *label_widget;
250 frame = gtk_frame_new(label);
251 label_widget = gtk_label_new(NULL);
252 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
253 gtk_container_add(GTK_CONTAINER(frame), label_widget);
258 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
260 GtkWidget *button, *box;
262 box = gtk_hbox_new(FALSE, 3);
263 gtk_container_add(GTK_CONTAINER(hbox), box);
265 button = gtk_spin_button_new_with_range(min, max, 1.0);
266 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
268 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
269 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
274 static void gfio_set_connected(struct gui_entry *ge, int connected)
277 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
279 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
280 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
283 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
284 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
285 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
286 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
290 static void label_set_int_value(GtkWidget *entry, unsigned int val)
294 sprintf(tmp, "%u", val);
295 gtk_label_set_text(GTK_LABEL(entry), tmp);
298 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
302 sprintf(tmp, "%u", val);
303 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
307 #define ALIGN_RIGHT 2
311 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
313 GtkCellRenderer *renderer;
314 GtkTreeViewColumn *col;
315 double xalign = 0.0; /* left as default */
316 PangoAlignment align;
319 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
320 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
322 visible = !(flags & INVISIBLE);
324 renderer = gtk_cell_renderer_text_new();
325 col = gtk_tree_view_column_new();
327 gtk_tree_view_column_set_title(col, title);
328 if (!(flags & UNSORTABLE))
329 gtk_tree_view_column_set_sort_column_id(col, index);
330 gtk_tree_view_column_set_resizable(col, TRUE);
331 gtk_tree_view_column_pack_start(col, renderer, TRUE);
332 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
333 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
335 case PANGO_ALIGN_LEFT:
338 case PANGO_ALIGN_CENTER:
341 case PANGO_ALIGN_RIGHT:
345 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
346 gtk_tree_view_column_set_visible(col, visible);
347 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
351 static void gfio_ui_setup_log(struct gui *ui)
353 GtkTreeSelection *selection;
355 GtkWidget *tree_view;
357 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
359 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
360 gtk_widget_set_can_focus(tree_view, FALSE);
362 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
363 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
364 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
365 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
367 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
368 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
369 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
370 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
372 ui->log_model = model;
373 ui->log_tree = tree_view;
376 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
382 GType types[FIO_IO_U_LIST_MAX_LEN];
383 GtkWidget *tree_view;
384 GtkTreeSelection *selection;
389 for (i = 0; i < len; i++)
390 types[i] = G_TYPE_INT;
392 model = gtk_list_store_newv(len, types);
394 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
395 gtk_widget_set_can_focus(tree_view, FALSE);
397 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
398 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
400 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
401 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
403 for (i = 0; i < len; i++) {
406 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
407 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
410 gtk_list_store_append(model, &iter);
412 for (i = 0; i < len; i++) {
414 ovals[i] = (ovals[i] + 999) / 1000;
415 gtk_list_store_set(model, &iter, i, ovals[i], -1);
421 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
424 unsigned int *io_u_plat = ts->io_u_plat[ddir];
425 unsigned long nr = ts->clat_stat[ddir].samples;
426 fio_fp64_t *plist = ts->percentile_list;
427 unsigned int *ovals, len, minv, maxv, scale_down;
429 GtkWidget *tree_view, *frame, *hbox;
432 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
437 * We default to usecs, but if the value range is such that we
438 * should scale down to msecs, do that.
440 if (minv > 2000 && maxv > 99999) {
448 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
450 sprintf(tmp, "Completion percentiles (%s)", base);
451 frame = gtk_frame_new(tmp);
452 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
454 hbox = gtk_hbox_new(FALSE, 3);
455 gtk_container_add(GTK_CONTAINER(frame), hbox);
457 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
463 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
464 unsigned long max, double mean, double dev)
466 const char *base = "(usec)";
467 GtkWidget *hbox, *label, *frame;
471 if (!usec_to_msec(&min, &max, &mean, &dev))
474 minp = num2str(min, 6, 1, 0);
475 maxp = num2str(max, 6, 1, 0);
477 sprintf(tmp, "%s %s", name, base);
478 frame = gtk_frame_new(tmp);
479 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
481 hbox = gtk_hbox_new(FALSE, 3);
482 gtk_container_add(GTK_CONTAINER(frame), hbox);
484 label = new_info_label_in_frame(hbox, "Minimum");
485 gtk_label_set_text(GTK_LABEL(label), minp);
486 label = new_info_label_in_frame(hbox, "Maximum");
487 gtk_label_set_text(GTK_LABEL(label), maxp);
488 label = new_info_label_in_frame(hbox, "Average");
489 sprintf(tmp, "%5.02f", mean);
490 gtk_label_set_text(GTK_LABEL(label), tmp);
491 label = new_info_label_in_frame(hbox, "Standard deviation");
492 sprintf(tmp, "%5.02f", dev);
493 gtk_label_set_text(GTK_LABEL(label), tmp);
504 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
505 struct thread_stat *ts, int ddir)
507 const char *ddir_label[2] = { "Read", "Write" };
508 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
509 unsigned long min[3], max[3], runt;
510 unsigned long long bw, iops;
511 unsigned int flags = 0;
512 double mean[3], dev[3];
513 char *io_p, *bw_p, *iops_p;
516 if (!ts->runtime[ddir])
519 i2p = is_power_of_2(rs->kb_base);
520 runt = ts->runtime[ddir];
522 bw = (1000 * ts->io_bytes[ddir]) / runt;
523 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
524 bw_p = num2str(bw, 6, 1, i2p);
526 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
527 iops_p = num2str(iops, 6, 1, 0);
529 box = gtk_hbox_new(FALSE, 3);
530 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
532 frame = gtk_frame_new(ddir_label[ddir]);
533 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
535 main_vbox = gtk_vbox_new(FALSE, 3);
536 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
538 box = gtk_hbox_new(FALSE, 3);
539 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
541 label = new_info_label_in_frame(box, "IO");
542 gtk_label_set_text(GTK_LABEL(label), io_p);
543 label = new_info_label_in_frame(box, "Bandwidth");
544 gtk_label_set_text(GTK_LABEL(label), bw_p);
545 label = new_info_label_in_frame(box, "IOPS");
546 gtk_label_set_text(GTK_LABEL(label), iops_p);
547 label = new_info_label_in_frame(box, "Runtime (msec)");
548 label_set_int_value(label, ts->runtime[ddir]);
550 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
551 double p_of_agg = 100.0;
552 const char *bw_str = "KB";
556 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
557 if (p_of_agg > 100.0)
561 if (mean[0] > 999999.9) {
569 sprintf(tmp, "Bandwidth (%s)", bw_str);
570 frame = gtk_frame_new(tmp);
571 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
573 box = gtk_hbox_new(FALSE, 3);
574 gtk_container_add(GTK_CONTAINER(frame), box);
576 label = new_info_label_in_frame(box, "Minimum");
577 label_set_int_value(label, min[0]);
578 label = new_info_label_in_frame(box, "Maximum");
579 label_set_int_value(label, max[0]);
580 label = new_info_label_in_frame(box, "Percentage of jobs");
581 sprintf(tmp, "%3.2f%%", p_of_agg);
582 gtk_label_set_text(GTK_LABEL(label), tmp);
583 label = new_info_label_in_frame(box, "Average");
584 sprintf(tmp, "%5.02f", mean[0]);
585 gtk_label_set_text(GTK_LABEL(label), tmp);
586 label = new_info_label_in_frame(box, "Standard deviation");
587 sprintf(tmp, "%5.02f", dev[0]);
588 gtk_label_set_text(GTK_LABEL(label), tmp);
591 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
593 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
595 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
599 frame = gtk_frame_new("Latency");
600 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
602 vbox = gtk_vbox_new(FALSE, 3);
603 gtk_container_add(GTK_CONTAINER(frame), vbox);
605 if (flags & GFIO_SLAT)
606 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
607 if (flags & GFIO_CLAT)
608 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
609 if (flags & GFIO_LAT)
610 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
613 if (ts->clat_percentiles)
614 gfio_show_clat_percentiles(main_vbox, ts, ddir);
622 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
625 GtkWidget *tree_view;
626 GtkTreeSelection *selection;
633 * Check if all are empty, in which case don't bother
635 for (i = 0, skipped = 0; i < num; i++)
642 types = malloc(num * sizeof(GType));
644 for (i = 0; i < num; i++)
645 types[i] = G_TYPE_STRING;
647 model = gtk_list_store_newv(num, types);
651 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
652 gtk_widget_set_can_focus(tree_view, FALSE);
654 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
655 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
657 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
658 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
660 for (i = 0; i < num; i++)
661 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
663 gtk_list_store_append(model, &iter);
665 for (i = 0; i < num; i++) {
669 sprintf(fbuf, "0.00");
671 sprintf(fbuf, "%3.2f%%", lat[i]);
673 gtk_list_store_set(model, &iter, i, fbuf, -1);
679 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
681 GtkWidget *box, *frame, *tree_view;
682 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
683 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
684 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
685 "250", "500", "750", "1000", };
686 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
687 "250", "500", "750", "1000", "2000",
690 stat_calc_lat_u(ts, io_u_lat_u);
691 stat_calc_lat_m(ts, io_u_lat_m);
693 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
695 frame = gtk_frame_new("Latency buckets (usec)");
696 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
698 box = gtk_hbox_new(FALSE, 3);
699 gtk_container_add(GTK_CONTAINER(frame), box);
700 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
703 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
705 frame = gtk_frame_new("Latency buckets (msec)");
706 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
708 box = gtk_hbox_new(FALSE, 3);
709 gtk_container_add(GTK_CONTAINER(frame), box);
710 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
714 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
716 GtkWidget *box, *frame, *entry;
717 double usr_cpu, sys_cpu;
718 unsigned long runtime;
721 runtime = ts->total_run_time;
723 double runt = (double) runtime;
725 usr_cpu = (double) ts->usr_time * 100 / runt;
726 sys_cpu = (double) ts->sys_time * 100 / runt;
732 frame = gtk_frame_new("OS resources");
733 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
735 box = gtk_hbox_new(FALSE, 3);
736 gtk_container_add(GTK_CONTAINER(frame), box);
738 entry = new_info_entry_in_frame(box, "User CPU");
739 sprintf(tmp, "%3.2f%%", usr_cpu);
740 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
741 entry = new_info_entry_in_frame(box, "System CPU");
742 sprintf(tmp, "%3.2f%%", sys_cpu);
743 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
744 entry = new_info_entry_in_frame(box, "Context switches");
745 entry_set_int_value(entry, ts->ctx);
746 entry = new_info_entry_in_frame(box, "Major faults");
747 entry_set_int_value(entry, ts->majf);
748 entry = new_info_entry_in_frame(box, "Minor faults");
749 entry_set_int_value(entry, ts->minf);
751 static void gfio_add_sc_depths_tree(GtkListStore *model,
752 struct thread_stat *ts, unsigned int len,
755 double io_u_dist[FIO_IO_U_MAP_NR];
757 /* Bits 0, and 3-8 */
758 const int add_mask = 0x1f9;
762 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
764 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
766 gtk_list_store_append(model, &iter);
768 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
770 for (i = 1, j = 0; i < len; i++) {
773 if (!(add_mask & (1UL << (i - 1))))
774 sprintf(fbuf, "0.0%%");
776 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
780 gtk_list_store_set(model, &iter, i, fbuf, -1);
785 static void gfio_add_total_depths_tree(GtkListStore *model,
786 struct thread_stat *ts, unsigned int len)
788 double io_u_dist[FIO_IO_U_MAP_NR];
790 /* Bits 1-6, and 8 */
791 const int add_mask = 0x17e;
794 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
796 gtk_list_store_append(model, &iter);
798 gtk_list_store_set(model, &iter, 0, "Total", -1);
800 for (i = 1, j = 0; i < len; i++) {
803 if (!(add_mask & (1UL << (i - 1))))
804 sprintf(fbuf, "0.0%%");
806 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
810 gtk_list_store_set(model, &iter, i, fbuf, -1);
815 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
817 GtkWidget *frame, *box, *tree_view;
818 GtkTreeSelection *selection;
820 GType types[FIO_IO_U_MAP_NR + 1];
823 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
825 frame = gtk_frame_new("IO depths");
826 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
828 box = gtk_hbox_new(FALSE, 3);
829 gtk_container_add(GTK_CONTAINER(frame), box);
831 for (i = 0; i < NR_LABELS; i++)
832 types[i] = G_TYPE_STRING;
834 model = gtk_list_store_newv(NR_LABELS, types);
836 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
837 gtk_widget_set_can_focus(tree_view, FALSE);
839 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
840 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
842 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
843 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
845 for (i = 0; i < NR_LABELS; i++)
846 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
848 gfio_add_total_depths_tree(model, ts, NR_LABELS);
849 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
850 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
852 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
855 static gboolean results_window_delete(GtkWidget *w, gpointer data)
857 struct gui_entry *ge = (struct gui_entry *) data;
859 gtk_widget_destroy(w);
860 ge->results_window = NULL;
861 ge->results_notebook = NULL;
865 static GtkWidget *get_results_window(struct gui_entry *ge)
867 GtkWidget *win, *notebook;
869 if (ge->results_window)
870 return ge->results_notebook;
872 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
873 gtk_window_set_title(GTK_WINDOW(win), "Results");
874 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
875 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
876 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
878 notebook = gtk_notebook_new();
879 gtk_container_add(GTK_CONTAINER(win), notebook);
881 ge->results_window = win;
882 ge->results_notebook = notebook;
883 return ge->results_notebook;
886 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
887 struct group_run_stats *rs)
889 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
890 struct gfio_client *gc = client->client_data;
894 res_win = get_results_window(gc->ge);
896 scroll = gtk_scrolled_window_new(NULL, NULL);
897 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
898 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
900 vbox = gtk_vbox_new(FALSE, 3);
902 box = gtk_hbox_new(FALSE, 0);
903 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
905 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
907 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
909 gc->results_widget = vbox;
911 entry = new_info_entry_in_frame(box, "Name");
912 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
913 if (strlen(ts->description)) {
914 entry = new_info_entry_in_frame(box, "Description");
915 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
917 entry = new_info_entry_in_frame(box, "Group ID");
918 entry_set_int_value(entry, ts->groupid);
919 entry = new_info_entry_in_frame(box, "Jobs");
920 entry_set_int_value(entry, ts->members);
921 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
922 entry_set_int_value(entry, ts->error);
923 entry = new_info_entry_in_frame(box, "PID");
924 entry_set_int_value(entry, ts->pid);
926 if (ts->io_bytes[DDIR_READ])
927 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
928 if (ts->io_bytes[DDIR_WRITE])
929 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
931 gfio_show_latency_buckets(vbox, ts);
932 gfio_show_cpu_usage(vbox, ts);
933 gfio_show_io_depths(vbox, ts);
935 gtk_widget_show_all(gc->ge->results_window);
939 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
941 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
942 struct gui *ui = &main_ui;
946 char tmp[64], timebuf[80];
949 tm = localtime(&sec);
950 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
951 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
955 gtk_list_store_append(ui->log_model, &iter);
956 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
957 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
958 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
959 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
961 if (p->level == FIO_LOG_ERR)
962 view_log(NULL, (gpointer) ui);
967 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
969 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
970 struct gfio_client *gc = client->client_data;
971 GtkWidget *box, *frame, *entry, *vbox;
977 if (!gc->results_widget)
980 if (!gc->disk_util_frame) {
981 gc->disk_util_frame = gtk_frame_new("Disk utilization");
982 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
985 vbox = gtk_vbox_new(FALSE, 3);
986 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
988 frame = gtk_frame_new((char *) p->dus.name);
989 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
991 box = gtk_vbox_new(FALSE, 3);
992 gtk_container_add(GTK_CONTAINER(frame), box);
994 frame = gtk_frame_new("Read");
995 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
996 vbox = gtk_hbox_new(TRUE, 3);
997 gtk_container_add(GTK_CONTAINER(frame), vbox);
998 entry = new_info_entry_in_frame(vbox, "IOs");
999 entry_set_int_value(entry, p->dus.ios[0]);
1000 entry = new_info_entry_in_frame(vbox, "Merges");
1001 entry_set_int_value(entry, p->dus.merges[0]);
1002 entry = new_info_entry_in_frame(vbox, "Sectors");
1003 entry_set_int_value(entry, p->dus.sectors[0]);
1004 entry = new_info_entry_in_frame(vbox, "Ticks");
1005 entry_set_int_value(entry, p->dus.ticks[0]);
1007 frame = gtk_frame_new("Write");
1008 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1009 vbox = gtk_hbox_new(TRUE, 3);
1010 gtk_container_add(GTK_CONTAINER(frame), vbox);
1011 entry = new_info_entry_in_frame(vbox, "IOs");
1012 entry_set_int_value(entry, p->dus.ios[1]);
1013 entry = new_info_entry_in_frame(vbox, "Merges");
1014 entry_set_int_value(entry, p->dus.merges[1]);
1015 entry = new_info_entry_in_frame(vbox, "Sectors");
1016 entry_set_int_value(entry, p->dus.sectors[1]);
1017 entry = new_info_entry_in_frame(vbox, "Ticks");
1018 entry_set_int_value(entry, p->dus.ticks[1]);
1020 frame = gtk_frame_new("Shared");
1021 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1022 vbox = gtk_hbox_new(TRUE, 3);
1023 gtk_container_add(GTK_CONTAINER(frame), vbox);
1024 entry = new_info_entry_in_frame(vbox, "IO ticks");
1025 entry_set_int_value(entry, p->dus.io_ticks);
1026 entry = new_info_entry_in_frame(vbox, "Time in queue");
1027 entry_set_int_value(entry, p->dus.time_in_queue);
1031 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1035 sprintf(tmp, "%3.2f%%", util);
1036 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1037 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1039 gtk_widget_show_all(gc->results_widget);
1041 gdk_threads_leave();
1044 extern int sum_stat_clients;
1045 extern struct thread_stat client_ts;
1046 extern struct group_run_stats client_gs;
1048 static int sum_stat_nr;
1050 static void gfio_thread_status_op(struct fio_client *client,
1051 struct fio_net_cmd *cmd)
1053 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1055 gfio_display_ts(client, &p->ts, &p->rs);
1057 if (sum_stat_clients == 1)
1060 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1061 sum_group_stats(&client_gs, &p->rs);
1063 client_ts.members++;
1064 client_ts.groupid = p->ts.groupid;
1066 if (++sum_stat_nr == sum_stat_clients) {
1067 strcpy(client_ts.name, "All clients");
1068 gfio_display_ts(client, &client_ts, &client_gs);
1072 static void gfio_group_stats_op(struct fio_client *client,
1073 struct fio_net_cmd *cmd)
1075 /* We're ignoring group stats for now */
1078 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1081 struct gfio_graphs *g = data;
1083 g->drawing_area_xdim = w->allocation.width;
1084 g->drawing_area_ydim = w->allocation.height;
1088 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1090 struct gfio_graphs *g = p;
1093 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1094 g->drawing_area_ydim);
1095 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1096 g->drawing_area_ydim);
1097 cr = gdk_cairo_create(w->window);
1099 cairo_set_source_rgb(cr, 0, 0, 0);
1102 cairo_translate(cr, 0, 0);
1103 line_graph_draw(g->bandwidth_graph, cr);
1108 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1109 line_graph_draw(g->iops_graph, cr);
1118 * Client specific ETA
1120 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1122 struct gfio_client *gc = client->client_data;
1123 struct gui_entry *ge = gc->ge;
1124 static int eta_good;
1131 gdk_threads_enter();
1136 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1137 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1138 eta_to_str(eta_str, je->eta_sec);
1141 sprintf(tmp, "%u", je->nr_running);
1142 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1143 sprintf(tmp, "%u", je->files_open);
1144 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1147 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1148 if (je->m_rate || je->t_rate) {
1151 mr = num2str(je->m_rate, 4, 0, i2p);
1152 tr = num2str(je->t_rate, 4, 0, i2p);
1153 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1154 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1157 } else if (je->m_iops || je->t_iops)
1158 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1160 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1161 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1162 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1163 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1166 if (je->eta_sec != INT_MAX && je->nr_running) {
1170 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1171 strcpy(output, "-.-% done");
1175 sprintf(output, "%3.1f%% done", perc);
1178 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1179 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1181 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1182 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1185 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1187 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1189 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1190 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1191 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1192 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1201 char *dst = output + strlen(output);
1203 sprintf(dst, " - %s", eta_str);
1206 gfio_update_thread_status(ge, output, perc);
1207 gdk_threads_leave();
1211 * Update ETA in main window for all clients
1213 static void gfio_update_all_eta(struct jobs_eta *je)
1215 struct gui *ui = &main_ui;
1216 static int eta_good;
1222 gdk_threads_enter();
1227 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1228 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1229 eta_to_str(eta_str, je->eta_sec);
1233 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1234 if (je->m_rate || je->t_rate) {
1237 mr = num2str(je->m_rate, 4, 0, i2p);
1238 tr = num2str(je->t_rate, 4, 0, i2p);
1239 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1240 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1243 } else if (je->m_iops || je->t_iops)
1244 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1246 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1247 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1248 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1249 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1252 if (je->eta_sec != INT_MAX && je->nr_running) {
1256 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1257 strcpy(output, "-.-% done");
1261 sprintf(output, "%3.1f%% done", perc);
1264 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1265 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1267 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1268 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1270 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1271 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1272 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1273 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1275 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1276 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1277 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1278 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1287 char *dst = output + strlen(output);
1289 sprintf(dst, " - %s", eta_str);
1292 gfio_update_thread_status_all(output, perc);
1293 gdk_threads_leave();
1296 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1298 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1299 struct gfio_client *gc = client->client_data;
1300 struct gui_entry *ge = gc->ge;
1301 const char *os, *arch;
1304 os = fio_get_os_string(probe->os);
1308 arch = fio_get_arch_string(probe->arch);
1313 client->name = strdup((char *) probe->hostname);
1315 gdk_threads_enter();
1317 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1318 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1319 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1320 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1321 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1323 gfio_set_connected(ge, 1);
1325 gdk_threads_leave();
1328 static void gfio_update_thread_status(struct gui_entry *ge,
1329 char *status_message, double perc)
1331 static char message[100];
1332 const char *m = message;
1334 strncpy(message, status_message, sizeof(message) - 1);
1335 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1336 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1337 gtk_widget_queue_draw(main_ui.window);
1340 static void gfio_update_thread_status_all(char *status_message, double perc)
1342 struct gui *ui = &main_ui;
1343 static char message[100];
1344 const char *m = message;
1346 strncpy(message, status_message, sizeof(message) - 1);
1347 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1348 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1349 gtk_widget_queue_draw(ui->window);
1352 static void gfio_quit_op(struct fio_client *client)
1354 struct gfio_client *gc = client->client_data;
1356 gdk_threads_enter();
1357 gfio_set_connected(gc->ge, 0);
1358 gdk_threads_leave();
1361 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1363 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1364 struct gfio_client *gc = client->client_data;
1365 struct thread_options *o = &gc->o;
1366 struct gui_entry *ge = gc->ge;
1369 convert_thread_options_to_cpu(o, &p->top);
1371 gdk_threads_enter();
1373 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1375 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1376 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1377 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1379 sprintf(tmp, "%u", o->iodepth);
1380 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1384 gdk_threads_leave();
1387 static void gfio_client_timed_out(struct fio_client *client)
1389 struct gfio_client *gc = client->client_data;
1390 GtkWidget *dialog, *label, *content;
1393 gdk_threads_enter();
1395 gfio_set_connected(gc->ge, 0);
1396 clear_ge_ui_info(gc->ge);
1398 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1400 dialog = gtk_dialog_new_with_buttons("Timed out!",
1401 GTK_WINDOW(main_ui.window),
1402 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1403 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1405 /* gtk_dialog_get_content_area() is 2.14 and newer */
1406 content = GTK_DIALOG(dialog)->vbox;
1408 label = gtk_label_new((const gchar *) buf);
1409 gtk_container_add(GTK_CONTAINER(content), label);
1410 gtk_widget_show_all(dialog);
1411 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1413 gtk_dialog_run(GTK_DIALOG(dialog));
1414 gtk_widget_destroy(dialog);
1416 gdk_threads_leave();
1419 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1421 struct gfio_client *gc = client->client_data;
1423 gdk_threads_enter();
1425 gfio_set_connected(gc->ge, 0);
1428 entry_set_int_value(gc->err_entry, client->error);
1430 gdk_threads_leave();
1433 struct client_ops gfio_client_ops = {
1434 .text_op = gfio_text_op,
1435 .disk_util = gfio_disk_util_op,
1436 .thread_status = gfio_thread_status_op,
1437 .group_stats = gfio_group_stats_op,
1438 .jobs_eta = gfio_update_client_eta,
1439 .eta = gfio_update_all_eta,
1440 .probe = gfio_probe_op,
1441 .quit = gfio_quit_op,
1442 .add_job = gfio_add_job_op,
1443 .timed_out = gfio_client_timed_out,
1444 .stop = gfio_client_stop,
1445 .stay_connected = 1,
1448 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1449 __attribute__((unused)) gpointer data)
1454 static void *job_thread(void *arg)
1456 struct gui *ui = arg;
1458 ui->handler_running = 1;
1459 fio_handle_clients(&gfio_client_ops);
1460 ui->handler_running = 0;
1464 static int send_job_files(struct gui_entry *ge)
1466 struct gfio_client *gc = ge->client;
1469 for (i = 0; i < ge->nr_job_files; i++) {
1470 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1474 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1475 report_error(error);
1476 g_error_free(error);
1481 free(ge->job_files[i]);
1482 ge->job_files[i] = NULL;
1484 while (i < ge->nr_job_files) {
1485 free(ge->job_files[i]);
1486 ge->job_files[i] = NULL;
1493 static void *server_thread(void *arg)
1496 gfio_server_running = 1;
1497 fio_start_server(NULL);
1498 gfio_server_running = 0;
1502 static void gfio_start_server(void)
1504 struct gui *ui = &main_ui;
1506 if (!gfio_server_running) {
1507 gfio_server_running = 1;
1508 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1509 pthread_detach(ui->server_t);
1513 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1516 struct gui_entry *ge = data;
1517 struct gfio_client *gc = ge->client;
1519 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1520 fio_start_client(gc->client);
1523 static void file_open(GtkWidget *w, gpointer data);
1525 static void connect_clicked(GtkWidget *widget, gpointer data)
1527 struct gui_entry *ge = data;
1528 struct gfio_client *gc = ge->client;
1530 if (!ge->connected) {
1533 if (!ge->nr_job_files)
1534 file_open(widget, data);
1535 if (!ge->nr_job_files)
1538 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1539 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1540 ret = fio_client_connect(gc->client);
1542 if (!ge->ui->handler_running)
1543 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1544 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1545 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1549 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1550 report_error(error);
1551 g_error_free(error);
1554 fio_client_terminate(gc->client);
1555 gfio_set_connected(ge, 0);
1556 clear_ge_ui_info(ge);
1560 static void send_clicked(GtkWidget *widget, gpointer data)
1562 struct gui_entry *ge = data;
1564 if (send_job_files(ge)) {
1567 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);
1568 report_error(error);
1569 g_error_free(error);
1571 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1574 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1575 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1578 static GtkWidget *add_button(GtkWidget *buttonbox,
1579 struct button_spec *buttonspec, gpointer data)
1581 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1583 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1584 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1585 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1586 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1591 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1596 for (i = 0; i < nbuttons; i++)
1597 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1600 static void on_info_bar_response(GtkWidget *widget, gint response,
1603 struct gui *ui = &main_ui;
1605 if (response == GTK_RESPONSE_OK) {
1606 gtk_widget_destroy(widget);
1607 ui->error_info_bar = NULL;
1611 void report_error(GError *error)
1613 struct gui *ui = &main_ui;
1615 if (ui->error_info_bar == NULL) {
1616 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1619 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1620 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1623 ui->error_label = gtk_label_new(error->message);
1624 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1625 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1627 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1628 gtk_widget_show_all(ui->vbox);
1631 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1632 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1636 struct connection_widgets
1643 static void hostname_cb(GtkEntry *entry, gpointer data)
1645 struct connection_widgets *cw = data;
1646 int uses_net = 0, is_localhost = 0;
1651 * Check whether to display the 'auto start backend' box
1652 * or not. Show it if we are a localhost and using network,
1653 * or using a socket.
1655 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1656 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1661 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1662 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1663 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1664 !strcmp(text, "ip6-loopback"))
1668 if (!uses_net || is_localhost) {
1669 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1670 gtk_widget_set_sensitive(cw->button, 1);
1672 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1673 gtk_widget_set_sensitive(cw->button, 0);
1677 static int get_connection_details(char **host, int *port, int *type,
1680 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1681 struct connection_widgets cw;
1684 dialog = gtk_dialog_new_with_buttons("Connection details",
1685 GTK_WINDOW(main_ui.window),
1686 GTK_DIALOG_DESTROY_WITH_PARENT,
1687 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1688 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1690 frame = gtk_frame_new("Hostname / socket name");
1691 /* gtk_dialog_get_content_area() is 2.14 and newer */
1692 vbox = GTK_DIALOG(dialog)->vbox;
1693 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1695 box = gtk_vbox_new(FALSE, 6);
1696 gtk_container_add(GTK_CONTAINER(frame), box);
1698 hbox = gtk_hbox_new(TRUE, 10);
1699 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1700 cw.hentry = gtk_entry_new();
1701 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1702 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1704 frame = gtk_frame_new("Port");
1705 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1706 box = gtk_vbox_new(FALSE, 10);
1707 gtk_container_add(GTK_CONTAINER(frame), box);
1709 hbox = gtk_hbox_new(TRUE, 4);
1710 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1711 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1713 frame = gtk_frame_new("Type");
1714 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1715 box = gtk_vbox_new(FALSE, 10);
1716 gtk_container_add(GTK_CONTAINER(frame), box);
1718 hbox = gtk_hbox_new(TRUE, 4);
1719 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1721 cw.combo = gtk_combo_box_new_text();
1722 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1723 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1724 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1725 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1727 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1729 frame = gtk_frame_new("Options");
1730 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1731 box = gtk_vbox_new(FALSE, 10);
1732 gtk_container_add(GTK_CONTAINER(frame), box);
1734 hbox = gtk_hbox_new(TRUE, 4);
1735 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1737 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1738 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1739 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.");
1740 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1743 * Connect edit signal, so we can show/not-show the auto start button
1745 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1746 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1748 gtk_widget_show_all(dialog);
1750 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1751 gtk_widget_destroy(dialog);
1755 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1756 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1758 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1759 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1760 *type = Fio_client_ipv4;
1761 else if (!strncmp(typeentry, "IPv6", 4))
1762 *type = Fio_client_ipv6;
1764 *type = Fio_client_socket;
1767 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1769 gtk_widget_destroy(dialog);
1773 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1775 struct gfio_client *gc;
1777 gc = malloc(sizeof(*gc));
1778 memset(gc, 0, sizeof(*gc));
1780 gc->client = fio_get_client(client);
1784 client->client_data = gc;
1787 static GtkWidget *new_client_page(struct gui_entry *ge);
1789 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1791 struct gui_entry *ge;
1793 ge = malloc(sizeof(*ge));
1794 memset(ge, 0, sizeof(*ge));
1795 INIT_FLIST_HEAD(&ge->list);
1796 flist_add_tail(&ge->list, &ui->list);
1802 * FIXME: need more handling here
1804 static void ge_destroy(GtkWidget *w, gpointer data)
1806 struct gui_entry *ge = data;
1807 struct gfio_client *gc = ge->client;
1810 fio_put_client(gc->client);
1812 flist_del(&ge->list);
1816 static struct gui_entry *get_new_ge_with_tab(const char *name)
1818 struct gui_entry *ge;
1820 ge = alloc_new_gui_entry(&main_ui);
1822 ge->vbox = new_client_page(ge);
1823 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1825 ge->page_label = gtk_label_new(name);
1826 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1828 gtk_widget_show_all(main_ui.window);
1832 static void file_new(GtkWidget *w, gpointer data)
1834 get_new_ge_with_tab("Untitled");
1838 * Return the 'ge' corresponding to the tab. If the active tab is the
1839 * main tab, open a new tab.
1841 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1843 struct flist_head *entry;
1844 struct gui_entry *ge;
1847 return get_new_ge_with_tab("Untitled");
1849 flist_for_each(entry, &main_ui.list) {
1850 ge = flist_entry(entry, struct gui_entry, list);
1851 if (ge->page_num == cur_page)
1858 static void file_open(GtkWidget *w, gpointer data)
1860 struct gui *ui = data;
1862 GSList *filenames, *fn_glist;
1863 GtkFileFilter *filter;
1865 int port, type, server_start;
1866 struct gui_entry *ge;
1870 * Creates new tab if current tab is the main window, or the
1871 * current tab already has a client.
1873 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1874 ge = get_ge_from_page(cur_page);
1876 ge = get_new_ge_with_tab("Untitled");
1878 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1880 dialog = gtk_file_chooser_dialog_new("Open File",
1881 GTK_WINDOW(ui->window),
1882 GTK_FILE_CHOOSER_ACTION_OPEN,
1883 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1884 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1886 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1888 filter = gtk_file_filter_new();
1889 gtk_file_filter_add_pattern(filter, "*.fio");
1890 gtk_file_filter_add_pattern(filter, "*.job");
1891 gtk_file_filter_add_pattern(filter, "*.ini");
1892 gtk_file_filter_add_mime_type(filter, "text/fio");
1893 gtk_file_filter_set_name(filter, "Fio job file");
1894 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1896 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1897 gtk_widget_destroy(dialog);
1901 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1903 gtk_widget_destroy(dialog);
1905 if (get_connection_details(&host, &port, &type, &server_start))
1908 filenames = fn_glist;
1909 while (filenames != NULL) {
1910 struct fio_client *client;
1912 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1913 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1916 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1920 error = g_error_new(g_quark_from_string("fio"), 1,
1921 "Failed to add client %s", host);
1922 report_error(error);
1923 g_error_free(error);
1925 gfio_client_added(ge, client);
1927 g_free(filenames->data);
1928 filenames = g_slist_next(filenames);
1933 gfio_start_server();
1935 g_slist_free(fn_glist);
1938 static void file_save(GtkWidget *w, gpointer data)
1940 struct gui *ui = data;
1943 dialog = gtk_file_chooser_dialog_new("Save File",
1944 GTK_WINDOW(ui->window),
1945 GTK_FILE_CHOOSER_ACTION_SAVE,
1946 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1947 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1950 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1951 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1953 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1956 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1957 // save_job_file(filename);
1960 gtk_widget_destroy(dialog);
1963 static void view_log_destroy(GtkWidget *w, gpointer data)
1965 struct gui *ui = (struct gui *) data;
1967 gtk_widget_ref(ui->log_tree);
1968 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1969 gtk_widget_destroy(w);
1970 ui->log_view = NULL;
1973 static void view_log(GtkWidget *w, gpointer data)
1975 GtkWidget *win, *scroll, *vbox, *box;
1976 struct gui *ui = (struct gui *) data;
1981 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1982 gtk_window_set_title(GTK_WINDOW(win), "Log");
1983 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1985 scroll = gtk_scrolled_window_new(NULL, NULL);
1987 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1989 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1991 box = gtk_hbox_new(TRUE, 0);
1992 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1993 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1994 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1996 vbox = gtk_vbox_new(TRUE, 5);
1997 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1999 gtk_container_add(GTK_CONTAINER(win), vbox);
2000 gtk_widget_show_all(win);
2003 static void preferences(GtkWidget *w, gpointer data)
2005 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2008 dialog = gtk_dialog_new_with_buttons("Preferences",
2009 GTK_WINDOW(main_ui.window),
2010 GTK_DIALOG_DESTROY_WITH_PARENT,
2011 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2012 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2015 frame = gtk_frame_new("Debug logging");
2016 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2018 vbox = gtk_vbox_new(FALSE, 6);
2019 gtk_container_add(GTK_CONTAINER(frame), vbox);
2021 box = gtk_hbox_new(FALSE, 6);
2022 gtk_container_add(GTK_CONTAINER(vbox), box);
2024 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2026 for (i = 0; i < FD_DEBUG_MAX; i++) {
2028 box = gtk_hbox_new(FALSE, 6);
2029 gtk_container_add(GTK_CONTAINER(vbox), box);
2033 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2034 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2035 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2038 frame = gtk_frame_new("Graph font");
2039 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2040 vbox = gtk_vbox_new(FALSE, 6);
2041 gtk_container_add(GTK_CONTAINER(frame), vbox);
2043 font = gtk_font_button_new();
2044 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2046 gtk_widget_show_all(dialog);
2048 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2049 gtk_widget_destroy(dialog);
2053 for (i = 0; i < FD_DEBUG_MAX; i++) {
2056 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2058 fio_debug |= (1UL << i);
2061 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2062 gtk_widget_destroy(dialog);
2065 static void about_dialog(GtkWidget *w, gpointer data)
2067 const char *authors[] = {
2068 "Jens Axboe <axboe@kernel.dk>",
2069 "Stephen Carmeron <stephenmcameron@gmail.com>",
2072 const char *license[] = {
2073 "Fio is free software; you can redistribute it and/or modify "
2074 "it under the terms of the GNU General Public License as published by "
2075 "the Free Software Foundation; either version 2 of the License, or "
2076 "(at your option) any later version.\n",
2077 "Fio is distributed in the hope that it will be useful, "
2078 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2079 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2080 "GNU General Public License for more details.\n",
2081 "You should have received a copy of the GNU General Public License "
2082 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2083 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2085 char *license_trans;
2087 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2088 license[2], "\n", NULL);
2090 gtk_show_about_dialog(NULL,
2091 "program-name", "gfio",
2092 "comments", "Gtk2 UI for fio",
2093 "license", license_trans,
2094 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2096 "version", fio_version_string,
2097 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2098 "logo-icon-name", "fio",
2100 "wrap-license", TRUE,
2103 g_free(license_trans);
2106 static GtkActionEntry menu_items[] = {
2107 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2108 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2109 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2110 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2111 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2112 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2113 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2114 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2115 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2116 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2118 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2120 static const gchar *ui_string = " \
2122 <menubar name=\"MainMenu\"> \
2123 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2124 <menuitem name=\"New\" action=\"NewFile\" /> \
2125 <separator name=\"Separator1\"/> \
2126 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2127 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2128 <separator name=\"Separator2\"/> \
2129 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2130 <separator name=\"Separator3\"/> \
2131 <menuitem name=\"Quit\" action=\"Quit\" /> \
2133 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2134 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2136 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2137 <menuitem name=\"About\" action=\"About\" /> \
2143 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2146 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2149 action_group = gtk_action_group_new("Menu");
2150 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2152 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2153 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2155 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2156 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2159 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2160 GtkWidget *vbox, GtkUIManager *ui_manager)
2162 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2165 static GtkWidget *new_client_page(struct gui_entry *ge)
2167 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2170 main_vbox = gtk_vbox_new(FALSE, 3);
2172 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2173 ge->topvbox = gtk_vbox_new(FALSE, 3);
2174 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2175 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2177 probe = gtk_frame_new("Job");
2178 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2179 probe_frame = gtk_vbox_new(FALSE, 3);
2180 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2182 probe_box = gtk_hbox_new(FALSE, 3);
2183 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2184 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2185 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2186 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2187 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2189 probe_box = gtk_hbox_new(FALSE, 3);
2190 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2192 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2193 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2194 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2195 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2196 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2197 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2199 probe_box = gtk_hbox_new(FALSE, 3);
2200 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2201 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2202 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2203 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2204 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2207 * Only add this if we have a commit rate
2210 probe_box = gtk_hbox_new(FALSE, 3);
2211 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2213 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2214 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2216 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2217 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2221 * Set up a drawing area and IOPS and bandwidth graphs
2223 gdk_color_parse("white", &white);
2224 ge->graphs.drawing_area = gtk_drawing_area_new();
2225 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2226 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2227 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2228 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2229 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2230 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2231 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2232 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2233 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2234 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2235 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2236 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2237 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2238 ge->graphs.drawing_area);
2239 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2242 setup_graphs(&ge->graphs);
2245 * Set up alignments for widgets at the bottom of ui,
2246 * align bottom left, expand horizontally but not vertically
2248 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2249 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2250 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2251 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2254 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2257 * Set up thread status progress bar
2259 ge->thread_status_pb = gtk_progress_bar_new();
2260 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2261 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2262 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2268 static GtkWidget *new_main_page(struct gui *ui)
2270 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2273 main_vbox = gtk_vbox_new(FALSE, 3);
2276 * Set up alignments for widgets at the top of ui,
2277 * align top left, expand horizontally but not vertically
2279 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2280 ui->topvbox = gtk_vbox_new(FALSE, 0);
2281 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2282 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2284 probe = gtk_frame_new("Run statistics");
2285 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2286 probe_frame = gtk_vbox_new(FALSE, 3);
2287 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2289 probe_box = gtk_hbox_new(FALSE, 3);
2290 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2291 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2292 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2293 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2294 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2297 * Only add this if we have a commit rate
2300 probe_box = gtk_hbox_new(FALSE, 3);
2301 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2303 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2304 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2306 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2307 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2311 * Set up a drawing area and IOPS and bandwidth graphs
2313 gdk_color_parse("white", &white);
2314 ui->graphs.drawing_area = gtk_drawing_area_new();
2315 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2316 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2317 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2318 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2319 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2320 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2321 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2322 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2323 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2324 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2325 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2326 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2327 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2328 ui->graphs.drawing_area);
2329 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2332 setup_graphs(&ui->graphs);
2335 * Set up alignments for widgets at the bottom of ui,
2336 * align bottom left, expand horizontally but not vertically
2338 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2339 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2340 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2341 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2345 * Set up thread status progress bar
2347 ui->thread_status_pb = gtk_progress_bar_new();
2348 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2349 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2350 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2355 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2356 guint page, gpointer data)
2362 static void init_ui(int *argc, char **argv[], struct gui *ui)
2364 GtkSettings *settings;
2365 GtkUIManager *uimanager;
2366 GtkWidget *menu, *vbox;
2368 /* Magical g*thread incantation, you just need this thread stuff.
2369 * Without it, the update that happens in gfio_update_thread_status
2370 * doesn't really happen in a timely fashion, you need expose events
2372 if (!g_thread_supported())
2373 g_thread_init(NULL);
2376 gtk_init(argc, argv);
2377 settings = gtk_settings_get_default();
2378 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2381 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2382 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2383 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2385 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2386 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2388 ui->vbox = gtk_vbox_new(FALSE, 0);
2389 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2391 uimanager = gtk_ui_manager_new();
2392 menu = get_menubar_menu(ui->window, uimanager, ui);
2393 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2395 ui->notebook = gtk_notebook_new();
2396 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2397 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2398 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2400 vbox = new_main_page(ui);
2402 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2404 gfio_ui_setup_log(ui);
2406 gtk_widget_show_all(ui->window);
2409 int main(int argc, char *argv[], char *envp[])
2411 if (initialize_fio(envp))
2413 if (fio_init_options())
2416 memset(&main_ui, 0, sizeof(main_ui));
2417 INIT_FLIST_HEAD(&main_ui.list);
2419 init_ui(&argc, &argv, &main_ui);
2421 gdk_threads_enter();
2423 gdk_threads_leave();