2 * gfio - gui front end for fio - the flexible io tester
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
6 * The license below covers all files distributed with fio unless otherwise
7 * noted in the file itself.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 static void gfio_update_thread_status(char *status_message, double perc);
33 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
35 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
37 static void connect_clicked(GtkWidget *widget, gpointer data);
38 static void start_job_clicked(GtkWidget *widget, gpointer data);
40 static struct button_spec {
41 const char *buttontext;
43 const char *tooltiptext;
44 const int start_insensitive;
45 } buttonspeclist[] = {
46 #define CONNECT_BUTTON 0
47 #define START_JOB_BUTTON 1
48 { "Connect", connect_clicked, "Connect to host", 0 },
51 "Send current fio job to fio server to be executed", 1 },
73 GtkWidget *write_iops;
83 GtkWidget *bottomalign;
84 GtkWidget *thread_status_pb;
86 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
87 GtkWidget *scrolled_window;
89 GtkWidget *error_info_bar;
90 GtkWidget *error_label;
92 struct probe_widget probe;
93 struct eta_widget eta;
97 struct fio_client *client;
102 static void clear_ui_info(struct gui *ui)
104 gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
105 gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
106 gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
107 gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
108 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
109 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
110 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
111 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
112 gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
113 gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
114 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
115 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
116 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
117 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
120 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
122 GtkWidget *entry, *frame;
124 frame = gtk_frame_new(label);
125 entry = gtk_entry_new();
126 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
127 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
128 gtk_container_add(GTK_CONTAINER(frame), entry);
133 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
135 GtkWidget *label_widget;
138 frame = gtk_frame_new(label);
139 label_widget = gtk_label_new(NULL);
140 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
141 gtk_container_add(GTK_CONTAINER(frame), label_widget);
146 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
148 GtkWidget *button, *box;
150 box = gtk_hbox_new(FALSE, 3);
151 gtk_container_add(GTK_CONTAINER(hbox), box);
153 button = gtk_spin_button_new_with_range(min, max, 1.0);
154 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
156 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
157 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
162 static void gfio_set_connected(struct gui *ui, int connected)
165 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
167 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
170 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
171 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
175 static void label_set_int_value(GtkWidget *entry, unsigned int val)
179 sprintf(tmp, "%u", val);
180 gtk_label_set_text(GTK_LABEL(entry), tmp);
183 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
187 sprintf(tmp, "%u", val);
188 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
192 #define ALIGN_RIGHT 2
196 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
198 GtkCellRenderer *renderer;
199 GtkTreeViewColumn *col;
200 double xalign = 0.0; /* left as default */
201 PangoAlignment align;
204 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
205 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
207 visible = !(flags & INVISIBLE);
209 renderer = gtk_cell_renderer_text_new();
210 col = gtk_tree_view_column_new();
212 gtk_tree_view_column_set_title(col, title);
213 if (!(flags & UNSORTABLE))
214 gtk_tree_view_column_set_sort_column_id(col, index);
215 gtk_tree_view_column_set_resizable(col, TRUE);
216 gtk_tree_view_column_pack_start(col, renderer, TRUE);
217 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
218 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
220 case PANGO_ALIGN_LEFT:
223 case PANGO_ALIGN_CENTER:
226 case PANGO_ALIGN_RIGHT:
230 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
231 gtk_tree_view_column_set_visible(col, visible);
232 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
236 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
242 GType types[FIO_IO_U_LIST_MAX_LEN];
243 GtkWidget *tree_view;
244 GtkTreeSelection *selection;
249 for (i = 0; i < len; i++)
250 types[i] = G_TYPE_INT;
252 model = gtk_list_store_newv(len, types);
254 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
255 gtk_widget_set_can_focus(tree_view, FALSE);
257 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
258 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
260 for (i = 0; i < len; i++) {
263 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
264 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
267 gtk_list_store_append(model, &iter);
269 for (i = 0; i < len; i++)
270 gtk_list_store_set(model, &iter, i, ovals[i], -1);
275 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
278 unsigned int *io_u_plat = ts->io_u_plat[ddir];
279 unsigned long nr = ts->clat_stat[ddir].samples;
280 fio_fp64_t *plist = ts->percentile_list;
281 unsigned int *ovals, len, minv, maxv, scale_down;
283 GtkWidget *tree_view, *frame, *hbox;
286 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
291 * We default to usecs, but if the value range is such that we
292 * should scale down to msecs, do that.
294 if (minv > 2000 && maxv > 99999) {
302 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
304 sprintf(tmp, "Completion percentiles (%s)", base);
305 frame = gtk_frame_new(tmp);
306 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
308 hbox = gtk_hbox_new(FALSE, 3);
309 gtk_container_add(GTK_CONTAINER(frame), hbox);
311 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
317 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
318 unsigned long max, double mean, double dev)
320 const char *base = "(usec)";
321 GtkWidget *hbox, *label, *frame;
325 if (!usec_to_msec(&min, &max, &mean, &dev))
328 minp = num2str(min, 6, 1, 0);
329 maxp = num2str(max, 6, 1, 0);
331 sprintf(tmp, "%s %s", name, base);
332 frame = gtk_frame_new(tmp);
333 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
335 hbox = gtk_hbox_new(FALSE, 3);
336 gtk_container_add(GTK_CONTAINER(frame), hbox);
338 label = new_info_label_in_frame(hbox, "Minimum");
339 gtk_label_set_text(GTK_LABEL(label), minp);
340 label = new_info_label_in_frame(hbox, "Maximum");
341 gtk_label_set_text(GTK_LABEL(label), maxp);
342 label = new_info_label_in_frame(hbox, "Average");
343 sprintf(tmp, "%5.02f", mean);
344 gtk_label_set_text(GTK_LABEL(label), tmp);
345 label = new_info_label_in_frame(hbox, "Standard deviation");
346 sprintf(tmp, "%5.02f", dev);
347 gtk_label_set_text(GTK_LABEL(label), tmp);
358 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
359 struct thread_stat *ts, int ddir)
361 const char *ddir_label[2] = { "Read", "Write" };
362 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
363 unsigned long min, max, runt;
364 unsigned long long bw, iops;
365 unsigned int flags = 0;
367 char *io_p, *bw_p, *iops_p;
370 if (!ts->runtime[ddir])
373 i2p = is_power_of_2(rs->kb_base);
374 runt = ts->runtime[ddir];
376 bw = (1000 * ts->io_bytes[ddir]) / runt;
377 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
378 bw_p = num2str(bw, 6, 1, i2p);
380 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
381 iops_p = num2str(iops, 6, 1, 0);
383 box = gtk_hbox_new(FALSE, 3);
384 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
386 frame = gtk_frame_new(ddir_label[ddir]);
387 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
389 main_vbox = gtk_vbox_new(FALSE, 3);
390 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
392 box = gtk_hbox_new(FALSE, 3);
393 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
395 label = new_info_label_in_frame(box, "IO");
396 gtk_label_set_text(GTK_LABEL(label), io_p);
397 label = new_info_label_in_frame(box, "Bandwidth");
398 gtk_label_set_text(GTK_LABEL(label), bw_p);
399 label = new_info_label_in_frame(box, "IOPS");
400 gtk_label_set_text(GTK_LABEL(label), iops_p);
401 label = new_info_label_in_frame(box, "Runtime (msec)");
402 label_set_int_value(label, ts->runtime[ddir]);
404 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
406 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
408 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
412 frame = gtk_frame_new("Latency");
413 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
415 vbox = gtk_vbox_new(FALSE, 3);
416 gtk_container_add(GTK_CONTAINER(frame), vbox);
418 if (flags & GFIO_SLAT)
419 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
420 if (flags & GFIO_CLAT)
421 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
422 if (flags & GFIO_LAT)
423 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
426 if (ts->clat_percentiles)
427 gfio_show_clat_percentiles(main_vbox, ts, ddir);
429 if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
430 double p_of_agg = 100.0;
431 const char *bw_str = "KB";
435 p_of_agg = mean * 100 / (double) rs->agg[ddir];
436 if (p_of_agg > 100.0)
440 if (mean > 999999.9) {
448 sprintf(tmp, "Bandwidth (%s)", bw_str);
449 frame = gtk_frame_new(tmp);
450 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
452 box = gtk_hbox_new(FALSE, 3);
453 gtk_container_add(GTK_CONTAINER(frame), box);
455 label = new_info_label_in_frame(box, "Minimum");
456 label_set_int_value(label, min);
457 label = new_info_label_in_frame(box, "Maximum");
458 label_set_int_value(label, max);
459 label = new_info_label_in_frame(box, "Percentage of jobs");
460 sprintf(tmp, "%3.2f%%", p_of_agg);
461 gtk_label_set_text(GTK_LABEL(label), tmp);
462 label = new_info_label_in_frame(box, "Average");
463 sprintf(tmp, "%5.02f", mean);
464 gtk_label_set_text(GTK_LABEL(label), tmp);
465 label = new_info_label_in_frame(box, "Standard deviation");
466 sprintf(tmp, "%5.02f", dev);
467 gtk_label_set_text(GTK_LABEL(label), tmp);
475 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
478 GtkWidget *tree_view;
479 GtkTreeSelection *selection;
486 * Check if all are empty, in which case don't bother
488 for (i = 0, skipped = 0; i < num; i++)
495 types = malloc(num * sizeof(GType));
497 for (i = 0; i < num; i++)
498 types[i] = G_TYPE_STRING;
500 model = gtk_list_store_newv(num, types);
504 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
505 gtk_widget_set_can_focus(tree_view, FALSE);
507 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
508 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
510 for (i = 0; i < num; i++)
511 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
513 gtk_list_store_append(model, &iter);
515 for (i = 0; i < num; i++) {
519 sprintf(fbuf, "0.00");
521 sprintf(fbuf, "%3.2f%%", lat[i]);
523 gtk_list_store_set(model, &iter, i, fbuf, -1);
529 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
531 GtkWidget *box, *frame, *tree_view;
532 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
533 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
534 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
535 "250", "500", "750", "1000", };
536 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
537 "250", "500", "750", "1000", "2000",
540 stat_calc_lat_u(ts, io_u_lat_u);
541 stat_calc_lat_m(ts, io_u_lat_m);
543 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
545 frame = gtk_frame_new("Latency buckets (usec)");
546 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
548 box = gtk_hbox_new(FALSE, 3);
549 gtk_container_add(GTK_CONTAINER(frame), box);
550 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
553 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
555 frame = gtk_frame_new("Latency buckets (msec)");
556 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
558 box = gtk_hbox_new(FALSE, 3);
559 gtk_container_add(GTK_CONTAINER(frame), box);
560 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
564 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
566 GtkWidget *box, *frame, *entry;
567 double usr_cpu, sys_cpu;
568 unsigned long runtime;
571 runtime = ts->total_run_time;
573 double runt = (double) runtime;
575 usr_cpu = (double) ts->usr_time * 100 / runt;
576 sys_cpu = (double) ts->sys_time * 100 / runt;
582 frame = gtk_frame_new("OS resources");
583 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
585 box = gtk_hbox_new(FALSE, 3);
586 gtk_container_add(GTK_CONTAINER(frame), box);
588 entry = new_info_entry_in_frame(box, "User CPU");
589 sprintf(tmp, "%3.2f%%", usr_cpu);
590 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
591 entry = new_info_entry_in_frame(box, "System CPU");
592 sprintf(tmp, "%3.2f%%", sys_cpu);
593 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
594 entry = new_info_entry_in_frame(box, "Context switches");
595 entry_set_int_value(entry, ts->ctx);
596 entry = new_info_entry_in_frame(box, "Major faults");
597 entry_set_int_value(entry, ts->majf);
598 entry = new_info_entry_in_frame(box, "Minor faults");
599 entry_set_int_value(entry, ts->minf);
602 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
604 double io_u_dist[FIO_IO_U_MAP_NR];
605 double io_u_dist_s[FIO_IO_U_MAP_NR];
606 double io_u_dist_c[FIO_IO_U_MAP_NR];
607 GtkWidget *frame, *box, *tree_view;
608 GtkTreeSelection *selection;
611 GType types[FIO_IO_U_MAP_NR + 1];
613 const char *labels[] = { "Type", "0", "4", "8", "16", "32", "64", ">= 64" };
615 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
616 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist_s);
617 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist_c);
619 frame = gtk_frame_new("IO depths");
620 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
622 box = gtk_hbox_new(FALSE, 3);
623 gtk_container_add(GTK_CONTAINER(frame), box);
625 for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++)
626 types[i] = G_TYPE_STRING;
628 model = gtk_list_store_newv(FIO_IO_U_MAP_NR + 1, types);
630 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
631 gtk_widget_set_can_focus(tree_view, FALSE);
633 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
634 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
636 for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++)
637 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
639 gtk_list_store_append(model, &iter);
641 for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++) {
645 gtk_list_store_set(model, &iter, i, "Total", -1);
649 sprintf(fbuf, "%3.1f%%", io_u_dist[i - 1]);
650 gtk_list_store_set(model, &iter, i, fbuf, -1);
653 gtk_list_store_append(model, &iter);
655 for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++) {
659 gtk_list_store_set(model, &iter, i, "Submit", -1);
663 sprintf(fbuf, "%3.1f%%", io_u_dist_s[i - 1]);
664 gtk_list_store_set(model, &iter, i, fbuf, -1);
667 gtk_list_store_append(model, &iter);
669 for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++) {
673 gtk_list_store_set(model, &iter, i, "Complete", -1);
677 sprintf(fbuf, "%3.1f%%", io_u_dist_c[i - 1]);
678 gtk_list_store_set(model, &iter, i, fbuf, -1);
681 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
684 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
685 struct group_run_stats *rs)
687 GtkWidget *dialog, *box, *vbox, *entry, *content;
688 struct gui *ui = client->client_data;
692 dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
693 GTK_DIALOG_DESTROY_WITH_PARENT,
694 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
696 g_signal_connect_swapped(dialog, "response",
697 G_CALLBACK(gtk_widget_destroy),
700 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
702 vbox = gtk_vbox_new(FALSE, 3);
703 gtk_container_add(GTK_CONTAINER(content), vbox);
705 box = gtk_hbox_new(TRUE, 3);
706 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
708 entry = new_info_entry_in_frame(box, "Name");
709 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
710 if (strlen(ts->description)) {
711 entry = new_info_entry_in_frame(box, "Description");
712 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
714 entry = new_info_entry_in_frame(box, "Group ID");
715 entry_set_int_value(entry, ts->groupid);
716 entry = new_info_entry_in_frame(box, "Jobs");
717 entry_set_int_value(entry, ts->members);
718 entry = new_info_entry_in_frame(box, "Error");
719 entry_set_int_value(entry, ts->error);
720 entry = new_info_entry_in_frame(box, "PID");
721 entry_set_int_value(entry, ts->pid);
723 if (ts->io_bytes[DDIR_READ])
724 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
725 if (ts->io_bytes[DDIR_WRITE])
726 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
728 gfio_show_latency_buckets(vbox, ts);
729 gfio_show_cpu_usage(vbox, ts);
730 gfio_show_io_depths(vbox, ts);
732 gtk_widget_show_all(dialog);
736 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
739 GtkTextBuffer *buffer;
742 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
744 gtk_text_buffer_get_end_iter(buffer, &end);
745 gtk_text_buffer_insert(buffer, &end, buf, -1);
747 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
748 &end, 0.0, FALSE, 0.0,0.0);
750 fio_client_ops.text_op(client, cmd);
754 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
756 printf("gfio_disk_util_op called\n");
757 fio_client_ops.disk_util(client, cmd);
760 extern int sum_stat_clients;
761 extern struct thread_stat client_ts;
762 extern struct group_run_stats client_gs;
764 static int sum_stat_nr;
766 static void gfio_thread_status_op(struct fio_client *client,
767 struct fio_net_cmd *cmd)
769 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
771 gfio_display_ts(client, &p->ts, &p->rs);
773 if (sum_stat_clients == 1)
776 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
777 sum_group_stats(&client_gs, &p->rs);
780 client_ts.groupid = p->ts.groupid;
782 if (++sum_stat_nr == sum_stat_clients) {
783 strcpy(client_ts.name, "All clients");
784 gfio_display_ts(client, &client_ts, &client_gs);
788 static void gfio_group_stats_op(struct fio_client *client,
789 struct fio_net_cmd *cmd)
791 printf("gfio_group_stats_op called\n");
792 fio_client_ops.group_stats(client, cmd);
795 static void gfio_update_eta(struct jobs_eta *je)
807 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
808 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
809 eta_to_str(eta_str, je->eta_sec);
812 sprintf(tmp, "%u", je->nr_running);
813 gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
814 sprintf(tmp, "%u", je->files_open);
815 gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
818 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
819 if (je->m_rate || je->t_rate) {
822 mr = num2str(je->m_rate, 4, 0, i2p);
823 tr = num2str(je->t_rate, 4, 0, i2p);
824 gtk_entry_set_text(GTK_ENTRY(ui.eta);
825 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
828 } else if (je->m_iops || je->t_iops)
829 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
831 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
832 gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
833 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
834 gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
837 if (je->eta_sec != INT_MAX && je->nr_running) {
841 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
842 strcpy(output, "-.-% done");
846 sprintf(output, "%3.1f%% done", perc);
849 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
850 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
852 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
853 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
855 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
856 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
857 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
858 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
867 char *dst = output + strlen(output);
869 sprintf(dst, " - %s", eta_str);
872 gfio_update_thread_status(output, perc);
875 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
877 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
878 const char *os, *arch;
881 os = fio_get_os_string(probe->os);
885 arch = fio_get_arch_string(probe->arch);
890 client->name = strdup((char *) probe->hostname);
892 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
893 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
894 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
895 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
896 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
899 static void gfio_update_thread_status(char *status_message, double perc)
901 static char message[100];
902 const char *m = message;
904 strncpy(message, status_message, sizeof(message) - 1);
905 gtk_progress_bar_set_text(
906 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
907 gtk_progress_bar_set_fraction(
908 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
910 gtk_widget_queue_draw(ui.window);
914 static void gfio_quit_op(struct fio_client *client)
916 struct gui *ui = client->client_data;
918 gfio_set_connected(ui, 0);
921 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
923 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
924 struct gui *ui = client->client_data;
928 p->iodepth = le32_to_cpu(p->iodepth);
929 p->rw = le32_to_cpu(p->rw);
931 for (i = 0; i < 2; i++) {
932 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
933 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
936 p->numjobs = le32_to_cpu(p->numjobs);
937 p->group_reporting = le32_to_cpu(p->group_reporting);
939 gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
940 gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
941 gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
943 sprintf(tmp, "%u", p->iodepth);
944 gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
947 static void gfio_client_timed_out(struct fio_client *client)
949 struct gui *ui = client->client_data;
950 GtkWidget *dialog, *label, *content;
955 gfio_set_connected(ui, 0);
958 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
960 dialog = gtk_dialog_new_with_buttons("Timed out!",
961 GTK_WINDOW(ui->window),
962 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
963 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
965 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
966 label = gtk_label_new((const gchar *) buf);
967 gtk_container_add(GTK_CONTAINER(content), label);
968 gtk_widget_show_all(dialog);
969 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
971 gtk_dialog_run(GTK_DIALOG(dialog));
972 gtk_widget_destroy(dialog);
977 struct client_ops gfio_client_ops = {
978 .text_op = gfio_text_op,
979 .disk_util = gfio_disk_util_op,
980 .thread_status = gfio_thread_status_op,
981 .group_stats = gfio_group_stats_op,
982 .eta = gfio_update_eta,
983 .probe = gfio_probe_op,
984 .quit = gfio_quit_op,
985 .add_job = gfio_add_job_op,
986 .timed_out = gfio_client_timed_out,
990 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
991 __attribute__((unused)) gpointer data)
996 static void *job_thread(void *arg)
998 fio_handle_clients(&gfio_client_ops);
1002 static int send_job_files(struct gui *ui)
1006 for (i = 0; i < ui->nr_job_files; i++) {
1007 ret = fio_clients_send_ini(ui->job_files[i]);
1011 free(ui->job_files[i]);
1012 ui->job_files[i] = NULL;
1014 while (i < ui->nr_job_files) {
1015 free(ui->job_files[i]);
1016 ui->job_files[i] = NULL;
1023 static void start_job_thread(struct gui *ui)
1025 if (send_job_files(ui)) {
1026 printf("Yeah, I didn't really like those options too much.\n");
1027 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1032 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1035 struct gui *ui = data;
1037 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1038 start_job_thread(ui);
1041 static void file_open(GtkWidget *w, gpointer data);
1043 static void connect_clicked(GtkWidget *widget, gpointer data)
1045 struct gui *ui = data;
1047 if (!ui->connected) {
1048 if (!ui->nr_job_files)
1049 file_open(widget, data);
1050 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1051 fio_clients_connect();
1052 pthread_create(&ui->t, NULL, job_thread, NULL);
1053 gfio_set_connected(ui, 1);
1055 fio_clients_terminate();
1056 gfio_set_connected(ui, 0);
1061 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1062 struct button_spec *buttonspec)
1064 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1065 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1066 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1067 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1068 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1071 static void add_buttons(struct gui *ui,
1072 struct button_spec *buttonlist,
1077 for (i = 0; i < nbuttons; i++)
1078 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1081 static void on_info_bar_response(GtkWidget *widget, gint response,
1084 if (response == GTK_RESPONSE_OK) {
1085 gtk_widget_destroy(widget);
1086 ui.error_info_bar = NULL;
1090 void report_error(GError *error)
1092 if (ui.error_info_bar == NULL) {
1093 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1096 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1097 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1100 ui.error_label = gtk_label_new(error->message);
1101 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1102 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1104 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1105 gtk_widget_show_all(ui.vbox);
1108 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1109 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1113 static int get_connection_details(char **host, int *port, int *type,
1116 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1120 dialog = gtk_dialog_new_with_buttons("Connection details",
1121 GTK_WINDOW(ui.window),
1122 GTK_DIALOG_DESTROY_WITH_PARENT,
1123 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1124 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1126 frame = gtk_frame_new("Hostname / socket name");
1127 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1128 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1130 box = gtk_vbox_new(FALSE, 6);
1131 gtk_container_add(GTK_CONTAINER(frame), box);
1133 hbox = gtk_hbox_new(TRUE, 10);
1134 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1135 hentry = gtk_entry_new();
1136 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1137 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1139 frame = gtk_frame_new("Port");
1140 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1141 box = gtk_vbox_new(FALSE, 10);
1142 gtk_container_add(GTK_CONTAINER(frame), box);
1144 hbox = gtk_hbox_new(TRUE, 4);
1145 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1146 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1148 frame = gtk_frame_new("Type");
1149 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1150 box = gtk_vbox_new(FALSE, 10);
1151 gtk_container_add(GTK_CONTAINER(frame), box);
1153 hbox = gtk_hbox_new(TRUE, 4);
1154 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1156 combo = gtk_combo_box_text_new();
1157 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1158 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1159 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1160 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1162 gtk_container_add(GTK_CONTAINER(hbox), combo);
1164 frame = gtk_frame_new("Options");
1165 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1166 box = gtk_vbox_new(FALSE, 10);
1167 gtk_container_add(GTK_CONTAINER(frame), box);
1169 hbox = gtk_hbox_new(TRUE, 4);
1170 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1172 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1174 gtk_widget_set_tooltip_text(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.");
1175 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1177 gtk_widget_show_all(dialog);
1179 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1180 gtk_widget_destroy(dialog);
1184 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1185 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1187 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1188 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1189 *type = Fio_client_ipv4;
1190 else if (!strncmp(typeentry, "IPv6", 4))
1191 *type = Fio_client_ipv6;
1193 *type = Fio_client_socket;
1196 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1198 gtk_widget_destroy(dialog);
1202 static void file_open(GtkWidget *w, gpointer data)
1205 GSList *filenames, *fn_glist;
1206 GtkFileFilter *filter;
1208 int port, type, server_start;
1210 dialog = gtk_file_chooser_dialog_new("Open File",
1211 GTK_WINDOW(ui.window),
1212 GTK_FILE_CHOOSER_ACTION_OPEN,
1213 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1214 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1216 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1218 filter = gtk_file_filter_new();
1219 gtk_file_filter_add_pattern(filter, "*.fio");
1220 gtk_file_filter_add_pattern(filter, "*.job");
1221 gtk_file_filter_add_mime_type(filter, "text/fio");
1222 gtk_file_filter_set_name(filter, "Fio job file");
1223 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1225 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1226 gtk_widget_destroy(dialog);
1230 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1232 gtk_widget_destroy(dialog);
1234 if (get_connection_details(&host, &port, &type, &server_start))
1237 filenames = fn_glist;
1238 while (filenames != NULL) {
1239 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1240 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1243 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1247 error = g_error_new(g_quark_from_string("fio"), 1,
1248 "Failed to add client %s", host);
1249 report_error(error);
1250 g_error_free(error);
1252 ui.client->client_data = &ui;
1254 g_free(filenames->data);
1255 filenames = g_slist_next(filenames);
1259 g_slist_free(fn_glist);
1262 static void file_save(GtkWidget *w, gpointer data)
1266 dialog = gtk_file_chooser_dialog_new("Save File",
1267 GTK_WINDOW(ui.window),
1268 GTK_FILE_CHOOSER_ACTION_SAVE,
1269 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1270 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1273 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1274 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1276 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1279 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1280 // save_job_file(filename);
1283 gtk_widget_destroy(dialog);
1286 static void preferences(GtkWidget *w, gpointer data)
1288 GtkWidget *dialog, *frame, *box, **buttons;
1291 dialog = gtk_dialog_new_with_buttons("Preferences",
1292 GTK_WINDOW(ui.window),
1293 GTK_DIALOG_DESTROY_WITH_PARENT,
1294 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1295 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1298 frame = gtk_frame_new("Debug logging");
1299 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1300 box = gtk_hbox_new(FALSE, 6);
1301 gtk_container_add(GTK_CONTAINER(frame), box);
1303 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1305 for (i = 0; i < FD_DEBUG_MAX; i++) {
1306 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1307 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1308 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1311 gtk_widget_show_all(dialog);
1313 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1314 gtk_widget_destroy(dialog);
1318 for (i = 0; i < FD_DEBUG_MAX; i++) {
1321 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1323 fio_debug |= (1UL << i);
1326 gtk_widget_destroy(dialog);
1329 static void about_dialog(GtkWidget *w, gpointer data)
1331 gtk_show_about_dialog(NULL,
1332 "program-name", "gfio",
1333 "comments", "Gtk2 UI for fio",
1335 "version", fio_version_string,
1336 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1337 "logo-icon-name", "fio",
1343 static GtkActionEntry menu_items[] = {
1344 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1345 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1346 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1347 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1348 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1349 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1350 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
1352 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1354 static const gchar *ui_string = " \
1356 <menubar name=\"MainMenu\"> \
1357 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1358 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1359 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1360 <separator name=\"Separator\"/> \
1361 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1362 <separator name=\"Separator2\"/> \
1363 <menuitem name=\"Quit\" action=\"Quit\" /> \
1365 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1366 <menuitem name=\"About\" action=\"About\" /> \
1372 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1374 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1377 action_group = gtk_action_group_new("Menu");
1378 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1380 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1381 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1383 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1384 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1387 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1388 GtkWidget *vbox, GtkUIManager *ui_manager)
1390 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1393 static void init_ui(int *argc, char **argv[], struct gui *ui)
1395 GtkSettings *settings;
1396 GtkUIManager *uimanager;
1397 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1399 memset(ui, 0, sizeof(*ui));
1401 /* Magical g*thread incantation, you just need this thread stuff.
1402 * Without it, the update that happens in gfio_update_thread_status
1403 * doesn't really happen in a timely fashion, you need expose events
1405 if (!g_thread_supported())
1406 g_thread_init(NULL);
1409 gtk_init(argc, argv);
1410 settings = gtk_settings_get_default();
1411 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1414 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1415 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1416 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1418 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1419 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1421 ui->vbox = gtk_vbox_new(FALSE, 0);
1422 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1424 uimanager = gtk_ui_manager_new();
1425 menu = get_menubar_menu(ui->window, uimanager);
1426 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1429 * Set up alignments for widgets at the top of ui,
1430 * align top left, expand horizontally but not vertically
1432 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1433 ui->topvbox = gtk_vbox_new(FALSE, 3);
1434 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1435 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1437 probe = gtk_frame_new("Job");
1438 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1439 probe_frame = gtk_vbox_new(FALSE, 3);
1440 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1442 probe_box = gtk_hbox_new(FALSE, 3);
1443 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1444 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1445 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1446 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1447 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1449 probe_box = gtk_hbox_new(FALSE, 3);
1450 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1452 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1453 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1454 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1455 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1456 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1457 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1459 probe_box = gtk_hbox_new(FALSE, 3);
1460 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1461 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1462 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1463 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1464 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1467 * Only add this if we have a commit rate
1470 probe_box = gtk_hbox_new(FALSE, 3);
1471 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1473 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1474 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1476 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1477 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1481 * Add a text box for text op messages
1483 ui->textview = gtk_text_view_new();
1484 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1485 gtk_text_buffer_set_text(ui->text, "", -1);
1486 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1487 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1488 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1489 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1490 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1491 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1492 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1496 * Set up alignments for widgets at the bottom of ui,
1497 * align bottom left, expand horizontally but not vertically
1499 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1500 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1501 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1502 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1505 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1508 * Set up thread status progress bar
1510 ui->thread_status_pb = gtk_progress_bar_new();
1511 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1512 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1513 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1516 gtk_widget_show_all(ui->window);
1519 int main(int argc, char *argv[], char *envp[])
1521 if (initialize_fio(envp))
1523 if (fio_init_options())
1526 init_ui(&argc, &argv, &ui);
1528 gdk_threads_enter();
1530 gdk_threads_leave();