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_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
251 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
252 const char *os, *arch;
255 os = fio_get_os_string(probe->os);
259 arch = fio_get_arch_string(probe->arch);
264 client->name = strdup((char *) probe->hostname);
266 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
267 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
268 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
269 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
270 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
273 static void gfio_update_thread_status(char *status_message, double perc)
275 static char message[100];
276 const char *m = message;
278 strncpy(message, status_message, sizeof(message) - 1);
279 gtk_progress_bar_set_text(
280 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
281 gtk_progress_bar_set_fraction(
282 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
284 gtk_widget_queue_draw(ui.window);
288 static void gfio_quit_op(struct fio_client *client)
290 struct gui *ui = client->client_data;
292 gfio_set_connected(ui, 0);
295 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
297 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
298 struct gui *ui = client->client_data;
302 p->iodepth = le32_to_cpu(p->iodepth);
303 p->rw = le32_to_cpu(p->rw);
305 for (i = 0; i < 2; i++) {
306 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
307 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
310 p->numjobs = le32_to_cpu(p->numjobs);
311 p->group_reporting = le32_to_cpu(p->group_reporting);
313 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
314 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
315 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
317 sprintf(tmp, "%u", p->iodepth);
318 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
321 static void gfio_client_timed_out(struct fio_client *client)
323 struct gui *ui = client->client_data;
324 GtkWidget *dialog, *label, *content;
329 gfio_set_connected(ui, 0);
332 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
334 dialog = gtk_dialog_new_with_buttons("Timed out!",
335 GTK_WINDOW(ui->window),
336 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
337 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
339 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
340 label = gtk_label_new((const gchar *) buf);
341 gtk_container_add(GTK_CONTAINER(content), label);
342 gtk_widget_show_all(dialog);
343 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
345 gtk_dialog_run(GTK_DIALOG(dialog));
346 gtk_widget_destroy(dialog);
351 struct client_ops gfio_client_ops = {
352 .text_op = gfio_text_op,
353 .disk_util = gfio_disk_util_op,
354 .thread_status = gfio_thread_status_op,
355 .group_stats = gfio_group_stats_op,
356 .eta = gfio_update_eta,
357 .probe = gfio_probe_op,
358 .quit = gfio_quit_op,
359 .add_job = gfio_add_job_op,
360 .timed_out = gfio_client_timed_out,
364 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
365 __attribute__((unused)) gpointer data)
370 static void *job_thread(void *arg)
372 fio_handle_clients(&gfio_client_ops);
376 static int send_job_files(struct gui *ui)
380 for (i = 0; i < ui->nr_job_files; i++) {
381 ret = fio_clients_send_ini(ui->job_files[i]);
385 free(ui->job_files[i]);
386 ui->job_files[i] = NULL;
388 while (i < ui->nr_job_files) {
389 free(ui->job_files[i]);
390 ui->job_files[i] = NULL;
397 static void start_job_thread(struct gui *ui)
399 if (send_job_files(ui)) {
400 printf("Yeah, I didn't really like those options too much.\n");
401 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
406 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
408 GtkWidget *label_widget;
411 frame = gtk_frame_new(label);
412 label_widget = gtk_label_new(NULL);
413 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
414 gtk_container_add(GTK_CONTAINER(frame), label_widget);
419 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
421 GtkWidget *button, *box;
423 box = gtk_hbox_new(FALSE, 3);
424 gtk_container_add(GTK_CONTAINER(hbox), box);
426 button = gtk_spin_button_new_with_range(min, max, 1.0);
427 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
429 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
430 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
435 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
438 struct gui *ui = data;
440 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
441 start_job_thread(ui);
444 static void file_open(GtkWidget *w, gpointer data);
446 static void connect_clicked(GtkWidget *widget, gpointer data)
448 struct gui *ui = data;
450 if (!ui->connected) {
451 if (!ui->nr_job_files)
452 file_open(widget, data);
453 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
454 fio_clients_connect();
455 pthread_create(&ui->t, NULL, job_thread, NULL);
456 gfio_set_connected(ui, 1);
458 fio_clients_terminate();
459 gfio_set_connected(ui, 0);
464 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
465 struct button_spec *buttonspec)
467 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
468 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
469 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
470 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
471 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
474 static void add_buttons(struct gui *ui,
475 struct button_spec *buttonlist,
480 for (i = 0; i < nbuttons; i++)
481 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
484 static void on_info_bar_response(GtkWidget *widget, gint response,
487 if (response == GTK_RESPONSE_OK) {
488 gtk_widget_destroy(widget);
489 ui.error_info_bar = NULL;
493 void report_error(GError *error)
495 if (ui.error_info_bar == NULL) {
496 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
499 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
500 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
503 ui.error_label = gtk_label_new(error->message);
504 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
505 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
507 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
508 gtk_widget_show_all(ui.vbox);
511 snprintf(buffer, sizeof(buffer), "Failed to open file.");
512 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
516 static int get_connection_details(char **host, int *port, int *type,
519 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
523 dialog = gtk_dialog_new_with_buttons("Connection details",
524 GTK_WINDOW(ui.window),
525 GTK_DIALOG_DESTROY_WITH_PARENT,
526 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
527 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
529 frame = gtk_frame_new("Hostname / socket name");
530 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
531 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
533 box = gtk_vbox_new(FALSE, 6);
534 gtk_container_add(GTK_CONTAINER(frame), box);
536 hbox = gtk_hbox_new(TRUE, 10);
537 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
538 hentry = gtk_entry_new();
539 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
540 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
542 frame = gtk_frame_new("Port");
543 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
544 box = gtk_vbox_new(FALSE, 10);
545 gtk_container_add(GTK_CONTAINER(frame), box);
547 hbox = gtk_hbox_new(TRUE, 4);
548 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
549 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
551 frame = gtk_frame_new("Type");
552 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
553 box = gtk_vbox_new(FALSE, 10);
554 gtk_container_add(GTK_CONTAINER(frame), box);
556 hbox = gtk_hbox_new(TRUE, 4);
557 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
559 combo = gtk_combo_box_text_new();
560 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
561 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
562 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
563 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
565 gtk_container_add(GTK_CONTAINER(hbox), combo);
567 frame = gtk_frame_new("Options");
568 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
569 box = gtk_vbox_new(FALSE, 10);
570 gtk_container_add(GTK_CONTAINER(frame), box);
572 hbox = gtk_hbox_new(TRUE, 4);
573 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
575 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
576 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
577 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.");
578 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
580 gtk_widget_show_all(dialog);
582 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
583 gtk_widget_destroy(dialog);
587 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
588 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
590 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
591 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
592 *type = Fio_client_ipv4;
593 else if (!strncmp(typeentry, "IPv6", 4))
594 *type = Fio_client_ipv6;
596 *type = Fio_client_socket;
599 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
601 gtk_widget_destroy(dialog);
605 static void file_open(GtkWidget *w, gpointer data)
608 GSList *filenames, *fn_glist;
609 GtkFileFilter *filter;
611 int port, type, server_start;
613 dialog = gtk_file_chooser_dialog_new("Open File",
614 GTK_WINDOW(ui.window),
615 GTK_FILE_CHOOSER_ACTION_OPEN,
616 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
617 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
619 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
621 filter = gtk_file_filter_new();
622 gtk_file_filter_add_pattern(filter, "*.fio");
623 gtk_file_filter_add_pattern(filter, "*.job");
624 gtk_file_filter_add_mime_type(filter, "text/fio");
625 gtk_file_filter_set_name(filter, "Fio job file");
626 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
628 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
629 gtk_widget_destroy(dialog);
633 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
635 gtk_widget_destroy(dialog);
637 if (get_connection_details(&host, &port, &type, &server_start))
640 filenames = fn_glist;
641 while (filenames != NULL) {
642 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
643 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
646 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
650 error = g_error_new(g_quark_from_string("fio"), 1,
651 "Failed to add client %s", host);
655 ui.client->client_data = &ui;
657 g_free(filenames->data);
658 filenames = g_slist_next(filenames);
662 g_slist_free(fn_glist);
665 static void file_save(GtkWidget *w, gpointer data)
669 dialog = gtk_file_chooser_dialog_new("Save File",
670 GTK_WINDOW(ui.window),
671 GTK_FILE_CHOOSER_ACTION_SAVE,
672 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
673 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
676 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
677 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
679 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
682 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
683 // save_job_file(filename);
686 gtk_widget_destroy(dialog);
689 static void preferences(GtkWidget *w, gpointer data)
691 GtkWidget *dialog, *frame, *box, **buttons;
694 dialog = gtk_dialog_new_with_buttons("Preferences",
695 GTK_WINDOW(ui.window),
696 GTK_DIALOG_DESTROY_WITH_PARENT,
697 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
698 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
701 frame = gtk_frame_new("Debug logging");
702 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
703 box = gtk_hbox_new(FALSE, 6);
704 gtk_container_add(GTK_CONTAINER(frame), box);
706 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
708 for (i = 0; i < FD_DEBUG_MAX; i++) {
709 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
710 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
711 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
714 gtk_widget_show_all(dialog);
716 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
717 gtk_widget_destroy(dialog);
721 for (i = 0; i < FD_DEBUG_MAX; i++) {
724 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
726 fio_debug |= (1UL << i);
729 gtk_widget_destroy(dialog);
732 static void about_dialog(GtkWidget *w, gpointer data)
734 gtk_show_about_dialog(NULL,
735 "program-name", "gfio",
736 "comments", "Gtk2 UI for fio",
738 "version", fio_version_string,
739 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
740 "logo-icon-name", "fio",
746 static GtkActionEntry menu_items[] = {
747 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
748 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
749 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
750 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
751 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
752 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
753 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
755 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
757 static const gchar *ui_string = " \
759 <menubar name=\"MainMenu\"> \
760 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
761 <menuitem name=\"Open\" action=\"OpenFile\" /> \
762 <menuitem name=\"Save\" action=\"SaveFile\" /> \
763 <separator name=\"Separator\"/> \
764 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
765 <separator name=\"Separator2\"/> \
766 <menuitem name=\"Quit\" action=\"Quit\" /> \
768 <menu name=\"Help\" action=\"HelpMenuAction\"> \
769 <menuitem name=\"About\" action=\"About\" /> \
775 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
777 GtkActionGroup *action_group = gtk_action_group_new("Menu");
780 action_group = gtk_action_group_new("Menu");
781 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
783 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
784 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
786 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
787 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
790 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
791 GtkWidget *vbox, GtkUIManager *ui_manager)
793 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
796 static void init_ui(int *argc, char **argv[], struct gui *ui)
798 GtkSettings *settings;
799 GtkUIManager *uimanager;
800 GtkWidget *menu, *probe, *probe_frame, *probe_box;
802 memset(ui, 0, sizeof(*ui));
804 /* Magical g*thread incantation, you just need this thread stuff.
805 * Without it, the update that happens in gfio_update_thread_status
806 * doesn't really happen in a timely fashion, you need expose events
808 if (!g_thread_supported())
812 gtk_init(argc, argv);
813 settings = gtk_settings_get_default();
814 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
817 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
818 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
819 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
821 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
822 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
824 ui->vbox = gtk_vbox_new(FALSE, 0);
825 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
827 uimanager = gtk_ui_manager_new();
828 menu = get_menubar_menu(ui->window, uimanager);
829 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
832 * Set up alignments for widgets at the top of ui,
833 * align top left, expand horizontally but not vertically
835 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
836 ui->topvbox = gtk_vbox_new(FALSE, 3);
837 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
838 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
840 probe = gtk_frame_new("Job");
841 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
842 probe_frame = gtk_vbox_new(FALSE, 3);
843 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
845 probe_box = gtk_hbox_new(FALSE, 3);
846 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
847 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
848 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
849 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
850 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
852 probe_box = gtk_hbox_new(FALSE, 3);
853 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
855 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
856 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
857 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
858 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
859 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
860 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
862 probe_box = gtk_hbox_new(FALSE, 3);
863 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
864 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
865 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
866 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
867 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
870 * Only add this if we have a commit rate
873 probe_box = gtk_hbox_new(FALSE, 3);
874 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
876 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
877 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
879 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
880 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
884 * Add a text box for text op messages
886 ui->textview = gtk_text_view_new();
887 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
888 gtk_text_buffer_set_text(ui->text, "", -1);
889 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
890 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
891 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
892 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
893 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
894 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
895 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
899 * Set up alignments for widgets at the bottom of ui,
900 * align bottom left, expand horizontally but not vertically
902 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
903 ui->buttonbox = gtk_hbox_new(FALSE, 0);
904 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
905 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
908 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
911 * Set up thread status progress bar
913 ui->thread_status_pb = gtk_progress_bar_new();
914 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
915 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
916 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
919 gtk_widget_show_all(ui->window);
922 int main(int argc, char *argv[], char *envp[])
924 if (initialize_fio(envp))
926 if (fio_init_options())
929 init_ui(&argc, &argv, &ui);