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 GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
122 GtkWidget *entry, *frame;
124 frame = gtk_frame_new(label);
125 entry = gtk_entry_new();
126 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
127 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
128 gtk_container_add(GTK_CONTAINER(frame), entry);
133 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
135 GtkWidget *label_widget;
138 frame = gtk_frame_new(label);
139 label_widget = gtk_label_new(NULL);
140 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
141 gtk_container_add(GTK_CONTAINER(frame), label_widget);
146 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
148 GtkWidget *button, *box;
150 box = gtk_hbox_new(FALSE, 3);
151 gtk_container_add(GTK_CONTAINER(hbox), box);
153 button = gtk_spin_button_new_with_range(min, max, 1.0);
154 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
156 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
157 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
162 static void gfio_set_connected(struct gui *ui, int connected)
165 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
167 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
170 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
171 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
175 static void label_set_int_value(GtkWidget *entry, unsigned int val)
179 sprintf(tmp, "%u", val);
180 gtk_label_set_text(GTK_LABEL(entry), tmp);
183 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
187 sprintf(tmp, "%u", val);
188 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
191 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
192 unsigned long max, double mean, double dev)
194 const char *base = "(usec)";
195 GtkWidget *hbox, *label, *frame;
199 if (!usec_to_msec(&min, &max, &mean, &dev))
202 minp = num2str(min, 6, 1, 0);
203 maxp = num2str(max, 6, 1, 0);
205 sprintf(tmp, "%s %s", name, base);
206 frame = gtk_frame_new(tmp);
207 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
209 hbox = gtk_hbox_new(FALSE, 3);
210 gtk_container_add(GTK_CONTAINER(frame), hbox);
212 label = new_info_label_in_frame(hbox, "Minimum");
213 gtk_label_set_text(GTK_LABEL(label), minp);
214 label = new_info_label_in_frame(hbox, "Maximum");
215 gtk_label_set_text(GTK_LABEL(label), maxp);
216 label = new_info_label_in_frame(hbox, "Average");
217 sprintf(tmp, "%5.02f", mean);
218 gtk_label_set_text(GTK_LABEL(label), tmp);
219 label = new_info_label_in_frame(hbox, "Standard deviation");
220 sprintf(tmp, "%5.02f", dev);
221 gtk_label_set_text(GTK_LABEL(label), tmp);
228 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
229 struct thread_stat *ts, int ddir)
231 const char *ddir_label[2] = { "Read", "Write" };
232 GtkWidget *frame, *label, *box, *vbox;
233 unsigned long min, max, runt;
234 unsigned long long bw, iops;
236 char *io_p, *bw_p, *iops_p;
239 if (!ts->runtime[ddir])
242 i2p = is_power_of_2(rs->kb_base);
243 runt = ts->runtime[ddir];
245 bw = (1000 * ts->io_bytes[ddir]) / runt;
246 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
247 bw_p = num2str(bw, 6, 1, i2p);
249 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
250 iops_p = num2str(iops, 6, 1, 0);
252 box = gtk_hbox_new(FALSE, 3);
253 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
255 frame = gtk_frame_new(ddir_label[ddir]);
256 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
258 vbox = gtk_vbox_new(FALSE, 3);
259 gtk_container_add(GTK_CONTAINER(frame), vbox);
261 box = gtk_hbox_new(FALSE, 3);
262 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 3);
264 label = new_info_label_in_frame(box, "IO");
265 gtk_label_set_text(GTK_LABEL(label), io_p);
266 label = new_info_label_in_frame(box, "Bandwidth");
267 gtk_label_set_text(GTK_LABEL(label), bw_p);
268 label = new_info_label_in_frame(box, "IOPS");
269 gtk_label_set_text(GTK_LABEL(label), iops_p);
270 label = new_info_label_in_frame(box, "Runtime (msec)");
271 label_set_int_value(label, ts->runtime[ddir]);
273 frame = gtk_frame_new("Latency");
274 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
276 vbox = gtk_vbox_new(FALSE, 3);
277 gtk_container_add(GTK_CONTAINER(frame), vbox);
279 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
280 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
281 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
282 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
283 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
284 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
291 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
292 struct group_run_stats *rs)
294 GtkWidget *dialog, *box, *vbox, *entry, *content;
295 struct gui *ui = client->client_data;
299 dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
300 GTK_DIALOG_DESTROY_WITH_PARENT,
301 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
303 g_signal_connect_swapped(dialog, "response",
304 G_CALLBACK(gtk_widget_destroy),
307 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
309 vbox = gtk_vbox_new(FALSE, 3);
310 gtk_container_add(GTK_CONTAINER(content), vbox);
312 box = gtk_hbox_new(TRUE, 3);
313 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
315 entry = new_info_entry_in_frame(box, "Name");
316 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
317 if (strlen(ts->description)) {
318 entry = new_info_entry_in_frame(box, "Description");
319 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
321 entry = new_info_entry_in_frame(box, "Group ID");
322 entry_set_int_value(entry, ts->groupid);
323 entry = new_info_entry_in_frame(box, "Jobs");
324 entry_set_int_value(entry, ts->members);
325 entry = new_info_entry_in_frame(box, "Error");
326 entry_set_int_value(entry, ts->error);
327 entry = new_info_entry_in_frame(box, "PID");
328 entry_set_int_value(entry, ts->pid);
330 if (ts->io_bytes[DDIR_READ])
331 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
332 if (ts->io_bytes[DDIR_WRITE])
333 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
335 gtk_widget_show_all(dialog);
340 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
343 GtkTextBuffer *buffer;
346 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
348 gtk_text_buffer_get_end_iter(buffer, &end);
349 gtk_text_buffer_insert(buffer, &end, buf, -1);
351 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
352 &end, 0.0, FALSE, 0.0,0.0);
354 fio_client_ops.text_op(client, cmd);
358 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
360 printf("gfio_disk_util_op called\n");
361 fio_client_ops.disk_util(client, cmd);
364 extern int sum_stat_clients;
365 extern struct thread_stat client_ts;
366 extern struct group_run_stats client_gs;
368 static int sum_stat_nr;
370 static void gfio_thread_status_op(struct fio_client *client,
371 struct fio_net_cmd *cmd)
373 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
375 gfio_display_ts(client, &p->ts, &p->rs);
377 if (sum_stat_clients == 1)
380 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
381 sum_group_stats(&client_gs, &p->rs);
384 client_ts.groupid = p->ts.groupid;
386 if (++sum_stat_nr == sum_stat_clients) {
387 strcpy(client_ts.name, "All clients");
388 gfio_display_ts(client, &client_ts, &client_gs);
392 static void gfio_group_stats_op(struct fio_client *client,
393 struct fio_net_cmd *cmd)
395 printf("gfio_group_stats_op called\n");
396 fio_client_ops.group_stats(client, cmd);
399 static void gfio_update_eta(struct jobs_eta *je)
411 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
412 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
413 eta_to_str(eta_str, je->eta_sec);
416 sprintf(tmp, "%u", je->nr_running);
417 gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp);
418 sprintf(tmp, "%u", je->files_open);
419 gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp);
422 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
423 if (je->m_rate || je->t_rate) {
426 mr = num2str(je->m_rate, 4, 0, i2p);
427 tr = num2str(je->t_rate, 4, 0, i2p);
428 gtk_label_set_text(GTK_LABEL(ui.eta.
429 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
432 } else if (je->m_iops || je->t_iops)
433 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
435 gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---");
436 gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---");
437 gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---");
438 gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---");
441 if (je->eta_sec != INT_MAX && je->nr_running) {
445 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
446 strcpy(output, "-.-% done");
450 sprintf(output, "%3.1f%% done", perc);
453 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
454 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
456 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
457 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
459 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]);
460 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]);
461 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]);
462 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]);
471 char *dst = output + strlen(output);
473 sprintf(dst, " - %s", eta_str);
476 gfio_update_thread_status(output, perc);
479 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
481 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
482 const char *os, *arch;
485 os = fio_get_os_string(probe->os);
489 arch = fio_get_arch_string(probe->arch);
494 client->name = strdup((char *) probe->hostname);
496 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
497 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
498 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
499 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
500 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
503 static void gfio_update_thread_status(char *status_message, double perc)
505 static char message[100];
506 const char *m = message;
508 strncpy(message, status_message, sizeof(message) - 1);
509 gtk_progress_bar_set_text(
510 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
511 gtk_progress_bar_set_fraction(
512 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
514 gtk_widget_queue_draw(ui.window);
518 static void gfio_quit_op(struct fio_client *client)
520 struct gui *ui = client->client_data;
522 gfio_set_connected(ui, 0);
525 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
527 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
528 struct gui *ui = client->client_data;
532 p->iodepth = le32_to_cpu(p->iodepth);
533 p->rw = le32_to_cpu(p->rw);
535 for (i = 0; i < 2; i++) {
536 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
537 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
540 p->numjobs = le32_to_cpu(p->numjobs);
541 p->group_reporting = le32_to_cpu(p->group_reporting);
543 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
544 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
545 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
547 sprintf(tmp, "%u", p->iodepth);
548 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
551 static void gfio_client_timed_out(struct fio_client *client)
553 struct gui *ui = client->client_data;
554 GtkWidget *dialog, *label, *content;
559 gfio_set_connected(ui, 0);
562 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
564 dialog = gtk_dialog_new_with_buttons("Timed out!",
565 GTK_WINDOW(ui->window),
566 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
567 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
569 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
570 label = gtk_label_new((const gchar *) buf);
571 gtk_container_add(GTK_CONTAINER(content), label);
572 gtk_widget_show_all(dialog);
573 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
575 gtk_dialog_run(GTK_DIALOG(dialog));
576 gtk_widget_destroy(dialog);
581 struct client_ops gfio_client_ops = {
582 .text_op = gfio_text_op,
583 .disk_util = gfio_disk_util_op,
584 .thread_status = gfio_thread_status_op,
585 .group_stats = gfio_group_stats_op,
586 .eta = gfio_update_eta,
587 .probe = gfio_probe_op,
588 .quit = gfio_quit_op,
589 .add_job = gfio_add_job_op,
590 .timed_out = gfio_client_timed_out,
594 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
595 __attribute__((unused)) gpointer data)
600 static void *job_thread(void *arg)
602 fio_handle_clients(&gfio_client_ops);
606 static int send_job_files(struct gui *ui)
610 for (i = 0; i < ui->nr_job_files; i++) {
611 ret = fio_clients_send_ini(ui->job_files[i]);
615 free(ui->job_files[i]);
616 ui->job_files[i] = NULL;
618 while (i < ui->nr_job_files) {
619 free(ui->job_files[i]);
620 ui->job_files[i] = NULL;
627 static void start_job_thread(struct gui *ui)
629 if (send_job_files(ui)) {
630 printf("Yeah, I didn't really like those options too much.\n");
631 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
636 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
639 struct gui *ui = data;
641 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
642 start_job_thread(ui);
645 static void file_open(GtkWidget *w, gpointer data);
647 static void connect_clicked(GtkWidget *widget, gpointer data)
649 struct gui *ui = data;
651 if (!ui->connected) {
652 if (!ui->nr_job_files)
653 file_open(widget, data);
654 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
655 fio_clients_connect();
656 pthread_create(&ui->t, NULL, job_thread, NULL);
657 gfio_set_connected(ui, 1);
659 fio_clients_terminate();
660 gfio_set_connected(ui, 0);
665 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
666 struct button_spec *buttonspec)
668 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
669 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
670 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
671 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
672 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
675 static void add_buttons(struct gui *ui,
676 struct button_spec *buttonlist,
681 for (i = 0; i < nbuttons; i++)
682 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
685 static void on_info_bar_response(GtkWidget *widget, gint response,
688 if (response == GTK_RESPONSE_OK) {
689 gtk_widget_destroy(widget);
690 ui.error_info_bar = NULL;
694 void report_error(GError *error)
696 if (ui.error_info_bar == NULL) {
697 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
700 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
701 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
704 ui.error_label = gtk_label_new(error->message);
705 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
706 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
708 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
709 gtk_widget_show_all(ui.vbox);
712 snprintf(buffer, sizeof(buffer), "Failed to open file.");
713 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
717 static int get_connection_details(char **host, int *port, int *type,
720 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
724 dialog = gtk_dialog_new_with_buttons("Connection details",
725 GTK_WINDOW(ui.window),
726 GTK_DIALOG_DESTROY_WITH_PARENT,
727 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
728 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
730 frame = gtk_frame_new("Hostname / socket name");
731 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
732 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
734 box = gtk_vbox_new(FALSE, 6);
735 gtk_container_add(GTK_CONTAINER(frame), box);
737 hbox = gtk_hbox_new(TRUE, 10);
738 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
739 hentry = gtk_entry_new();
740 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
741 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
743 frame = gtk_frame_new("Port");
744 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
745 box = gtk_vbox_new(FALSE, 10);
746 gtk_container_add(GTK_CONTAINER(frame), box);
748 hbox = gtk_hbox_new(TRUE, 4);
749 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
750 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
752 frame = gtk_frame_new("Type");
753 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
754 box = gtk_vbox_new(FALSE, 10);
755 gtk_container_add(GTK_CONTAINER(frame), box);
757 hbox = gtk_hbox_new(TRUE, 4);
758 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
760 combo = gtk_combo_box_text_new();
761 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
762 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
763 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
764 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
766 gtk_container_add(GTK_CONTAINER(hbox), combo);
768 frame = gtk_frame_new("Options");
769 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
770 box = gtk_vbox_new(FALSE, 10);
771 gtk_container_add(GTK_CONTAINER(frame), box);
773 hbox = gtk_hbox_new(TRUE, 4);
774 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
776 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
777 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
778 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.");
779 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
781 gtk_widget_show_all(dialog);
783 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
784 gtk_widget_destroy(dialog);
788 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
789 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
791 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
792 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
793 *type = Fio_client_ipv4;
794 else if (!strncmp(typeentry, "IPv6", 4))
795 *type = Fio_client_ipv6;
797 *type = Fio_client_socket;
800 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
802 gtk_widget_destroy(dialog);
806 static void file_open(GtkWidget *w, gpointer data)
809 GSList *filenames, *fn_glist;
810 GtkFileFilter *filter;
812 int port, type, server_start;
814 dialog = gtk_file_chooser_dialog_new("Open File",
815 GTK_WINDOW(ui.window),
816 GTK_FILE_CHOOSER_ACTION_OPEN,
817 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
818 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
820 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
822 filter = gtk_file_filter_new();
823 gtk_file_filter_add_pattern(filter, "*.fio");
824 gtk_file_filter_add_pattern(filter, "*.job");
825 gtk_file_filter_add_mime_type(filter, "text/fio");
826 gtk_file_filter_set_name(filter, "Fio job file");
827 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
829 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
830 gtk_widget_destroy(dialog);
834 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
836 gtk_widget_destroy(dialog);
838 if (get_connection_details(&host, &port, &type, &server_start))
841 filenames = fn_glist;
842 while (filenames != NULL) {
843 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
844 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
847 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
851 error = g_error_new(g_quark_from_string("fio"), 1,
852 "Failed to add client %s", host);
856 ui.client->client_data = &ui;
858 g_free(filenames->data);
859 filenames = g_slist_next(filenames);
863 g_slist_free(fn_glist);
866 static void file_save(GtkWidget *w, gpointer data)
870 dialog = gtk_file_chooser_dialog_new("Save File",
871 GTK_WINDOW(ui.window),
872 GTK_FILE_CHOOSER_ACTION_SAVE,
873 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
874 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
877 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
878 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
880 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
883 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
884 // save_job_file(filename);
887 gtk_widget_destroy(dialog);
890 static void preferences(GtkWidget *w, gpointer data)
892 GtkWidget *dialog, *frame, *box, **buttons;
895 dialog = gtk_dialog_new_with_buttons("Preferences",
896 GTK_WINDOW(ui.window),
897 GTK_DIALOG_DESTROY_WITH_PARENT,
898 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
899 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
902 frame = gtk_frame_new("Debug logging");
903 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
904 box = gtk_hbox_new(FALSE, 6);
905 gtk_container_add(GTK_CONTAINER(frame), box);
907 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
909 for (i = 0; i < FD_DEBUG_MAX; i++) {
910 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
911 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
912 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
915 gtk_widget_show_all(dialog);
917 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
918 gtk_widget_destroy(dialog);
922 for (i = 0; i < FD_DEBUG_MAX; i++) {
925 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
927 fio_debug |= (1UL << i);
930 gtk_widget_destroy(dialog);
933 static void about_dialog(GtkWidget *w, gpointer data)
935 gtk_show_about_dialog(NULL,
936 "program-name", "gfio",
937 "comments", "Gtk2 UI for fio",
939 "version", fio_version_string,
940 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
941 "logo-icon-name", "fio",
947 static GtkActionEntry menu_items[] = {
948 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
949 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
950 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
951 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
952 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
953 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
954 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
956 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
958 static const gchar *ui_string = " \
960 <menubar name=\"MainMenu\"> \
961 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
962 <menuitem name=\"Open\" action=\"OpenFile\" /> \
963 <menuitem name=\"Save\" action=\"SaveFile\" /> \
964 <separator name=\"Separator\"/> \
965 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
966 <separator name=\"Separator2\"/> \
967 <menuitem name=\"Quit\" action=\"Quit\" /> \
969 <menu name=\"Help\" action=\"HelpMenuAction\"> \
970 <menuitem name=\"About\" action=\"About\" /> \
976 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
978 GtkActionGroup *action_group = gtk_action_group_new("Menu");
981 action_group = gtk_action_group_new("Menu");
982 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
984 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
985 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
987 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
988 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
991 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
992 GtkWidget *vbox, GtkUIManager *ui_manager)
994 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
997 static void init_ui(int *argc, char **argv[], struct gui *ui)
999 GtkSettings *settings;
1000 GtkUIManager *uimanager;
1001 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1003 memset(ui, 0, sizeof(*ui));
1005 /* Magical g*thread incantation, you just need this thread stuff.
1006 * Without it, the update that happens in gfio_update_thread_status
1007 * doesn't really happen in a timely fashion, you need expose events
1009 if (!g_thread_supported())
1010 g_thread_init(NULL);
1013 gtk_init(argc, argv);
1014 settings = gtk_settings_get_default();
1015 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1018 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1019 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1020 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1022 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1023 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1025 ui->vbox = gtk_vbox_new(FALSE, 0);
1026 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1028 uimanager = gtk_ui_manager_new();
1029 menu = get_menubar_menu(ui->window, uimanager);
1030 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1033 * Set up alignments for widgets at the top of ui,
1034 * align top left, expand horizontally but not vertically
1036 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1037 ui->topvbox = gtk_vbox_new(FALSE, 3);
1038 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1039 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1041 probe = gtk_frame_new("Job");
1042 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1043 probe_frame = gtk_vbox_new(FALSE, 3);
1044 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1046 probe_box = gtk_hbox_new(FALSE, 3);
1047 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1048 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1049 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1050 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1051 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1053 probe_box = gtk_hbox_new(FALSE, 3);
1054 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1056 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
1057 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
1058 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
1059 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
1060 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
1061 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
1063 probe_box = gtk_hbox_new(FALSE, 3);
1064 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1065 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
1066 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
1067 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
1068 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
1071 * Only add this if we have a commit rate
1074 probe_box = gtk_hbox_new(FALSE, 3);
1075 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1077 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1078 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1080 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1081 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1085 * Add a text box for text op messages
1087 ui->textview = gtk_text_view_new();
1088 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1089 gtk_text_buffer_set_text(ui->text, "", -1);
1090 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1091 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1092 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1093 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1094 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1095 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1096 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1100 * Set up alignments for widgets at the bottom of ui,
1101 * align bottom left, expand horizontally but not vertically
1103 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1104 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1105 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1106 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1109 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1112 * Set up thread status progress bar
1114 ui->thread_status_pb = gtk_progress_bar_new();
1115 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1116 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1117 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1120 gtk_widget_show_all(ui->window);
1123 int main(int argc, char *argv[], char *envp[])
1125 if (initialize_fio(envp))
1127 if (fio_init_options())
1130 init_ui(&argc, &argv, &ui);
1132 gdk_threads_enter();
1134 gdk_threads_leave();