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_client *client,
158 struct fio_net_cmd *cmd)
160 printf("gfio_thread_status_op called\n");
161 fio_client_ops.thread_status(client, cmd);
164 static void gfio_group_stats_op(struct fio_client *client,
165 struct fio_net_cmd *cmd)
167 printf("gfio_group_stats_op called\n");
168 fio_client_ops.group_stats(client, cmd);
171 static void gfio_update_eta(struct jobs_eta *je)
183 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
184 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
185 eta_to_str(eta_str, je->eta_sec);
188 sprintf(tmp, "%u", je->nr_running);
189 gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp);
190 sprintf(tmp, "%u", je->files_open);
191 gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp);
194 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
195 if (je->m_rate || je->t_rate) {
198 mr = num2str(je->m_rate, 4, 0, i2p);
199 tr = num2str(je->t_rate, 4, 0, i2p);
200 gtk_label_set_text(GTK_LABEL(ui.eta.
201 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
204 } else if (je->m_iops || je->t_iops)
205 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
207 gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---");
208 gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---");
209 gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---");
210 gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---");
213 if (je->eta_sec != INT_MAX && je->nr_running) {
217 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
218 strcpy(output, "-.-% done");
222 sprintf(output, "%3.1f%% done", perc);
225 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
226 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
228 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
229 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
231 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]);
232 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]);
233 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]);
234 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]);
243 char *dst = output + strlen(output);
245 sprintf(dst, " - %s", eta_str);
248 gfio_update_thread_status(output, perc);
251 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
253 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
254 const char *os, *arch;
257 os = fio_get_os_string(probe->os);
261 arch = fio_get_arch_string(probe->arch);
266 client->name = strdup((char *) probe->hostname);
268 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
269 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
270 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
271 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
272 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
275 static void gfio_update_thread_status(char *status_message, double perc)
277 static char message[100];
278 const char *m = message;
280 strncpy(message, status_message, sizeof(message) - 1);
281 gtk_progress_bar_set_text(
282 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
283 gtk_progress_bar_set_fraction(
284 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
286 gtk_widget_queue_draw(ui.window);
290 static void gfio_quit_op(struct fio_client *client)
292 struct gui *ui = client->client_data;
294 gfio_set_connected(ui, 0);
297 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
299 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
300 struct gui *ui = client->client_data;
304 p->iodepth = le32_to_cpu(p->iodepth);
305 p->rw = le32_to_cpu(p->rw);
307 for (i = 0; i < 2; i++) {
308 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
309 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
312 p->numjobs = le32_to_cpu(p->numjobs);
313 p->group_reporting = le32_to_cpu(p->group_reporting);
315 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
316 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
317 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
319 sprintf(tmp, "%u", p->iodepth);
320 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
323 static void gfio_client_timed_out(struct fio_client *client)
325 struct gui *ui = client->client_data;
326 GtkWidget *dialog, *label, *content;
331 gfio_set_connected(ui, 0);
334 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
336 dialog = gtk_dialog_new_with_buttons("Timed out!",
337 GTK_WINDOW(ui->window),
338 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
339 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
341 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
342 label = gtk_label_new((const gchar *) buf);
343 gtk_container_add(GTK_CONTAINER(content), label);
344 gtk_widget_show_all(dialog);
345 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
347 gtk_dialog_run(GTK_DIALOG(dialog));
348 gtk_widget_destroy(dialog);
353 struct client_ops gfio_client_ops = {
354 .text_op = gfio_text_op,
355 .disk_util = gfio_disk_util_op,
356 .thread_status = gfio_thread_status_op,
357 .group_stats = gfio_group_stats_op,
358 .eta = gfio_update_eta,
359 .probe = gfio_probe_op,
360 .quit = gfio_quit_op,
361 .add_job = gfio_add_job_op,
362 .timed_out = gfio_client_timed_out,
366 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
367 __attribute__((unused)) gpointer data)
372 static void *job_thread(void *arg)
374 fio_handle_clients(&gfio_client_ops);
378 static int send_job_files(struct gui *ui)
382 for (i = 0; i < ui->nr_job_files; i++) {
383 ret = fio_clients_send_ini(ui->job_files[i]);
387 free(ui->job_files[i]);
388 ui->job_files[i] = NULL;
390 while (i < ui->nr_job_files) {
391 free(ui->job_files[i]);
392 ui->job_files[i] = NULL;
399 static void start_job_thread(struct gui *ui)
401 if (send_job_files(ui)) {
402 printf("Yeah, I didn't really like those options too much.\n");
403 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
408 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
410 GtkWidget *label_widget;
413 frame = gtk_frame_new(label);
414 label_widget = gtk_label_new(NULL);
415 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
416 gtk_container_add(GTK_CONTAINER(frame), label_widget);
421 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
423 GtkWidget *button, *box;
425 box = gtk_hbox_new(FALSE, 3);
426 gtk_container_add(GTK_CONTAINER(hbox), box);
428 button = gtk_spin_button_new_with_range(min, max, 1.0);
429 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
431 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
432 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
437 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
440 struct gui *ui = data;
442 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
443 start_job_thread(ui);
446 static void file_open(GtkWidget *w, gpointer data);
448 static void connect_clicked(GtkWidget *widget, gpointer data)
450 struct gui *ui = data;
452 if (!ui->connected) {
453 if (!ui->nr_job_files)
454 file_open(widget, data);
455 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
456 fio_clients_connect();
457 pthread_create(&ui->t, NULL, job_thread, NULL);
458 gfio_set_connected(ui, 1);
460 fio_clients_terminate();
461 gfio_set_connected(ui, 0);
466 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
467 struct button_spec *buttonspec)
469 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
470 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
471 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
472 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
473 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
476 static void add_buttons(struct gui *ui,
477 struct button_spec *buttonlist,
482 for (i = 0; i < nbuttons; i++)
483 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
486 static void on_info_bar_response(GtkWidget *widget, gint response,
489 if (response == GTK_RESPONSE_OK) {
490 gtk_widget_destroy(widget);
491 ui.error_info_bar = NULL;
495 void report_error(GError *error)
497 if (ui.error_info_bar == NULL) {
498 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
501 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
502 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
505 ui.error_label = gtk_label_new(error->message);
506 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
507 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
509 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
510 gtk_widget_show_all(ui.vbox);
513 snprintf(buffer, sizeof(buffer), "Failed to open file.");
514 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
518 static int get_connection_details(char **host, int *port, int *type,
521 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
525 dialog = gtk_dialog_new_with_buttons("Connection details",
526 GTK_WINDOW(ui.window),
527 GTK_DIALOG_DESTROY_WITH_PARENT,
528 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
529 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
531 frame = gtk_frame_new("Hostname / socket name");
532 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
533 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
535 box = gtk_vbox_new(FALSE, 6);
536 gtk_container_add(GTK_CONTAINER(frame), box);
538 hbox = gtk_hbox_new(TRUE, 10);
539 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
540 hentry = gtk_entry_new();
541 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
542 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
544 frame = gtk_frame_new("Port");
545 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
546 box = gtk_vbox_new(FALSE, 10);
547 gtk_container_add(GTK_CONTAINER(frame), box);
549 hbox = gtk_hbox_new(TRUE, 4);
550 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
551 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
553 frame = gtk_frame_new("Type");
554 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
555 box = gtk_vbox_new(FALSE, 10);
556 gtk_container_add(GTK_CONTAINER(frame), box);
558 hbox = gtk_hbox_new(TRUE, 4);
559 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
561 combo = gtk_combo_box_text_new();
562 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
563 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
564 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
565 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
567 gtk_container_add(GTK_CONTAINER(hbox), combo);
569 frame = gtk_frame_new("Options");
570 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
571 box = gtk_vbox_new(FALSE, 10);
572 gtk_container_add(GTK_CONTAINER(frame), box);
574 hbox = gtk_hbox_new(TRUE, 4);
575 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
577 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
578 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
579 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.");
580 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
582 gtk_widget_show_all(dialog);
584 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
585 gtk_widget_destroy(dialog);
589 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
590 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
592 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
593 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
594 *type = Fio_client_ipv4;
595 else if (!strncmp(typeentry, "IPv6", 4))
596 *type = Fio_client_ipv6;
598 *type = Fio_client_socket;
601 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
603 gtk_widget_destroy(dialog);
607 static void file_open(GtkWidget *w, gpointer data)
610 GSList *filenames, *fn_glist;
611 GtkFileFilter *filter;
613 int port, type, server_start;
615 dialog = gtk_file_chooser_dialog_new("Open File",
616 GTK_WINDOW(ui.window),
617 GTK_FILE_CHOOSER_ACTION_OPEN,
618 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
619 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
621 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
623 filter = gtk_file_filter_new();
624 gtk_file_filter_add_pattern(filter, "*.fio");
625 gtk_file_filter_add_pattern(filter, "*.job");
626 gtk_file_filter_add_mime_type(filter, "text/fio");
627 gtk_file_filter_set_name(filter, "Fio job file");
628 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
630 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
631 gtk_widget_destroy(dialog);
635 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
637 gtk_widget_destroy(dialog);
639 if (get_connection_details(&host, &port, &type, &server_start))
642 filenames = fn_glist;
643 while (filenames != NULL) {
644 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
645 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
648 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
652 error = g_error_new(g_quark_from_string("fio"), 1,
653 "Failed to add client %s", host);
657 ui.client->client_data = &ui;
659 g_free(filenames->data);
660 filenames = g_slist_next(filenames);
664 g_slist_free(fn_glist);
667 static void file_save(GtkWidget *w, gpointer data)
671 dialog = gtk_file_chooser_dialog_new("Save File",
672 GTK_WINDOW(ui.window),
673 GTK_FILE_CHOOSER_ACTION_SAVE,
674 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
675 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
678 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
679 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
681 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
684 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
685 // save_job_file(filename);
688 gtk_widget_destroy(dialog);
691 static void preferences(GtkWidget *w, gpointer data)
693 GtkWidget *dialog, *frame, *box, **buttons;
696 dialog = gtk_dialog_new_with_buttons("Preferences",
697 GTK_WINDOW(ui.window),
698 GTK_DIALOG_DESTROY_WITH_PARENT,
699 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
700 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
703 frame = gtk_frame_new("Debug logging");
704 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
705 box = gtk_hbox_new(FALSE, 6);
706 gtk_container_add(GTK_CONTAINER(frame), box);
708 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
710 for (i = 0; i < FD_DEBUG_MAX; i++) {
711 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
712 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
713 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
716 gtk_widget_show_all(dialog);
718 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
719 gtk_widget_destroy(dialog);
723 for (i = 0; i < FD_DEBUG_MAX; i++) {
726 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
728 fio_debug |= (1UL << i);
731 gtk_widget_destroy(dialog);
734 static void about_dialog(GtkWidget *w, gpointer data)
736 gtk_show_about_dialog(NULL,
737 "program-name", "gfio",
738 "comments", "Gtk2 UI for fio",
740 "version", fio_version_string,
741 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
742 "logo-icon-name", "fio",
748 static GtkActionEntry menu_items[] = {
749 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
750 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
751 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
752 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
753 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
754 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
755 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
757 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
759 static const gchar *ui_string = " \
761 <menubar name=\"MainMenu\"> \
762 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
763 <menuitem name=\"Open\" action=\"OpenFile\" /> \
764 <menuitem name=\"Save\" action=\"SaveFile\" /> \
765 <separator name=\"Separator\"/> \
766 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
767 <separator name=\"Separator2\"/> \
768 <menuitem name=\"Quit\" action=\"Quit\" /> \
770 <menu name=\"Help\" action=\"HelpMenuAction\"> \
771 <menuitem name=\"About\" action=\"About\" /> \
777 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
779 GtkActionGroup *action_group = gtk_action_group_new("Menu");
782 action_group = gtk_action_group_new("Menu");
783 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
785 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
786 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
788 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
789 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
792 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
793 GtkWidget *vbox, GtkUIManager *ui_manager)
795 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
798 static void init_ui(int *argc, char **argv[], struct gui *ui)
800 GtkSettings *settings;
801 GtkUIManager *uimanager;
802 GtkWidget *menu, *probe, *probe_frame, *probe_box;
804 memset(ui, 0, sizeof(*ui));
806 /* Magical g*thread incantation, you just need this thread stuff.
807 * Without it, the update that happens in gfio_update_thread_status
808 * doesn't really happen in a timely fashion, you need expose events
810 if (!g_thread_supported())
814 gtk_init(argc, argv);
815 settings = gtk_settings_get_default();
816 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
819 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
820 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
821 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
823 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
824 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
826 ui->vbox = gtk_vbox_new(FALSE, 0);
827 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
829 uimanager = gtk_ui_manager_new();
830 menu = get_menubar_menu(ui->window, uimanager);
831 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
834 * Set up alignments for widgets at the top of ui,
835 * align top left, expand horizontally but not vertically
837 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
838 ui->topvbox = gtk_vbox_new(FALSE, 3);
839 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
840 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
842 probe = gtk_frame_new("Job");
843 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
844 probe_frame = gtk_vbox_new(FALSE, 3);
845 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
847 probe_box = gtk_hbox_new(FALSE, 3);
848 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
849 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
850 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
851 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
852 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
854 probe_box = gtk_hbox_new(FALSE, 3);
855 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
857 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
858 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
859 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
860 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
861 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
862 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
864 probe_box = gtk_hbox_new(FALSE, 3);
865 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
866 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
867 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
868 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
869 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
872 * Only add this if we have a commit rate
875 probe_box = gtk_hbox_new(FALSE, 3);
876 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
878 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
879 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
881 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
882 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
886 * Add a text box for text op messages
888 ui->textview = gtk_text_view_new();
889 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
890 gtk_text_buffer_set_text(ui->text, "", -1);
891 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
892 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
893 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
894 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
895 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
896 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
897 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
901 * Set up alignments for widgets at the bottom of ui,
902 * align bottom left, expand horizontally but not vertically
904 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
905 ui->buttonbox = gtk_hbox_new(FALSE, 0);
906 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
907 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
910 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
913 * Set up thread status progress bar
915 ui->thread_status_pb = gtk_progress_bar_new();
916 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
917 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
918 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
921 gtk_widget_show_all(ui->window);
924 int main(int argc, char *argv[], char *envp[])
926 if (initialize_fio(envp))
928 if (fio_init_options())
931 init_ui(&argc, &argv, &ui);