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_label_set_text(GTK_LABEL(ui->eta.name), "");
109 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), "");
110 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), "");
111 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), "");
112 gtk_label_set_text(GTK_LABEL(ui->eta.jobs), "");
113 gtk_label_set_text(GTK_LABEL(ui->eta.files), "");
114 gtk_label_set_text(GTK_LABEL(ui->eta.read_bw), "");
115 gtk_label_set_text(GTK_LABEL(ui->eta.read_iops), "");
116 gtk_label_set_text(GTK_LABEL(ui->eta.write_bw), "");
117 gtk_label_set_text(GTK_LABEL(ui->eta.write_iops), "");
120 static void gfio_set_connected(struct gui *ui, int connected)
123 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
125 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
128 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
129 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
133 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
136 GtkTextBuffer *buffer;
139 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
141 gtk_text_buffer_get_end_iter(buffer, &end);
142 gtk_text_buffer_insert(buffer, &end, buf, -1);
144 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
145 &end, 0.0, FALSE, 0.0,0.0);
147 fio_client_ops.text_op(client, cmd);
151 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
153 printf("gfio_disk_util_op called\n");
154 fio_client_ops.disk_util(client, cmd);
157 static void gfio_thread_status_op(struct fio_net_cmd *cmd)
159 printf("gfio_thread_status_op called\n");
160 fio_client_ops.thread_status(cmd);
163 static void gfio_group_stats_op(struct fio_net_cmd *cmd)
165 printf("gfio_group_stats_op called\n");
166 fio_client_ops.group_stats(cmd);
169 static void gfio_update_eta(struct jobs_eta *je)
181 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
182 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
183 eta_to_str(eta_str, je->eta_sec);
186 sprintf(tmp, "%u", je->nr_running);
187 gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp);
188 sprintf(tmp, "%u", je->files_open);
189 gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp);
192 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
193 if (je->m_rate || je->t_rate) {
196 mr = num2str(je->m_rate, 4, 0, i2p);
197 tr = num2str(je->t_rate, 4, 0, i2p);
198 gtk_label_set_text(GTK_LABEL(ui.eta.
199 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
202 } else if (je->m_iops || je->t_iops)
203 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
205 gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---");
206 gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---");
207 gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---");
208 gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---");
211 if (je->eta_sec != INT_MAX && je->nr_running) {
215 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
216 strcpy(output, "-.-% done");
220 sprintf(output, "%3.1f%% done", perc);
223 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
224 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
226 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
227 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
229 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]);
230 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]);
231 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]);
232 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]);
241 char *dst = output + strlen(output);
243 sprintf(dst, " - %s", eta_str);
246 gfio_update_thread_status(output, perc);
249 static void gfio_eta_op(struct fio_client *client, struct fio_net_cmd *cmd)
251 struct jobs_eta *je = (struct jobs_eta *) cmd->payload;
252 struct client_eta *eta = (struct client_eta *) (uintptr_t) cmd->tag;
254 client->eta_in_flight = NULL;
255 flist_del_init(&client->eta_list);
257 fio_client_sum_jobs_eta(&eta->eta, je);
258 fio_client_dec_jobs_eta(eta, gfio_update_eta);
261 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
263 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
264 const char *os, *arch;
267 os = fio_get_os_string(probe->os);
271 arch = fio_get_arch_string(probe->arch);
276 client->name = strdup((char *) probe->hostname);
278 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
279 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
280 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
281 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
282 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
285 static void gfio_update_thread_status(char *status_message, double perc)
287 static char message[100];
288 const char *m = message;
290 strncpy(message, status_message, sizeof(message) - 1);
291 gtk_progress_bar_set_text(
292 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
293 gtk_progress_bar_set_fraction(
294 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
296 gtk_widget_queue_draw(ui.window);
300 static void gfio_quit_op(struct fio_client *client)
302 struct gui *ui = client->client_data;
304 gfio_set_connected(ui, 0);
307 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
309 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
310 struct gui *ui = client->client_data;
314 p->iodepth = le32_to_cpu(p->iodepth);
315 p->rw = le32_to_cpu(p->rw);
317 for (i = 0; i < 2; i++) {
318 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
319 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
322 p->numjobs = le32_to_cpu(p->numjobs);
323 p->group_reporting = le32_to_cpu(p->group_reporting);
325 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
326 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
327 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
329 sprintf(tmp, "%u", p->iodepth);
330 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
333 static void gfio_client_timed_out(struct fio_client *client)
335 struct gui *ui = client->client_data;
336 GtkWidget *dialog, *label, *content;
341 gfio_set_connected(ui, 0);
344 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
346 dialog = gtk_dialog_new_with_buttons("Timed out!",
347 GTK_WINDOW(ui->window),
348 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
349 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
351 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
352 label = gtk_label_new((const gchar *) buf);
353 gtk_container_add(GTK_CONTAINER(content), label);
354 gtk_widget_show_all(dialog);
355 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
357 gtk_dialog_run(GTK_DIALOG(dialog));
358 gtk_widget_destroy(dialog);
363 struct client_ops gfio_client_ops = {
364 .text_op = gfio_text_op,
365 .disk_util = gfio_disk_util_op,
366 .thread_status = gfio_thread_status_op,
367 .group_stats = gfio_group_stats_op,
369 .probe = gfio_probe_op,
370 .quit = gfio_quit_op,
371 .add_job = gfio_add_job_op,
372 .timed_out = gfio_client_timed_out,
376 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
377 __attribute__((unused)) gpointer data)
382 static void *job_thread(void *arg)
384 fio_handle_clients(&gfio_client_ops);
388 static int send_job_files(struct gui *ui)
392 for (i = 0; i < ui->nr_job_files; i++) {
393 ret = fio_clients_send_ini(ui->job_files[i]);
397 free(ui->job_files[i]);
398 ui->job_files[i] = NULL;
400 while (i < ui->nr_job_files) {
401 free(ui->job_files[i]);
402 ui->job_files[i] = NULL;
409 static void start_job_thread(struct gui *ui)
411 if (send_job_files(ui)) {
412 printf("Yeah, I didn't really like those options too much.\n");
413 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
418 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
420 GtkWidget *label_widget;
423 frame = gtk_frame_new(label);
424 label_widget = gtk_label_new(NULL);
425 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
426 gtk_container_add(GTK_CONTAINER(frame), label_widget);
431 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
433 GtkWidget *button, *box;
435 box = gtk_hbox_new(FALSE, 3);
436 gtk_container_add(GTK_CONTAINER(hbox), box);
438 button = gtk_spin_button_new_with_range(min, max, 1.0);
439 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
441 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
442 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
447 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
450 struct gui *ui = data;
452 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
453 start_job_thread(ui);
456 static void file_open(GtkWidget *w, gpointer data);
458 static void connect_clicked(GtkWidget *widget, gpointer data)
460 struct gui *ui = data;
462 if (!ui->connected) {
463 if (!ui->nr_job_files)
464 file_open(widget, data);
465 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
466 fio_clients_connect();
467 pthread_create(&ui->t, NULL, job_thread, NULL);
468 gfio_set_connected(ui, 1);
470 fio_clients_terminate();
471 gfio_set_connected(ui, 0);
476 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
477 struct button_spec *buttonspec)
479 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
480 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
481 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
482 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
483 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
486 static void add_buttons(struct gui *ui,
487 struct button_spec *buttonlist,
492 for (i = 0; i < nbuttons; i++)
493 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
496 static void on_info_bar_response(GtkWidget *widget, gint response,
499 if (response == GTK_RESPONSE_OK) {
500 gtk_widget_destroy(widget);
501 ui.error_info_bar = NULL;
505 void report_error(GError *error)
507 if (ui.error_info_bar == NULL) {
508 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
511 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
512 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
515 ui.error_label = gtk_label_new(error->message);
516 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
517 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
519 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
520 gtk_widget_show_all(ui.vbox);
523 snprintf(buffer, sizeof(buffer), "Failed to open file.");
524 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
528 static int get_connection_details(char **host, int *port, int *type,
531 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
535 dialog = gtk_dialog_new_with_buttons("Connection details",
536 GTK_WINDOW(ui.window),
537 GTK_DIALOG_DESTROY_WITH_PARENT,
538 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
539 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
541 frame = gtk_frame_new("Hostname / socket name");
542 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
543 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
545 box = gtk_vbox_new(FALSE, 6);
546 gtk_container_add(GTK_CONTAINER(frame), box);
548 hbox = gtk_hbox_new(TRUE, 10);
549 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
550 hentry = gtk_entry_new();
551 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
552 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
554 frame = gtk_frame_new("Port");
555 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
556 box = gtk_vbox_new(FALSE, 10);
557 gtk_container_add(GTK_CONTAINER(frame), box);
559 hbox = gtk_hbox_new(TRUE, 4);
560 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
561 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
563 frame = gtk_frame_new("Type");
564 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
565 box = gtk_vbox_new(FALSE, 10);
566 gtk_container_add(GTK_CONTAINER(frame), box);
568 hbox = gtk_hbox_new(TRUE, 4);
569 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
571 combo = gtk_combo_box_text_new();
572 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
573 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
574 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
575 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
577 gtk_container_add(GTK_CONTAINER(hbox), combo);
579 frame = gtk_frame_new("Options");
580 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
581 box = gtk_vbox_new(FALSE, 10);
582 gtk_container_add(GTK_CONTAINER(frame), box);
584 hbox = gtk_hbox_new(TRUE, 4);
585 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
587 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
588 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
589 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.");
590 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
592 gtk_widget_show_all(dialog);
594 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
595 gtk_widget_destroy(dialog);
599 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
600 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
602 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
603 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
604 *type = Fio_client_ipv4;
605 else if (!strncmp(typeentry, "IPv6", 4))
606 *type = Fio_client_ipv6;
608 *type = Fio_client_socket;
611 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
613 gtk_widget_destroy(dialog);
617 static void file_open(GtkWidget *w, gpointer data)
620 GSList *filenames, *fn_glist;
621 GtkFileFilter *filter;
623 int port, type, server_start;
625 dialog = gtk_file_chooser_dialog_new("Open File",
626 GTK_WINDOW(ui.window),
627 GTK_FILE_CHOOSER_ACTION_OPEN,
628 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
629 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
631 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
633 filter = gtk_file_filter_new();
634 gtk_file_filter_add_pattern(filter, "*.fio");
635 gtk_file_filter_add_pattern(filter, "*.job");
636 gtk_file_filter_add_mime_type(filter, "text/fio");
637 gtk_file_filter_set_name(filter, "Fio job file");
638 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
640 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
641 gtk_widget_destroy(dialog);
645 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
647 gtk_widget_destroy(dialog);
649 if (get_connection_details(&host, &port, &type, &server_start))
652 filenames = fn_glist;
653 while (filenames != NULL) {
654 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
655 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
658 ui.client = fio_client_add_explicit(host, type, port);
662 error = g_error_new(g_quark_from_string("fio"), 1,
663 "Failed to add client %s", host);
667 ui.client->client_data = &ui;
669 g_free(filenames->data);
670 filenames = g_slist_next(filenames);
674 g_slist_free(fn_glist);
677 static void file_save(GtkWidget *w, gpointer data)
681 dialog = gtk_file_chooser_dialog_new("Save File",
682 GTK_WINDOW(ui.window),
683 GTK_FILE_CHOOSER_ACTION_SAVE,
684 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
685 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
688 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
689 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
691 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
694 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
695 // save_job_file(filename);
698 gtk_widget_destroy(dialog);
701 static void preferences(GtkWidget *w, gpointer data)
703 GtkWidget *dialog, *frame, *box, **buttons;
706 dialog = gtk_dialog_new_with_buttons("Preferences",
707 GTK_WINDOW(ui.window),
708 GTK_DIALOG_DESTROY_WITH_PARENT,
709 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
710 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
713 frame = gtk_frame_new("Debug logging");
714 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
715 box = gtk_hbox_new(FALSE, 6);
716 gtk_container_add(GTK_CONTAINER(frame), box);
718 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
720 for (i = 0; i < FD_DEBUG_MAX; i++) {
721 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
722 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
723 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
726 gtk_widget_show_all(dialog);
728 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
729 gtk_widget_destroy(dialog);
733 for (i = 0; i < FD_DEBUG_MAX; i++) {
736 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
738 fio_debug |= (1UL << i);
741 gtk_widget_destroy(dialog);
744 static void about_dialog(GtkWidget *w, gpointer data)
746 gtk_show_about_dialog(NULL,
747 "program-name", "gfio",
748 "comments", "Gtk2 UI for fio",
750 "version", fio_version_string,
751 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
752 "logo-icon-name", "fio",
758 static GtkActionEntry menu_items[] = {
759 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
760 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
761 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
762 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
763 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
764 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
765 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
767 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
769 static const gchar *ui_string = " \
771 <menubar name=\"MainMenu\"> \
772 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
773 <menuitem name=\"Open\" action=\"OpenFile\" /> \
774 <menuitem name=\"Save\" action=\"SaveFile\" /> \
775 <separator name=\"Separator\"/> \
776 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
777 <separator name=\"Separator2\"/> \
778 <menuitem name=\"Quit\" action=\"Quit\" /> \
780 <menu name=\"Help\" action=\"HelpMenuAction\"> \
781 <menuitem name=\"About\" action=\"About\" /> \
787 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
789 GtkActionGroup *action_group = gtk_action_group_new("Menu");
792 action_group = gtk_action_group_new("Menu");
793 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
795 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
796 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
798 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
799 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
802 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
803 GtkWidget *vbox, GtkUIManager *ui_manager)
805 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
808 static void init_ui(int *argc, char **argv[], struct gui *ui)
810 GtkSettings *settings;
811 GtkUIManager *uimanager;
812 GtkWidget *menu, *probe, *probe_frame, *probe_box;
814 memset(ui, 0, sizeof(*ui));
816 /* Magical g*thread incantation, you just need this thread stuff.
817 * Without it, the update that happens in gfio_update_thread_status
818 * doesn't really happen in a timely fashion, you need expose events
820 if (!g_thread_supported())
824 gtk_init(argc, argv);
825 settings = gtk_settings_get_default();
826 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
829 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
830 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
831 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
833 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
834 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
836 ui->vbox = gtk_vbox_new(FALSE, 0);
837 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
839 uimanager = gtk_ui_manager_new();
840 menu = get_menubar_menu(ui->window, uimanager);
841 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
844 * Set up alignments for widgets at the top of ui,
845 * align top left, expand horizontally but not vertically
847 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
848 ui->topvbox = gtk_vbox_new(FALSE, 3);
849 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
850 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
852 probe = gtk_frame_new("Job");
853 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
854 probe_frame = gtk_vbox_new(FALSE, 3);
855 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
857 probe_box = gtk_hbox_new(FALSE, 3);
858 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
859 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
860 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
861 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
862 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
864 probe_box = gtk_hbox_new(FALSE, 3);
865 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
867 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
868 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
869 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
870 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
871 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
872 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
874 probe_box = gtk_hbox_new(FALSE, 3);
875 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
876 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
877 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
878 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
879 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
882 * Only add this if we have a commit rate
885 probe_box = gtk_hbox_new(FALSE, 3);
886 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
888 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
889 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
891 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
892 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
896 * Add a text box for text op messages
898 ui->textview = gtk_text_view_new();
899 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
900 gtk_text_buffer_set_text(ui->text, "", -1);
901 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
902 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
903 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
904 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
905 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
906 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
907 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
911 * Set up alignments for widgets at the bottom of ui,
912 * align bottom left, expand horizontally but not vertically
914 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
915 ui->buttonbox = gtk_hbox_new(FALSE, 0);
916 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
917 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
920 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
923 * Set up thread status progress bar
925 ui->thread_status_pb = gtk_progress_bar_new();
926 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
927 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
928 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
931 gtk_widget_show_all(ui->window);
934 int main(int argc, char *argv[], char *envp[])
936 if (initialize_fio(envp))
938 if (fio_init_options())
941 init_ui(&argc, &argv, &ui);