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 = 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;
1808 flist_del(&ge->list);
1812 static struct gui_entry *get_new_ge_with_tab(const char *name)
1814 struct gui_entry *ge;
1816 ge = alloc_new_gui_entry(&main_ui);
1818 ge->vbox = new_client_page(ge);
1819 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1821 ge->page_label = gtk_label_new(name);
1822 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1824 gtk_widget_show_all(main_ui.window);
1828 static void file_new(GtkWidget *w, gpointer data)
1830 get_new_ge_with_tab("Untitled");
1834 * Return the 'ge' corresponding to the tab. If the active tab is the
1835 * main tab, open a new tab.
1837 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1839 struct flist_head *entry;
1840 struct gui_entry *ge;
1843 return get_new_ge_with_tab("Untitled");
1845 flist_for_each(entry, &main_ui.list) {
1846 ge = flist_entry(entry, struct gui_entry, list);
1847 if (ge->page_num == cur_page)
1854 static void file_open(GtkWidget *w, gpointer data)
1856 struct gui *ui = data;
1858 GSList *filenames, *fn_glist;
1859 GtkFileFilter *filter;
1861 int port, type, server_start;
1862 struct gui_entry *ge;
1866 * Creates new tab if current tab is the main window, or the
1867 * current tab already has a client.
1869 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1870 ge = get_ge_from_page(cur_page);
1872 ge = get_new_ge_with_tab("Untitled");
1874 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1876 dialog = gtk_file_chooser_dialog_new("Open File",
1877 GTK_WINDOW(ui->window),
1878 GTK_FILE_CHOOSER_ACTION_OPEN,
1879 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1880 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1882 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1884 filter = gtk_file_filter_new();
1885 gtk_file_filter_add_pattern(filter, "*.fio");
1886 gtk_file_filter_add_pattern(filter, "*.job");
1887 gtk_file_filter_add_pattern(filter, "*.ini");
1888 gtk_file_filter_add_mime_type(filter, "text/fio");
1889 gtk_file_filter_set_name(filter, "Fio job file");
1890 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1892 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1893 gtk_widget_destroy(dialog);
1897 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1899 gtk_widget_destroy(dialog);
1901 if (get_connection_details(&host, &port, &type, &server_start))
1904 filenames = fn_glist;
1905 while (filenames != NULL) {
1906 struct fio_client *client;
1908 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1909 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1912 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1916 error = g_error_new(g_quark_from_string("fio"), 1,
1917 "Failed to add client %s", host);
1918 report_error(error);
1919 g_error_free(error);
1921 gfio_client_added(ge, client);
1923 g_free(filenames->data);
1924 filenames = g_slist_next(filenames);
1929 gfio_start_server();
1931 g_slist_free(fn_glist);
1934 static void file_save(GtkWidget *w, gpointer data)
1936 struct gui *ui = data;
1939 dialog = gtk_file_chooser_dialog_new("Save File",
1940 GTK_WINDOW(ui->window),
1941 GTK_FILE_CHOOSER_ACTION_SAVE,
1942 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1943 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1946 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1947 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1949 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1952 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1953 // save_job_file(filename);
1956 gtk_widget_destroy(dialog);
1959 static void view_log_destroy(GtkWidget *w, gpointer data)
1961 struct gui *ui = (struct gui *) data;
1963 gtk_widget_ref(ui->log_tree);
1964 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1965 gtk_widget_destroy(w);
1966 ui->log_view = NULL;
1969 static void view_log(GtkWidget *w, gpointer data)
1971 GtkWidget *win, *scroll, *vbox, *box;
1972 struct gui *ui = (struct gui *) data;
1977 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1978 gtk_window_set_title(GTK_WINDOW(win), "Log");
1979 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1981 scroll = gtk_scrolled_window_new(NULL, NULL);
1983 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1985 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1987 box = gtk_hbox_new(TRUE, 0);
1988 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1989 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1990 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1992 vbox = gtk_vbox_new(TRUE, 5);
1993 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1995 gtk_container_add(GTK_CONTAINER(win), vbox);
1996 gtk_widget_show_all(win);
1999 static void preferences(GtkWidget *w, gpointer data)
2001 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2004 dialog = gtk_dialog_new_with_buttons("Preferences",
2005 GTK_WINDOW(main_ui.window),
2006 GTK_DIALOG_DESTROY_WITH_PARENT,
2007 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2008 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2011 frame = gtk_frame_new("Debug logging");
2012 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2014 vbox = gtk_vbox_new(FALSE, 6);
2015 gtk_container_add(GTK_CONTAINER(frame), vbox);
2017 box = gtk_hbox_new(FALSE, 6);
2018 gtk_container_add(GTK_CONTAINER(vbox), box);
2020 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2022 for (i = 0; i < FD_DEBUG_MAX; i++) {
2024 box = gtk_hbox_new(FALSE, 6);
2025 gtk_container_add(GTK_CONTAINER(vbox), box);
2029 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2030 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2031 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2034 frame = gtk_frame_new("Graph font");
2035 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2036 vbox = gtk_vbox_new(FALSE, 6);
2037 gtk_container_add(GTK_CONTAINER(frame), vbox);
2039 font = gtk_font_button_new();
2040 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2042 gtk_widget_show_all(dialog);
2044 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2045 gtk_widget_destroy(dialog);
2049 for (i = 0; i < FD_DEBUG_MAX; i++) {
2052 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2054 fio_debug |= (1UL << i);
2057 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2058 gtk_widget_destroy(dialog);
2061 static void about_dialog(GtkWidget *w, gpointer data)
2063 const char *authors[] = {
2064 "Jens Axboe <axboe@kernel.dk>",
2065 "Stephen Carmeron <stephenmcameron@gmail.com>",
2068 const char *license[] = {
2069 "Fio is free software; you can redistribute it and/or modify "
2070 "it under the terms of the GNU General Public License as published by "
2071 "the Free Software Foundation; either version 2 of the License, or "
2072 "(at your option) any later version.\n",
2073 "Fio is distributed in the hope that it will be useful, "
2074 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2075 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2076 "GNU General Public License for more details.\n",
2077 "You should have received a copy of the GNU General Public License "
2078 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2079 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2081 char *license_trans;
2083 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2084 license[2], "\n", NULL);
2086 gtk_show_about_dialog(NULL,
2087 "program-name", "gfio",
2088 "comments", "Gtk2 UI for fio",
2089 "license", license_trans,
2090 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2092 "version", fio_version_string,
2093 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2094 "logo-icon-name", "fio",
2096 "wrap-license", TRUE,
2099 g_free(license_trans);
2102 static GtkActionEntry menu_items[] = {
2103 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2104 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2105 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2106 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2107 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2108 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2109 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2110 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2111 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2112 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2114 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2116 static const gchar *ui_string = " \
2118 <menubar name=\"MainMenu\"> \
2119 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2120 <menuitem name=\"New\" action=\"NewFile\" /> \
2121 <separator name=\"Separator1\"/> \
2122 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2123 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2124 <separator name=\"Separator2\"/> \
2125 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2126 <separator name=\"Separator3\"/> \
2127 <menuitem name=\"Quit\" action=\"Quit\" /> \
2129 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2130 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2132 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2133 <menuitem name=\"About\" action=\"About\" /> \
2139 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2142 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2145 action_group = gtk_action_group_new("Menu");
2146 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2148 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2149 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2151 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2152 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2155 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2156 GtkWidget *vbox, GtkUIManager *ui_manager)
2158 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2161 static GtkWidget *new_client_page(struct gui_entry *ge)
2163 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2166 main_vbox = gtk_vbox_new(FALSE, 3);
2168 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2169 ge->topvbox = gtk_vbox_new(FALSE, 3);
2170 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2171 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2173 probe = gtk_frame_new("Job");
2174 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2175 probe_frame = gtk_vbox_new(FALSE, 3);
2176 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2178 probe_box = gtk_hbox_new(FALSE, 3);
2179 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2180 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2181 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2182 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2183 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2185 probe_box = gtk_hbox_new(FALSE, 3);
2186 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2188 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2189 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2190 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2191 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2192 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2193 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2195 probe_box = gtk_hbox_new(FALSE, 3);
2196 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2197 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2198 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2199 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2200 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2203 * Only add this if we have a commit rate
2206 probe_box = gtk_hbox_new(FALSE, 3);
2207 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2209 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2210 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2212 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2213 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2217 * Set up a drawing area and IOPS and bandwidth graphs
2219 gdk_color_parse("white", &white);
2220 ge->graphs.drawing_area = gtk_drawing_area_new();
2221 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2222 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2223 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2224 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2225 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2226 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2227 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2228 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2229 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2230 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2231 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2232 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2233 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2234 ge->graphs.drawing_area);
2235 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2238 setup_graphs(&ge->graphs);
2241 * Set up alignments for widgets at the bottom of ui,
2242 * align bottom left, expand horizontally but not vertically
2244 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2245 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2246 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2247 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2250 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2253 * Set up thread status progress bar
2255 ge->thread_status_pb = gtk_progress_bar_new();
2256 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2257 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2258 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2264 static GtkWidget *new_main_page(struct gui *ui)
2266 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2269 main_vbox = gtk_vbox_new(FALSE, 3);
2272 * Set up alignments for widgets at the top of ui,
2273 * align top left, expand horizontally but not vertically
2275 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2276 ui->topvbox = gtk_vbox_new(FALSE, 0);
2277 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2278 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2280 probe = gtk_frame_new("Run statistics");
2281 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2282 probe_frame = gtk_vbox_new(FALSE, 3);
2283 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2285 probe_box = gtk_hbox_new(FALSE, 3);
2286 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2287 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2288 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2289 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2290 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2293 * Only add this if we have a commit rate
2296 probe_box = gtk_hbox_new(FALSE, 3);
2297 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2299 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2300 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2302 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2303 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2307 * Set up a drawing area and IOPS and bandwidth graphs
2309 gdk_color_parse("white", &white);
2310 ui->graphs.drawing_area = gtk_drawing_area_new();
2311 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2312 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2313 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2314 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2315 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2316 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2317 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2318 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2319 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2320 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2321 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2322 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2323 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2324 ui->graphs.drawing_area);
2325 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2328 setup_graphs(&ui->graphs);
2331 * Set up alignments for widgets at the bottom of ui,
2332 * align bottom left, expand horizontally but not vertically
2334 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2335 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2336 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2337 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2341 * Set up thread status progress bar
2343 ui->thread_status_pb = gtk_progress_bar_new();
2344 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2345 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2346 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2351 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2352 guint page, gpointer data)
2358 static void init_ui(int *argc, char **argv[], struct gui *ui)
2360 GtkSettings *settings;
2361 GtkUIManager *uimanager;
2362 GtkWidget *menu, *vbox;
2364 /* Magical g*thread incantation, you just need this thread stuff.
2365 * Without it, the update that happens in gfio_update_thread_status
2366 * doesn't really happen in a timely fashion, you need expose events
2368 if (!g_thread_supported())
2369 g_thread_init(NULL);
2372 gtk_init(argc, argv);
2373 settings = gtk_settings_get_default();
2374 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2377 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2378 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2379 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2381 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2382 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2384 ui->vbox = gtk_vbox_new(FALSE, 0);
2385 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2387 uimanager = gtk_ui_manager_new();
2388 menu = get_menubar_menu(ui->window, uimanager, ui);
2389 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2391 ui->notebook = gtk_notebook_new();
2392 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2393 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2395 vbox = new_main_page(ui);
2397 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2399 gfio_ui_setup_log(ui);
2401 gtk_widget_show_all(ui->window);
2404 int main(int argc, char *argv[], char *envp[])
2406 if (initialize_fio(envp))
2408 if (fio_init_options())
2411 memset(&main_ui, 0, sizeof(main_ui));
2412 INIT_FLIST_HEAD(&main_ui.list);
2414 init_ui(&argc, &argv, &main_ui);
2416 gdk_threads_enter();
2418 gdk_threads_leave();