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_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
880 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
881 gtk_container_add(GTK_CONTAINER(win), notebook);
883 ge->results_window = win;
884 ge->results_notebook = notebook;
885 return ge->results_notebook;
888 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
889 struct group_run_stats *rs)
891 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
892 struct gfio_client *gc = client->client_data;
896 res_win = get_results_window(gc->ge);
898 scroll = gtk_scrolled_window_new(NULL, NULL);
899 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
900 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
902 vbox = gtk_vbox_new(FALSE, 3);
904 box = gtk_hbox_new(FALSE, 0);
905 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
907 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
909 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
911 gc->results_widget = vbox;
913 entry = new_info_entry_in_frame(box, "Name");
914 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
915 if (strlen(ts->description)) {
916 entry = new_info_entry_in_frame(box, "Description");
917 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
919 entry = new_info_entry_in_frame(box, "Group ID");
920 entry_set_int_value(entry, ts->groupid);
921 entry = new_info_entry_in_frame(box, "Jobs");
922 entry_set_int_value(entry, ts->members);
923 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
924 entry_set_int_value(entry, ts->error);
925 entry = new_info_entry_in_frame(box, "PID");
926 entry_set_int_value(entry, ts->pid);
928 if (ts->io_bytes[DDIR_READ])
929 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
930 if (ts->io_bytes[DDIR_WRITE])
931 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
933 gfio_show_latency_buckets(vbox, ts);
934 gfio_show_cpu_usage(vbox, ts);
935 gfio_show_io_depths(vbox, ts);
937 gtk_widget_show_all(gc->ge->results_window);
941 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
943 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
944 struct gui *ui = &main_ui;
948 char tmp[64], timebuf[80];
951 tm = localtime(&sec);
952 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
953 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
957 gtk_list_store_append(ui->log_model, &iter);
958 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
959 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
960 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
961 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
963 if (p->level == FIO_LOG_ERR)
964 view_log(NULL, (gpointer) ui);
969 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
971 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
972 struct gfio_client *gc = client->client_data;
973 GtkWidget *box, *frame, *entry, *vbox;
979 if (!gc->results_widget)
982 if (!gc->disk_util_frame) {
983 gc->disk_util_frame = gtk_frame_new("Disk utilization");
984 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
987 vbox = gtk_vbox_new(FALSE, 3);
988 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
990 frame = gtk_frame_new((char *) p->dus.name);
991 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
993 box = gtk_vbox_new(FALSE, 3);
994 gtk_container_add(GTK_CONTAINER(frame), box);
996 frame = gtk_frame_new("Read");
997 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
998 vbox = gtk_hbox_new(TRUE, 3);
999 gtk_container_add(GTK_CONTAINER(frame), vbox);
1000 entry = new_info_entry_in_frame(vbox, "IOs");
1001 entry_set_int_value(entry, p->dus.ios[0]);
1002 entry = new_info_entry_in_frame(vbox, "Merges");
1003 entry_set_int_value(entry, p->dus.merges[0]);
1004 entry = new_info_entry_in_frame(vbox, "Sectors");
1005 entry_set_int_value(entry, p->dus.sectors[0]);
1006 entry = new_info_entry_in_frame(vbox, "Ticks");
1007 entry_set_int_value(entry, p->dus.ticks[0]);
1009 frame = gtk_frame_new("Write");
1010 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1011 vbox = gtk_hbox_new(TRUE, 3);
1012 gtk_container_add(GTK_CONTAINER(frame), vbox);
1013 entry = new_info_entry_in_frame(vbox, "IOs");
1014 entry_set_int_value(entry, p->dus.ios[1]);
1015 entry = new_info_entry_in_frame(vbox, "Merges");
1016 entry_set_int_value(entry, p->dus.merges[1]);
1017 entry = new_info_entry_in_frame(vbox, "Sectors");
1018 entry_set_int_value(entry, p->dus.sectors[1]);
1019 entry = new_info_entry_in_frame(vbox, "Ticks");
1020 entry_set_int_value(entry, p->dus.ticks[1]);
1022 frame = gtk_frame_new("Shared");
1023 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1024 vbox = gtk_hbox_new(TRUE, 3);
1025 gtk_container_add(GTK_CONTAINER(frame), vbox);
1026 entry = new_info_entry_in_frame(vbox, "IO ticks");
1027 entry_set_int_value(entry, p->dus.io_ticks);
1028 entry = new_info_entry_in_frame(vbox, "Time in queue");
1029 entry_set_int_value(entry, p->dus.time_in_queue);
1033 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1037 sprintf(tmp, "%3.2f%%", util);
1038 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1039 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1041 gtk_widget_show_all(gc->results_widget);
1043 gdk_threads_leave();
1046 extern int sum_stat_clients;
1047 extern struct thread_stat client_ts;
1048 extern struct group_run_stats client_gs;
1050 static int sum_stat_nr;
1052 static void gfio_thread_status_op(struct fio_client *client,
1053 struct fio_net_cmd *cmd)
1055 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1057 gfio_display_ts(client, &p->ts, &p->rs);
1059 if (sum_stat_clients == 1)
1062 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1063 sum_group_stats(&client_gs, &p->rs);
1065 client_ts.members++;
1066 client_ts.groupid = p->ts.groupid;
1068 if (++sum_stat_nr == sum_stat_clients) {
1069 strcpy(client_ts.name, "All clients");
1070 gfio_display_ts(client, &client_ts, &client_gs);
1074 static void gfio_group_stats_op(struct fio_client *client,
1075 struct fio_net_cmd *cmd)
1077 /* We're ignoring group stats for now */
1080 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1083 struct gfio_graphs *g = data;
1085 g->drawing_area_xdim = w->allocation.width;
1086 g->drawing_area_ydim = w->allocation.height;
1090 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1092 struct gfio_graphs *g = p;
1095 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1096 g->drawing_area_ydim);
1097 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1098 g->drawing_area_ydim);
1099 cr = gdk_cairo_create(w->window);
1101 cairo_set_source_rgb(cr, 0, 0, 0);
1104 cairo_translate(cr, 0, 0);
1105 line_graph_draw(g->bandwidth_graph, cr);
1110 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1111 line_graph_draw(g->iops_graph, cr);
1120 * Client specific ETA
1122 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1124 struct gfio_client *gc = client->client_data;
1125 struct gui_entry *ge = gc->ge;
1126 static int eta_good;
1133 gdk_threads_enter();
1138 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1139 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1140 eta_to_str(eta_str, je->eta_sec);
1143 sprintf(tmp, "%u", je->nr_running);
1144 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1145 sprintf(tmp, "%u", je->files_open);
1146 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1149 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1150 if (je->m_rate || je->t_rate) {
1153 mr = num2str(je->m_rate, 4, 0, i2p);
1154 tr = num2str(je->t_rate, 4, 0, i2p);
1155 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1156 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1159 } else if (je->m_iops || je->t_iops)
1160 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1162 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1163 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1164 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1165 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1168 if (je->eta_sec != INT_MAX && je->nr_running) {
1172 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1173 strcpy(output, "-.-% done");
1177 sprintf(output, "%3.1f%% done", perc);
1180 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1181 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1183 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1184 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1186 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1187 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1188 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1189 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1191 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1192 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1193 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1194 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1203 char *dst = output + strlen(output);
1205 sprintf(dst, " - %s", eta_str);
1208 gfio_update_thread_status(ge, output, perc);
1209 gdk_threads_leave();
1213 * Update ETA in main window for all clients
1215 static void gfio_update_all_eta(struct jobs_eta *je)
1217 struct gui *ui = &main_ui;
1218 static int eta_good;
1224 gdk_threads_enter();
1229 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1230 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1231 eta_to_str(eta_str, je->eta_sec);
1235 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1236 if (je->m_rate || je->t_rate) {
1239 mr = num2str(je->m_rate, 4, 0, i2p);
1240 tr = num2str(je->t_rate, 4, 0, i2p);
1241 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1242 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1245 } else if (je->m_iops || je->t_iops)
1246 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1248 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1249 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1250 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1251 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1254 if (je->eta_sec != INT_MAX && je->nr_running) {
1258 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1259 strcpy(output, "-.-% done");
1263 sprintf(output, "%3.1f%% done", perc);
1266 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1267 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1269 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1270 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1272 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1273 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1274 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1275 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1277 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1278 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1279 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1280 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1289 char *dst = output + strlen(output);
1291 sprintf(dst, " - %s", eta_str);
1294 gfio_update_thread_status_all(output, perc);
1295 gdk_threads_leave();
1298 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1300 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1301 struct gfio_client *gc = client->client_data;
1302 struct gui_entry *ge = gc->ge;
1303 const char *os, *arch;
1306 os = fio_get_os_string(probe->os);
1310 arch = fio_get_arch_string(probe->arch);
1315 client->name = strdup((char *) probe->hostname);
1317 gdk_threads_enter();
1319 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1320 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1321 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1322 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1323 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1325 gfio_set_connected(ge, 1);
1327 gdk_threads_leave();
1330 static void gfio_update_thread_status(struct gui_entry *ge,
1331 char *status_message, double perc)
1333 static char message[100];
1334 const char *m = message;
1336 strncpy(message, status_message, sizeof(message) - 1);
1337 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1338 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1339 gtk_widget_queue_draw(main_ui.window);
1342 static void gfio_update_thread_status_all(char *status_message, double perc)
1344 struct gui *ui = &main_ui;
1345 static char message[100];
1346 const char *m = message;
1348 strncpy(message, status_message, sizeof(message) - 1);
1349 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1350 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1351 gtk_widget_queue_draw(ui->window);
1354 static void gfio_quit_op(struct fio_client *client)
1356 struct gfio_client *gc = client->client_data;
1358 gdk_threads_enter();
1359 gfio_set_connected(gc->ge, 0);
1360 gdk_threads_leave();
1363 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1365 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1366 struct gfio_client *gc = client->client_data;
1367 struct thread_options *o = &gc->o;
1368 struct gui_entry *ge = gc->ge;
1371 convert_thread_options_to_cpu(o, &p->top);
1373 gdk_threads_enter();
1375 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1377 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1378 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1379 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1381 sprintf(tmp, "%u", o->iodepth);
1382 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1386 gdk_threads_leave();
1389 static void gfio_client_timed_out(struct fio_client *client)
1391 struct gfio_client *gc = client->client_data;
1392 GtkWidget *dialog, *label, *content;
1395 gdk_threads_enter();
1397 gfio_set_connected(gc->ge, 0);
1398 clear_ge_ui_info(gc->ge);
1400 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1402 dialog = gtk_dialog_new_with_buttons("Timed out!",
1403 GTK_WINDOW(main_ui.window),
1404 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1405 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1407 /* gtk_dialog_get_content_area() is 2.14 and newer */
1408 content = GTK_DIALOG(dialog)->vbox;
1410 label = gtk_label_new((const gchar *) buf);
1411 gtk_container_add(GTK_CONTAINER(content), label);
1412 gtk_widget_show_all(dialog);
1413 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1415 gtk_dialog_run(GTK_DIALOG(dialog));
1416 gtk_widget_destroy(dialog);
1418 gdk_threads_leave();
1421 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1423 struct gfio_client *gc = client->client_data;
1425 gdk_threads_enter();
1427 gfio_set_connected(gc->ge, 0);
1430 entry_set_int_value(gc->err_entry, client->error);
1432 gdk_threads_leave();
1435 struct client_ops gfio_client_ops = {
1436 .text_op = gfio_text_op,
1437 .disk_util = gfio_disk_util_op,
1438 .thread_status = gfio_thread_status_op,
1439 .group_stats = gfio_group_stats_op,
1440 .jobs_eta = gfio_update_client_eta,
1441 .eta = gfio_update_all_eta,
1442 .probe = gfio_probe_op,
1443 .quit = gfio_quit_op,
1444 .add_job = gfio_add_job_op,
1445 .timed_out = gfio_client_timed_out,
1446 .stop = gfio_client_stop,
1447 .stay_connected = 1,
1450 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1451 __attribute__((unused)) gpointer data)
1456 static void *job_thread(void *arg)
1458 struct gui *ui = arg;
1460 ui->handler_running = 1;
1461 fio_handle_clients(&gfio_client_ops);
1462 ui->handler_running = 0;
1466 static int send_job_files(struct gui_entry *ge)
1468 struct gfio_client *gc = ge->client;
1471 for (i = 0; i < ge->nr_job_files; i++) {
1472 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1476 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1477 report_error(error);
1478 g_error_free(error);
1483 free(ge->job_files[i]);
1484 ge->job_files[i] = NULL;
1486 while (i < ge->nr_job_files) {
1487 free(ge->job_files[i]);
1488 ge->job_files[i] = NULL;
1495 static void *server_thread(void *arg)
1498 gfio_server_running = 1;
1499 fio_start_server(NULL);
1500 gfio_server_running = 0;
1504 static void gfio_start_server(void)
1506 struct gui *ui = &main_ui;
1508 if (!gfio_server_running) {
1509 gfio_server_running = 1;
1510 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1511 pthread_detach(ui->server_t);
1515 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1518 struct gui_entry *ge = data;
1519 struct gfio_client *gc = ge->client;
1521 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1522 fio_start_client(gc->client);
1525 static void file_open(GtkWidget *w, gpointer data);
1527 static void connect_clicked(GtkWidget *widget, gpointer data)
1529 struct gui_entry *ge = data;
1530 struct gfio_client *gc = ge->client;
1532 if (!ge->connected) {
1535 if (!ge->nr_job_files)
1536 file_open(widget, data);
1537 if (!ge->nr_job_files)
1540 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1541 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1542 ret = fio_client_connect(gc->client);
1544 if (!ge->ui->handler_running)
1545 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1546 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1547 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1551 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1552 report_error(error);
1553 g_error_free(error);
1556 fio_client_terminate(gc->client);
1557 gfio_set_connected(ge, 0);
1558 clear_ge_ui_info(ge);
1562 static void send_clicked(GtkWidget *widget, gpointer data)
1564 struct gui_entry *ge = data;
1566 if (send_job_files(ge)) {
1569 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);
1570 report_error(error);
1571 g_error_free(error);
1573 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1576 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1577 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1580 static GtkWidget *add_button(GtkWidget *buttonbox,
1581 struct button_spec *buttonspec, gpointer data)
1583 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1585 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1586 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1587 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1588 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1593 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1598 for (i = 0; i < nbuttons; i++)
1599 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1602 static void on_info_bar_response(GtkWidget *widget, gint response,
1605 struct gui *ui = &main_ui;
1607 if (response == GTK_RESPONSE_OK) {
1608 gtk_widget_destroy(widget);
1609 ui->error_info_bar = NULL;
1613 void report_error(GError *error)
1615 struct gui *ui = &main_ui;
1617 if (ui->error_info_bar == NULL) {
1618 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1621 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1622 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1625 ui->error_label = gtk_label_new(error->message);
1626 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1627 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1629 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1630 gtk_widget_show_all(ui->vbox);
1633 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1634 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1638 struct connection_widgets
1645 static void hostname_cb(GtkEntry *entry, gpointer data)
1647 struct connection_widgets *cw = data;
1648 int uses_net = 0, is_localhost = 0;
1653 * Check whether to display the 'auto start backend' box
1654 * or not. Show it if we are a localhost and using network,
1655 * or using a socket.
1657 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1658 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1663 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1664 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1665 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1666 !strcmp(text, "ip6-loopback"))
1670 if (!uses_net || is_localhost) {
1671 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1672 gtk_widget_set_sensitive(cw->button, 1);
1674 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1675 gtk_widget_set_sensitive(cw->button, 0);
1679 static int get_connection_details(char **host, int *port, int *type,
1682 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1683 struct connection_widgets cw;
1686 dialog = gtk_dialog_new_with_buttons("Connection details",
1687 GTK_WINDOW(main_ui.window),
1688 GTK_DIALOG_DESTROY_WITH_PARENT,
1689 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1690 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1692 frame = gtk_frame_new("Hostname / socket name");
1693 /* gtk_dialog_get_content_area() is 2.14 and newer */
1694 vbox = GTK_DIALOG(dialog)->vbox;
1695 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1697 box = gtk_vbox_new(FALSE, 6);
1698 gtk_container_add(GTK_CONTAINER(frame), box);
1700 hbox = gtk_hbox_new(TRUE, 10);
1701 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1702 cw.hentry = gtk_entry_new();
1703 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1704 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1706 frame = gtk_frame_new("Port");
1707 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1708 box = gtk_vbox_new(FALSE, 10);
1709 gtk_container_add(GTK_CONTAINER(frame), box);
1711 hbox = gtk_hbox_new(TRUE, 4);
1712 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1713 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1715 frame = gtk_frame_new("Type");
1716 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1717 box = gtk_vbox_new(FALSE, 10);
1718 gtk_container_add(GTK_CONTAINER(frame), box);
1720 hbox = gtk_hbox_new(TRUE, 4);
1721 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1723 cw.combo = gtk_combo_box_new_text();
1724 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1725 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1726 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1727 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1729 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1731 frame = gtk_frame_new("Options");
1732 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1733 box = gtk_vbox_new(FALSE, 10);
1734 gtk_container_add(GTK_CONTAINER(frame), box);
1736 hbox = gtk_hbox_new(TRUE, 4);
1737 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1739 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1740 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1741 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.");
1742 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1745 * Connect edit signal, so we can show/not-show the auto start button
1747 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1748 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1750 gtk_widget_show_all(dialog);
1752 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1753 gtk_widget_destroy(dialog);
1757 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1758 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1760 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1761 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1762 *type = Fio_client_ipv4;
1763 else if (!strncmp(typeentry, "IPv6", 4))
1764 *type = Fio_client_ipv6;
1766 *type = Fio_client_socket;
1769 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1771 gtk_widget_destroy(dialog);
1775 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1777 struct gfio_client *gc;
1779 gc = malloc(sizeof(*gc));
1780 memset(gc, 0, sizeof(*gc));
1782 gc->client = fio_get_client(client);
1786 client->client_data = gc;
1789 static GtkWidget *new_client_page(struct gui_entry *ge);
1791 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1793 struct gui_entry *ge;
1795 ge = malloc(sizeof(*ge));
1796 memset(ge, 0, sizeof(*ge));
1797 INIT_FLIST_HEAD(&ge->list);
1798 flist_add_tail(&ge->list, &ui->list);
1804 * FIXME: need more handling here
1806 static void ge_destroy(GtkWidget *w, gpointer data)
1808 struct gui_entry *ge = data;
1809 struct gfio_client *gc = ge->client;
1812 fio_put_client(gc->client);
1814 flist_del(&ge->list);
1818 static struct gui_entry *get_new_ge_with_tab(const char *name)
1820 struct gui_entry *ge;
1822 ge = alloc_new_gui_entry(&main_ui);
1824 ge->vbox = new_client_page(ge);
1825 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1827 ge->page_label = gtk_label_new(name);
1828 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1830 gtk_widget_show_all(main_ui.window);
1834 static void file_new(GtkWidget *w, gpointer data)
1836 get_new_ge_with_tab("Untitled");
1840 * Return the 'ge' corresponding to the tab. If the active tab is the
1841 * main tab, open a new tab.
1843 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1845 struct flist_head *entry;
1846 struct gui_entry *ge;
1849 return get_new_ge_with_tab("Untitled");
1851 flist_for_each(entry, &main_ui.list) {
1852 ge = flist_entry(entry, struct gui_entry, list);
1853 if (ge->page_num == cur_page)
1860 static void file_open(GtkWidget *w, gpointer data)
1862 struct gui *ui = data;
1864 GSList *filenames, *fn_glist;
1865 GtkFileFilter *filter;
1867 int port, type, server_start;
1868 struct gui_entry *ge;
1872 * Creates new tab if current tab is the main window, or the
1873 * current tab already has a client.
1875 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1876 ge = get_ge_from_page(cur_page);
1878 ge = get_new_ge_with_tab("Untitled");
1880 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1882 dialog = gtk_file_chooser_dialog_new("Open File",
1883 GTK_WINDOW(ui->window),
1884 GTK_FILE_CHOOSER_ACTION_OPEN,
1885 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1886 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1888 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1890 filter = gtk_file_filter_new();
1891 gtk_file_filter_add_pattern(filter, "*.fio");
1892 gtk_file_filter_add_pattern(filter, "*.job");
1893 gtk_file_filter_add_pattern(filter, "*.ini");
1894 gtk_file_filter_add_mime_type(filter, "text/fio");
1895 gtk_file_filter_set_name(filter, "Fio job file");
1896 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1898 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1899 gtk_widget_destroy(dialog);
1903 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1905 gtk_widget_destroy(dialog);
1907 if (get_connection_details(&host, &port, &type, &server_start))
1910 filenames = fn_glist;
1911 while (filenames != NULL) {
1912 struct fio_client *client;
1914 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1915 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1918 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1922 error = g_error_new(g_quark_from_string("fio"), 1,
1923 "Failed to add client %s", host);
1924 report_error(error);
1925 g_error_free(error);
1927 gfio_client_added(ge, client);
1929 g_free(filenames->data);
1930 filenames = g_slist_next(filenames);
1935 gfio_start_server();
1937 g_slist_free(fn_glist);
1940 static void file_save(GtkWidget *w, gpointer data)
1942 struct gui *ui = data;
1945 dialog = gtk_file_chooser_dialog_new("Save File",
1946 GTK_WINDOW(ui->window),
1947 GTK_FILE_CHOOSER_ACTION_SAVE,
1948 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1949 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1952 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1953 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1955 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1958 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1959 // save_job_file(filename);
1962 gtk_widget_destroy(dialog);
1965 static void view_log_destroy(GtkWidget *w, gpointer data)
1967 struct gui *ui = (struct gui *) data;
1969 gtk_widget_ref(ui->log_tree);
1970 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1971 gtk_widget_destroy(w);
1972 ui->log_view = NULL;
1975 static void view_log(GtkWidget *w, gpointer data)
1977 GtkWidget *win, *scroll, *vbox, *box;
1978 struct gui *ui = (struct gui *) data;
1983 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1984 gtk_window_set_title(GTK_WINDOW(win), "Log");
1985 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1987 scroll = gtk_scrolled_window_new(NULL, NULL);
1989 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1991 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1993 box = gtk_hbox_new(TRUE, 0);
1994 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1995 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1996 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1998 vbox = gtk_vbox_new(TRUE, 5);
1999 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2001 gtk_container_add(GTK_CONTAINER(win), vbox);
2002 gtk_widget_show_all(win);
2005 static void preferences(GtkWidget *w, gpointer data)
2007 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2010 dialog = gtk_dialog_new_with_buttons("Preferences",
2011 GTK_WINDOW(main_ui.window),
2012 GTK_DIALOG_DESTROY_WITH_PARENT,
2013 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2014 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2017 frame = gtk_frame_new("Debug logging");
2018 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2020 vbox = gtk_vbox_new(FALSE, 6);
2021 gtk_container_add(GTK_CONTAINER(frame), vbox);
2023 box = gtk_hbox_new(FALSE, 6);
2024 gtk_container_add(GTK_CONTAINER(vbox), box);
2026 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2028 for (i = 0; i < FD_DEBUG_MAX; i++) {
2030 box = gtk_hbox_new(FALSE, 6);
2031 gtk_container_add(GTK_CONTAINER(vbox), box);
2035 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2036 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2037 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2040 frame = gtk_frame_new("Graph font");
2041 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2042 vbox = gtk_vbox_new(FALSE, 6);
2043 gtk_container_add(GTK_CONTAINER(frame), vbox);
2045 font = gtk_font_button_new();
2046 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2048 gtk_widget_show_all(dialog);
2050 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2051 gtk_widget_destroy(dialog);
2055 for (i = 0; i < FD_DEBUG_MAX; i++) {
2058 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2060 fio_debug |= (1UL << i);
2063 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2064 gtk_widget_destroy(dialog);
2067 static void about_dialog(GtkWidget *w, gpointer data)
2069 const char *authors[] = {
2070 "Jens Axboe <axboe@kernel.dk>",
2071 "Stephen Carmeron <stephenmcameron@gmail.com>",
2074 const char *license[] = {
2075 "Fio is free software; you can redistribute it and/or modify "
2076 "it under the terms of the GNU General Public License as published by "
2077 "the Free Software Foundation; either version 2 of the License, or "
2078 "(at your option) any later version.\n",
2079 "Fio is distributed in the hope that it will be useful, "
2080 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2081 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2082 "GNU General Public License for more details.\n",
2083 "You should have received a copy of the GNU General Public License "
2084 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2085 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2087 char *license_trans;
2089 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2090 license[2], "\n", NULL);
2092 gtk_show_about_dialog(NULL,
2093 "program-name", "gfio",
2094 "comments", "Gtk2 UI for fio",
2095 "license", license_trans,
2096 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2098 "version", fio_version_string,
2099 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2100 "logo-icon-name", "fio",
2102 "wrap-license", TRUE,
2105 g_free(license_trans);
2108 static GtkActionEntry menu_items[] = {
2109 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2110 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2111 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2112 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2113 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2114 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2115 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2116 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2117 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2118 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2120 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2122 static const gchar *ui_string = " \
2124 <menubar name=\"MainMenu\"> \
2125 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2126 <menuitem name=\"New\" action=\"NewFile\" /> \
2127 <separator name=\"Separator1\"/> \
2128 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2129 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2130 <separator name=\"Separator2\"/> \
2131 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2132 <separator name=\"Separator3\"/> \
2133 <menuitem name=\"Quit\" action=\"Quit\" /> \
2135 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2136 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2138 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2139 <menuitem name=\"About\" action=\"About\" /> \
2145 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2148 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2151 action_group = gtk_action_group_new("Menu");
2152 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2154 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2155 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2157 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2158 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2161 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2162 GtkWidget *vbox, GtkUIManager *ui_manager)
2164 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2167 static GtkWidget *new_client_page(struct gui_entry *ge)
2169 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2172 main_vbox = gtk_vbox_new(FALSE, 3);
2174 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2175 ge->topvbox = gtk_vbox_new(FALSE, 3);
2176 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2177 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2179 probe = gtk_frame_new("Job");
2180 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2181 probe_frame = gtk_vbox_new(FALSE, 3);
2182 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2184 probe_box = gtk_hbox_new(FALSE, 3);
2185 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2186 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2187 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2188 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2189 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2191 probe_box = gtk_hbox_new(FALSE, 3);
2192 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2194 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2195 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2196 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2197 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2198 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2199 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2201 probe_box = gtk_hbox_new(FALSE, 3);
2202 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2203 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2204 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2205 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2206 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2209 * Only add this if we have a commit rate
2212 probe_box = gtk_hbox_new(FALSE, 3);
2213 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2215 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2216 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2218 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2219 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2223 * Set up a drawing area and IOPS and bandwidth graphs
2225 gdk_color_parse("white", &white);
2226 ge->graphs.drawing_area = gtk_drawing_area_new();
2227 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2228 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2229 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2230 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2231 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2232 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2233 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2234 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2235 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2236 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2237 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2238 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2239 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2240 ge->graphs.drawing_area);
2241 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2244 setup_graphs(&ge->graphs);
2247 * Set up alignments for widgets at the bottom of ui,
2248 * align bottom left, expand horizontally but not vertically
2250 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2251 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2252 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2253 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2256 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2259 * Set up thread status progress bar
2261 ge->thread_status_pb = gtk_progress_bar_new();
2262 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2263 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2264 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2270 static GtkWidget *new_main_page(struct gui *ui)
2272 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2275 main_vbox = gtk_vbox_new(FALSE, 3);
2278 * Set up alignments for widgets at the top of ui,
2279 * align top left, expand horizontally but not vertically
2281 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2282 ui->topvbox = gtk_vbox_new(FALSE, 0);
2283 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2284 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2286 probe = gtk_frame_new("Run statistics");
2287 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2288 probe_frame = gtk_vbox_new(FALSE, 3);
2289 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2291 probe_box = gtk_hbox_new(FALSE, 3);
2292 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2293 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2294 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2295 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2296 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2299 * Only add this if we have a commit rate
2302 probe_box = gtk_hbox_new(FALSE, 3);
2303 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2305 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2306 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2308 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2309 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2313 * Set up a drawing area and IOPS and bandwidth graphs
2315 gdk_color_parse("white", &white);
2316 ui->graphs.drawing_area = gtk_drawing_area_new();
2317 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2318 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2319 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2320 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2321 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2322 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2323 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2324 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2325 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2326 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2327 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2328 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2329 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2330 ui->graphs.drawing_area);
2331 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2334 setup_graphs(&ui->graphs);
2337 * Set up alignments for widgets at the bottom of ui,
2338 * align bottom left, expand horizontally but not vertically
2340 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2341 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2342 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2343 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2347 * Set up thread status progress bar
2349 ui->thread_status_pb = gtk_progress_bar_new();
2350 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2351 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2352 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2357 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2358 guint page, gpointer data)
2364 static void init_ui(int *argc, char **argv[], struct gui *ui)
2366 GtkSettings *settings;
2367 GtkUIManager *uimanager;
2368 GtkWidget *menu, *vbox;
2370 /* Magical g*thread incantation, you just need this thread stuff.
2371 * Without it, the update that happens in gfio_update_thread_status
2372 * doesn't really happen in a timely fashion, you need expose events
2374 if (!g_thread_supported())
2375 g_thread_init(NULL);
2378 gtk_init(argc, argv);
2379 settings = gtk_settings_get_default();
2380 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2383 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2384 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2385 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2387 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2388 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2390 ui->vbox = gtk_vbox_new(FALSE, 0);
2391 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2393 uimanager = gtk_ui_manager_new();
2394 menu = get_menubar_menu(ui->window, uimanager, ui);
2395 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2397 ui->notebook = gtk_notebook_new();
2398 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2399 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2400 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2401 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2403 vbox = new_main_page(ui);
2405 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2407 gfio_ui_setup_log(ui);
2409 gtk_widget_show_all(ui->window);
2412 int main(int argc, char *argv[], char *envp[])
2414 if (initialize_fio(envp))
2416 if (fio_init_options())
2419 memset(&main_ui, 0, sizeof(main_ui));
2420 INIT_FLIST_HEAD(&main_ui.list);
2422 init_ui(&argc, &argv, &main_ui);
2424 gdk_threads_enter();
2426 gdk_threads_leave();