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,
134 FILE *f, __u16 pdu_len, const char *buf)
137 GtkTextBuffer *buffer;
140 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
142 gtk_text_buffer_get_end_iter(buffer, &end);
143 gtk_text_buffer_insert(buffer, &end, buf, -1);
145 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
146 &end, 0.0, FALSE, 0.0,0.0);
148 fio_client_ops.text_op(client, f, pdu_len, buf);
152 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
154 printf("gfio_disk_util_op called\n");
155 fio_client_ops.disk_util(client, cmd);
158 static void gfio_thread_status_op(struct fio_net_cmd *cmd)
160 printf("gfio_thread_status_op called\n");
161 fio_client_ops.thread_status(cmd);
164 static void gfio_group_stats_op(struct fio_net_cmd *cmd)
166 printf("gfio_group_stats_op called\n");
167 fio_client_ops.group_stats(cmd);
170 static void gfio_update_eta(struct jobs_eta *je)
182 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
183 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
184 eta_to_str(eta_str, je->eta_sec);
187 sprintf(tmp, "%u", je->nr_running);
188 gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp);
189 sprintf(tmp, "%u", je->files_open);
190 gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp);
193 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
194 if (je->m_rate || je->t_rate) {
197 mr = num2str(je->m_rate, 4, 0, i2p);
198 tr = num2str(je->t_rate, 4, 0, i2p);
199 gtk_label_set_text(GTK_LABEL(ui.eta.
200 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
203 } else if (je->m_iops || je->t_iops)
204 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
206 gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---");
207 gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---");
208 gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---");
209 gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---");
212 if (je->eta_sec != INT_MAX && je->nr_running) {
216 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
217 strcpy(output, "-.-% done");
221 sprintf(output, "%3.1f%% done", perc);
224 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
225 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
227 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
228 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
230 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]);
231 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]);
232 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]);
233 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]);
242 char *dst = output + strlen(output);
244 sprintf(dst, " - %s", eta_str);
247 gfio_update_thread_status(output, perc);
250 static void gfio_eta_op(struct fio_client *client, struct fio_net_cmd *cmd)
252 struct jobs_eta *je = (struct jobs_eta *) cmd->payload;
253 struct client_eta *eta = (struct client_eta *) (uintptr_t) cmd->tag;
255 client->eta_in_flight = NULL;
256 flist_del_init(&client->eta_list);
258 fio_client_convert_jobs_eta(je);
259 fio_client_sum_jobs_eta(&eta->eta, je);
260 fio_client_dec_jobs_eta(eta, gfio_update_eta);
263 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
265 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
266 const char *os, *arch;
269 os = fio_get_os_string(probe->os);
273 arch = fio_get_arch_string(probe->arch);
278 client->name = strdup((char *) probe->hostname);
280 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
281 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
282 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
283 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
284 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
287 static void gfio_update_thread_status(char *status_message, double perc)
289 static char message[100];
290 const char *m = message;
292 strncpy(message, status_message, sizeof(message) - 1);
293 gtk_progress_bar_set_text(
294 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
295 gtk_progress_bar_set_fraction(
296 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
298 gtk_widget_queue_draw(ui.window);
302 static void gfio_quit_op(struct fio_client *client)
304 struct gui *ui = client->client_data;
306 gfio_set_connected(ui, 0);
309 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
311 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
312 struct gui *ui = client->client_data;
316 p->iodepth = le32_to_cpu(p->iodepth);
317 p->rw = le32_to_cpu(p->rw);
319 for (i = 0; i < 2; i++) {
320 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
321 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
324 p->numjobs = le32_to_cpu(p->numjobs);
325 p->group_reporting = le32_to_cpu(p->group_reporting);
327 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
328 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
329 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
331 sprintf(tmp, "%u", p->iodepth);
332 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
335 static void gfio_client_timed_out(struct fio_client *client)
337 struct gui *ui = client->client_data;
338 GtkWidget *dialog, *label, *content;
343 gfio_set_connected(ui, 0);
346 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
348 dialog = gtk_dialog_new_with_buttons("Timed out!",
349 GTK_WINDOW(ui->window),
350 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
351 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
353 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
354 label = gtk_label_new((const gchar *) buf);
355 gtk_container_add(GTK_CONTAINER(content), label);
356 gtk_widget_show_all(dialog);
357 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
359 gtk_dialog_run(GTK_DIALOG(dialog));
360 gtk_widget_destroy(dialog);
365 struct client_ops gfio_client_ops = {
366 .text_op = gfio_text_op,
367 .disk_util = gfio_disk_util_op,
368 .thread_status = gfio_thread_status_op,
369 .group_stats = gfio_group_stats_op,
371 .probe = gfio_probe_op,
372 .quit = gfio_quit_op,
373 .add_job = gfio_add_job_op,
374 .timed_out = gfio_client_timed_out,
378 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
379 __attribute__((unused)) gpointer data)
384 static void *job_thread(void *arg)
386 fio_handle_clients(&gfio_client_ops);
390 static int send_job_files(struct gui *ui)
394 for (i = 0; i < ui->nr_job_files; i++) {
395 ret = fio_clients_send_ini(ui->job_files[i]);
399 free(ui->job_files[i]);
400 ui->job_files[i] = NULL;
402 while (i < ui->nr_job_files) {
403 free(ui->job_files[i]);
404 ui->job_files[i] = NULL;
411 static void start_job_thread(struct gui *ui)
413 if (send_job_files(ui)) {
414 printf("Yeah, I didn't really like those options too much.\n");
415 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
420 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
422 GtkWidget *label_widget;
425 frame = gtk_frame_new(label);
426 label_widget = gtk_label_new(NULL);
427 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
428 gtk_container_add(GTK_CONTAINER(frame), label_widget);
433 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
435 GtkWidget *button, *box;
437 box = gtk_hbox_new(FALSE, 3);
438 gtk_container_add(GTK_CONTAINER(hbox), box);
440 button = gtk_spin_button_new_with_range(min, max, 1.0);
441 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
443 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
444 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
449 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
452 struct gui *ui = data;
454 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
455 start_job_thread(ui);
458 static void file_open(GtkWidget *w, gpointer data);
460 static void connect_clicked(GtkWidget *widget, gpointer data)
462 struct gui *ui = data;
464 if (!ui->connected) {
465 if (!ui->nr_job_files)
466 file_open(widget, data);
467 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
468 fio_clients_connect();
469 pthread_create(&ui->t, NULL, job_thread, NULL);
470 gfio_set_connected(ui, 1);
472 fio_clients_terminate();
473 gfio_set_connected(ui, 0);
478 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
479 struct button_spec *buttonspec)
481 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
482 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
483 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
484 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
485 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
488 static void add_buttons(struct gui *ui,
489 struct button_spec *buttonlist,
494 for (i = 0; i < nbuttons; i++)
495 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
498 static void on_info_bar_response(GtkWidget *widget, gint response,
501 if (response == GTK_RESPONSE_OK) {
502 gtk_widget_destroy(widget);
503 ui.error_info_bar = NULL;
507 void report_error(GError *error)
509 if (ui.error_info_bar == NULL) {
510 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
513 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
514 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
517 ui.error_label = gtk_label_new(error->message);
518 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
519 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
521 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
522 gtk_widget_show_all(ui.vbox);
525 snprintf(buffer, sizeof(buffer), "Failed to open file.");
526 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
530 static int get_connection_details(char **host, int *port, int *type,
533 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
537 dialog = gtk_dialog_new_with_buttons("Connection details",
538 GTK_WINDOW(ui.window),
539 GTK_DIALOG_DESTROY_WITH_PARENT,
540 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
541 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
543 frame = gtk_frame_new("Hostname / socket name");
544 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
545 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
547 box = gtk_vbox_new(FALSE, 6);
548 gtk_container_add(GTK_CONTAINER(frame), box);
550 hbox = gtk_hbox_new(TRUE, 10);
551 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
552 hentry = gtk_entry_new();
553 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
554 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
556 frame = gtk_frame_new("Port");
557 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
558 box = gtk_vbox_new(FALSE, 10);
559 gtk_container_add(GTK_CONTAINER(frame), box);
561 hbox = gtk_hbox_new(TRUE, 4);
562 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
563 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
565 frame = gtk_frame_new("Type");
566 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
567 box = gtk_vbox_new(FALSE, 10);
568 gtk_container_add(GTK_CONTAINER(frame), box);
570 hbox = gtk_hbox_new(TRUE, 4);
571 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
573 combo = gtk_combo_box_text_new();
574 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
575 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
576 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
577 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
579 gtk_container_add(GTK_CONTAINER(hbox), combo);
581 frame = gtk_frame_new("Options");
582 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
583 box = gtk_vbox_new(FALSE, 10);
584 gtk_container_add(GTK_CONTAINER(frame), box);
586 hbox = gtk_hbox_new(TRUE, 4);
587 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
589 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
590 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
591 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.");
592 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
594 gtk_widget_show_all(dialog);
596 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
597 gtk_widget_destroy(dialog);
601 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
602 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
604 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
605 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
606 *type = Fio_client_ipv4;
607 else if (!strncmp(typeentry, "IPv6", 4))
608 *type = Fio_client_ipv6;
610 *type = Fio_client_socket;
613 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
615 gtk_widget_destroy(dialog);
619 static void file_open(GtkWidget *w, gpointer data)
622 GSList *filenames, *fn_glist;
623 GtkFileFilter *filter;
625 int port, type, server_start;
627 dialog = gtk_file_chooser_dialog_new("Open File",
628 GTK_WINDOW(ui.window),
629 GTK_FILE_CHOOSER_ACTION_OPEN,
630 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
631 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
633 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
635 filter = gtk_file_filter_new();
636 gtk_file_filter_add_pattern(filter, "*.fio");
637 gtk_file_filter_add_pattern(filter, "*.job");
638 gtk_file_filter_add_mime_type(filter, "text/fio");
639 gtk_file_filter_set_name(filter, "Fio job file");
640 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
642 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
643 gtk_widget_destroy(dialog);
647 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
649 gtk_widget_destroy(dialog);
651 if (get_connection_details(&host, &port, &type, &server_start))
654 filenames = fn_glist;
655 while (filenames != NULL) {
656 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
657 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
660 ui.client = fio_client_add_explicit(host, type, port);
664 error = g_error_new(g_quark_from_string("fio"), 1,
665 "Failed to add client %s", host);
669 ui.client->client_data = &ui;
671 g_free(filenames->data);
672 filenames = g_slist_next(filenames);
676 g_slist_free(fn_glist);
679 static void file_save(GtkWidget *w, gpointer data)
683 dialog = gtk_file_chooser_dialog_new("Save File",
684 GTK_WINDOW(ui.window),
685 GTK_FILE_CHOOSER_ACTION_SAVE,
686 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
687 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
690 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
691 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
693 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
696 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
697 // save_job_file(filename);
700 gtk_widget_destroy(dialog);
703 static void about_dialog(GtkWidget *w, gpointer data)
705 gtk_show_about_dialog(NULL,
706 "program-name", "gfio",
707 "comments", "Gtk2 UI for fio",
709 "version", fio_version_string,
710 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
711 "logo-icon-name", "fio",
717 static GtkActionEntry menu_items[] = {
718 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
719 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
720 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
721 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
722 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
723 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
725 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
727 static const gchar *ui_string = " \
729 <menubar name=\"MainMenu\"> \
730 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
731 <menuitem name=\"Open\" action=\"OpenFile\" /> \
732 <menuitem name=\"Save\" action=\"SaveFile\" /> \
733 <separator name=\"Separator\"/> \
734 <menuitem name=\"Quit\" action=\"Quit\" /> \
736 <menu name=\"Help\" action=\"HelpMenuAction\"> \
737 <menuitem name=\"About\" action=\"About\" /> \
743 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
745 GtkActionGroup *action_group = gtk_action_group_new("Menu");
748 action_group = gtk_action_group_new("Menu");
749 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
751 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
752 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
754 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
755 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
758 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
759 GtkWidget *vbox, GtkUIManager *ui_manager)
761 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
764 static void init_ui(int *argc, char **argv[], struct gui *ui)
766 GtkSettings *settings;
767 GtkUIManager *uimanager;
768 GtkWidget *menu, *probe, *probe_frame, *probe_box;
770 memset(ui, 0, sizeof(*ui));
772 /* Magical g*thread incantation, you just need this thread stuff.
773 * Without it, the update that happens in gfio_update_thread_status
774 * doesn't really happen in a timely fashion, you need expose events
776 if (!g_thread_supported())
780 gtk_init(argc, argv);
781 settings = gtk_settings_get_default();
782 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
785 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
786 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
787 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
789 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
790 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
792 ui->vbox = gtk_vbox_new(FALSE, 0);
793 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
795 uimanager = gtk_ui_manager_new();
796 menu = get_menubar_menu(ui->window, uimanager);
797 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
800 * Set up alignments for widgets at the top of ui,
801 * align top left, expand horizontally but not vertically
803 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
804 ui->topvbox = gtk_vbox_new(FALSE, 3);
805 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
806 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
808 probe = gtk_frame_new("Job");
809 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
810 probe_frame = gtk_vbox_new(FALSE, 3);
811 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
813 probe_box = gtk_hbox_new(FALSE, 3);
814 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
815 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
816 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
817 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
818 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
820 probe_box = gtk_hbox_new(FALSE, 3);
821 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
823 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
824 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
825 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
826 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
827 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
828 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
830 probe_box = gtk_hbox_new(FALSE, 3);
831 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
832 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
833 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
834 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
835 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
838 * Only add this if we have a commit rate
841 probe_box = gtk_hbox_new(FALSE, 3);
842 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
844 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
845 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
847 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
848 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
852 * Add a text box for text op messages
854 ui->textview = gtk_text_view_new();
855 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
856 gtk_text_buffer_set_text(ui->text, "", -1);
857 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
858 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
859 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
860 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
861 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
862 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
863 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
867 * Set up alignments for widgets at the bottom of ui,
868 * align bottom left, expand horizontally but not vertically
870 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
871 ui->buttonbox = gtk_hbox_new(FALSE, 0);
872 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
873 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
876 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
879 * Set up thread status progress bar
881 ui->thread_status_pb = gtk_progress_bar_new();
882 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
883 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
884 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
887 gtk_widget_show_all(ui->window);
890 int main(int argc, char *argv[], char *envp[])
892 if (initialize_fio(envp))
894 if (fio_init_options())
897 init_ui(&argc, &argv, &ui);