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);
134 static void gfio_text_op(struct fio_client *client,
135 FILE *f, __u16 pdu_len, const char *buf)
138 GtkTextBuffer *buffer;
141 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
143 gtk_text_buffer_get_end_iter(buffer, &end);
144 gtk_text_buffer_insert(buffer, &end, buf, -1);
146 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
147 &end, 0.0, FALSE, 0.0,0.0);
149 fio_client_ops.text_op(client, f, pdu_len, buf);
153 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
155 printf("gfio_disk_util_op called\n");
156 fio_client_ops.disk_util(client, cmd);
159 static void gfio_thread_status_op(struct fio_net_cmd *cmd)
161 printf("gfio_thread_status_op called\n");
162 fio_client_ops.thread_status(cmd);
165 static void gfio_group_stats_op(struct fio_net_cmd *cmd)
167 printf("gfio_group_stats_op called\n");
168 fio_client_ops.group_stats(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_eta_op(struct fio_client *client, struct fio_net_cmd *cmd)
253 struct jobs_eta *je = (struct jobs_eta *) cmd->payload;
254 struct client_eta *eta = (struct client_eta *) (uintptr_t) cmd->tag;
256 client->eta_in_flight = NULL;
257 flist_del_init(&client->eta_list);
259 fio_client_convert_jobs_eta(je);
260 fio_client_sum_jobs_eta(&eta->eta, je);
261 fio_client_dec_jobs_eta(eta, gfio_update_eta);
264 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
266 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
267 const char *os, *arch;
270 os = fio_get_os_string(probe->os);
274 arch = fio_get_arch_string(probe->arch);
279 client->name = strdup((char *) probe->hostname);
281 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
282 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
283 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
284 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
285 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
288 static void gfio_update_thread_status(char *status_message, double perc)
290 static char message[100];
291 const char *m = message;
293 strncpy(message, status_message, sizeof(message) - 1);
294 gtk_progress_bar_set_text(
295 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
296 gtk_progress_bar_set_fraction(
297 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
299 gtk_widget_queue_draw(ui.window);
303 static void gfio_quit_op(struct fio_client *client)
305 struct gui *ui = client->client_data;
307 gfio_set_connected(ui, 0);
310 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
312 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
313 struct gui *ui = client->client_data;
317 p->iodepth = le32_to_cpu(p->iodepth);
318 p->rw = le32_to_cpu(p->rw);
320 for (i = 0; i < 2; i++) {
321 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
322 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
325 p->numjobs = le32_to_cpu(p->numjobs);
326 p->group_reporting = le32_to_cpu(p->group_reporting);
328 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
329 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
330 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
332 sprintf(tmp, "%u", p->iodepth);
333 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
336 static void gfio_client_timed_out(struct fio_client *client)
338 struct gui *ui = client->client_data;
339 GtkWidget *dialog, *label, *content;
344 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);
477 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
478 struct button_spec *buttonspec)
480 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
481 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
482 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
483 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
484 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
487 static void add_buttons(struct gui *ui,
488 struct button_spec *buttonlist,
493 for (i = 0; i < nbuttons; i++)
494 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
497 static void on_info_bar_response(GtkWidget *widget, gint response,
500 if (response == GTK_RESPONSE_OK) {
501 gtk_widget_destroy(widget);
502 ui.error_info_bar = NULL;
506 void report_error(GError *error)
508 if (ui.error_info_bar == NULL) {
509 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
512 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
513 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
516 ui.error_label = gtk_label_new(error->message);
517 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
518 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
520 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
521 gtk_widget_show_all(ui.vbox);
524 snprintf(buffer, sizeof(buffer), "Failed to open file.");
525 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
529 static int get_connection_details(char **host, int *port, int *type,
532 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
536 dialog = gtk_dialog_new_with_buttons("Connection details",
537 GTK_WINDOW(ui.window),
538 GTK_DIALOG_DESTROY_WITH_PARENT,
539 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
540 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
542 frame = gtk_frame_new("Hostname / socket name");
543 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
544 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
546 box = gtk_vbox_new(FALSE, 6);
547 gtk_container_add(GTK_CONTAINER(frame), box);
549 hbox = gtk_hbox_new(TRUE, 10);
550 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
551 hentry = gtk_entry_new();
552 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
553 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
555 frame = gtk_frame_new("Port");
556 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
557 box = gtk_vbox_new(FALSE, 10);
558 gtk_container_add(GTK_CONTAINER(frame), box);
560 hbox = gtk_hbox_new(TRUE, 4);
561 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
562 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
564 frame = gtk_frame_new("Type");
565 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
566 box = gtk_vbox_new(FALSE, 10);
567 gtk_container_add(GTK_CONTAINER(frame), box);
569 hbox = gtk_hbox_new(TRUE, 4);
570 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
572 combo = gtk_combo_box_text_new();
573 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
574 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
575 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
576 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
578 gtk_container_add(GTK_CONTAINER(hbox), combo);
580 frame = gtk_frame_new("Options");
581 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
582 box = gtk_vbox_new(FALSE, 10);
583 gtk_container_add(GTK_CONTAINER(frame), box);
585 hbox = gtk_hbox_new(TRUE, 4);
586 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
588 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
589 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
590 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.");
591 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
593 gtk_widget_show_all(dialog);
595 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
596 gtk_widget_destroy(dialog);
600 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
601 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
603 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
604 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
605 *type = Fio_client_ipv4;
606 else if (!strncmp(typeentry, "IPv6", 4))
607 *type = Fio_client_ipv6;
609 *type = Fio_client_socket;
612 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
614 gtk_widget_destroy(dialog);
618 static void file_open(GtkWidget *w, gpointer data)
621 GSList *filenames, *fn_glist;
622 GtkFileFilter *filter;
624 int port, type, server_start;
626 dialog = gtk_file_chooser_dialog_new("Open File",
627 GTK_WINDOW(ui.window),
628 GTK_FILE_CHOOSER_ACTION_OPEN,
629 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
630 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
632 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
634 filter = gtk_file_filter_new();
635 gtk_file_filter_add_pattern(filter, "*.fio");
636 gtk_file_filter_add_pattern(filter, "*.job");
637 gtk_file_filter_add_mime_type(filter, "text/fio");
638 gtk_file_filter_set_name(filter, "Fio job file");
639 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
641 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
642 gtk_widget_destroy(dialog);
646 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
648 gtk_widget_destroy(dialog);
650 if (get_connection_details(&host, &port, &type, &server_start))
653 filenames = fn_glist;
654 while (filenames != NULL) {
655 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
656 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
659 ui.client = fio_client_add_explicit(host, type, port);
663 error = g_error_new(g_quark_from_string("fio"), 1,
664 "Failed to add client %s", host);
668 ui.client->client_data = &ui;
670 g_free(filenames->data);
671 filenames = g_slist_next(filenames);
675 g_slist_free(fn_glist);
678 static void file_save(GtkWidget *w, gpointer data)
682 dialog = gtk_file_chooser_dialog_new("Save File",
683 GTK_WINDOW(ui.window),
684 GTK_FILE_CHOOSER_ACTION_SAVE,
685 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
686 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
689 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
690 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
692 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
695 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
696 // save_job_file(filename);
699 gtk_widget_destroy(dialog);
702 static void about_dialog(GtkWidget *w, gpointer data)
704 gtk_show_about_dialog(NULL,
705 "program-name", "gfio",
706 "comments", "Gtk2 UI for fio",
708 "version", fio_version_string,
709 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
710 "logo-icon-name", "fio",
716 static GtkActionEntry menu_items[] = {
717 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
718 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
719 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
720 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
721 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
722 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
724 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
726 static const gchar *ui_string = " \
728 <menubar name=\"MainMenu\"> \
729 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
730 <menuitem name=\"Open\" action=\"OpenFile\" /> \
731 <menuitem name=\"Save\" action=\"SaveFile\" /> \
732 <separator name=\"Separator\"/> \
733 <menuitem name=\"Quit\" action=\"Quit\" /> \
735 <menu name=\"Help\" action=\"HelpMenuAction\"> \
736 <menuitem name=\"About\" action=\"About\" /> \
742 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
744 GtkActionGroup *action_group = gtk_action_group_new("Menu");
747 action_group = gtk_action_group_new("Menu");
748 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
750 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
751 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
753 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
754 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
757 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
758 GtkWidget *vbox, GtkUIManager *ui_manager)
760 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
763 static void init_ui(int *argc, char **argv[], struct gui *ui)
765 GtkSettings *settings;
766 GtkUIManager *uimanager;
767 GtkWidget *menu, *probe, *probe_frame, *probe_box;
769 memset(ui, 0, sizeof(*ui));
771 /* Magical g*thread incantation, you just need this thread stuff.
772 * Without it, the update that happens in gfio_update_thread_status
773 * doesn't really happen in a timely fashion, you need expose events
775 if (!g_thread_supported())
779 gtk_init(argc, argv);
780 settings = gtk_settings_get_default();
781 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
784 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
785 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
786 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
788 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
789 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
791 ui->vbox = gtk_vbox_new(FALSE, 0);
792 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
794 uimanager = gtk_ui_manager_new();
795 menu = get_menubar_menu(ui->window, uimanager);
796 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
799 * Set up alignments for widgets at the top of ui,
800 * align top left, expand horizontally but not vertically
802 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
803 ui->topvbox = gtk_vbox_new(FALSE, 3);
804 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
805 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
807 probe = gtk_frame_new("Job");
808 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
809 probe_frame = gtk_vbox_new(FALSE, 3);
810 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
812 probe_box = gtk_hbox_new(FALSE, 3);
813 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
814 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
815 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
816 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
817 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
819 probe_box = gtk_hbox_new(FALSE, 3);
820 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
822 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
823 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
824 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
825 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
826 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
827 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
829 probe_box = gtk_hbox_new(FALSE, 3);
830 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
831 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
832 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
833 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
834 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
837 * Only add this if we have a commit rate
840 probe_box = gtk_hbox_new(FALSE, 3);
841 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
843 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
844 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
846 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
847 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
851 * Add a text box for text op messages
853 ui->textview = gtk_text_view_new();
854 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
855 gtk_text_buffer_set_text(ui->text, "", -1);
856 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
857 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
858 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
859 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
860 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
861 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
862 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
866 * Set up alignments for widgets at the bottom of ui,
867 * align bottom left, expand horizontally but not vertically
869 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
870 ui->buttonbox = gtk_hbox_new(FALSE, 0);
871 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
872 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
875 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
878 * Set up thread status progress bar
880 ui->thread_status_pb = gtk_progress_bar_new();
881 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
882 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
883 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
886 gtk_widget_show_all(ui->window);
889 int main(int argc, char *argv[], char *envp[])
891 if (initialize_fio(envp))
893 if (fio_init_options())
897 init_ui(&argc, &argv, &ui);