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_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
127 gtk_container_add(GTK_CONTAINER(frame), entry);
132 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
134 GtkWidget *label_widget;
137 frame = gtk_frame_new(label);
138 label_widget = gtk_label_new(NULL);
139 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
140 gtk_container_add(GTK_CONTAINER(frame), label_widget);
145 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
147 GtkWidget *button, *box;
149 box = gtk_hbox_new(FALSE, 3);
150 gtk_container_add(GTK_CONTAINER(hbox), box);
152 button = gtk_spin_button_new_with_range(min, max, 1.0);
153 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
155 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
156 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
161 static void gfio_set_connected(struct gui *ui, int connected)
164 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
166 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
169 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
170 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
174 static void label_set_int_value(GtkWidget *entry, unsigned int val)
178 sprintf(tmp, "%u", val);
179 gtk_label_set_text(GTK_LABEL(entry), tmp);
182 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
186 sprintf(tmp, "%u", val);
187 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
190 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
191 unsigned long max, double mean, double dev)
193 const char *base = "(usec)";
194 GtkWidget *hbox, *label, *frame, *box;
198 if (!usec_to_msec(&min, &max, &mean, &dev))
201 minp = num2str(min, 6, 1, 0);
202 maxp = num2str(max, 6, 1, 0);
204 printf("adding %s\n", name);
206 sprintf(tmp, "%s %s", name, base);
207 frame = gtk_frame_new(tmp);
208 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
210 box = gtk_vbox_new(FALSE, 3);
211 gtk_container_add(GTK_CONTAINER(frame), box);
213 hbox = gtk_hbox_new(FALSE, 3);
214 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, FALSE, 3);
216 label = new_info_label_in_frame(hbox, "Minimum");
217 gtk_label_set_text(GTK_LABEL(label), minp);
218 label = new_info_label_in_frame(hbox, "Maximum");
219 gtk_label_set_text(GTK_LABEL(label), maxp);
220 label = new_info_label_in_frame(hbox, "Average");
221 sprintf(tmp, "%5.02f", mean);
222 gtk_label_set_text(GTK_LABEL(label), tmp);
223 label = new_info_label_in_frame(hbox, "Standard deviation");
224 sprintf(tmp, "%5.02f", dev);
225 gtk_label_set_text(GTK_LABEL(label), tmp);
232 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
233 struct thread_stat *ts, int ddir)
235 const char *ddir_label[2] = { "Read", "Write" };
236 GtkWidget *frame, *label, *box, *vbox;
237 unsigned long min, max, runt;
238 unsigned long long bw, iops;
240 char *io_p, *bw_p, *iops_p;
243 if (!ts->runtime[ddir])
246 i2p = is_power_of_2(rs->kb_base);
247 runt = ts->runtime[ddir];
249 bw = (1000 * ts->io_bytes[ddir]) / runt;
250 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
251 bw_p = num2str(bw, 6, 1, i2p);
253 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
254 iops_p = num2str(iops, 6, 1, 0);
256 box = gtk_hbox_new(FALSE, 3);
257 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
259 frame = gtk_frame_new(ddir_label[ddir]);
260 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
262 vbox = gtk_vbox_new(FALSE, 3);
263 gtk_container_add(GTK_CONTAINER(frame), vbox);
265 box = gtk_hbox_new(FALSE, 3);
266 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 3);
268 label = new_info_label_in_frame(box, "IO");
269 gtk_label_set_text(GTK_LABEL(label), io_p);
270 label = new_info_label_in_frame(box, "Bandwidth");
271 gtk_label_set_text(GTK_LABEL(label), bw_p);
272 label = new_info_label_in_frame(box, "IOPS");
273 gtk_label_set_text(GTK_LABEL(label), iops_p);
274 label = new_info_label_in_frame(box, "Runtime (msec)");
275 label_set_int_value(label, ts->runtime[ddir]);
277 frame = gtk_frame_new("Latency");
278 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
280 vbox = gtk_vbox_new(FALSE, 3);
281 gtk_container_add(GTK_CONTAINER(frame), vbox);
283 if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
284 gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
285 if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
286 gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
287 if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
288 gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
295 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
296 struct group_run_stats *rs)
298 GtkWidget *dialog, *box, *vbox, *entry, *content;
299 struct gui *ui = client->client_data;
303 dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
304 GTK_DIALOG_DESTROY_WITH_PARENT,
305 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
307 g_signal_connect_swapped(dialog, "response",
308 G_CALLBACK(gtk_widget_destroy),
311 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
313 vbox = gtk_vbox_new(FALSE, 3);
314 gtk_container_add(GTK_CONTAINER(content), vbox);
316 box = gtk_hbox_new(TRUE, 3);
317 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
319 entry = new_info_entry_in_frame(box, "Name");
320 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
321 if (strlen(ts->description)) {
322 entry = new_info_entry_in_frame(box, "Description");
323 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
325 entry = new_info_entry_in_frame(box, "Group ID");
326 entry_set_int_value(entry, ts->groupid);
327 entry = new_info_entry_in_frame(box, "Jobs");
328 entry_set_int_value(entry, ts->members);
329 entry = new_info_entry_in_frame(box, "Error");
330 entry_set_int_value(entry, ts->error);
331 entry = new_info_entry_in_frame(box, "PID");
332 entry_set_int_value(entry, ts->pid);
334 if (ts->io_bytes[DDIR_READ])
335 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
336 if (ts->io_bytes[DDIR_WRITE])
337 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
339 gtk_widget_show_all(dialog);
344 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
347 GtkTextBuffer *buffer;
350 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
352 gtk_text_buffer_get_end_iter(buffer, &end);
353 gtk_text_buffer_insert(buffer, &end, buf, -1);
355 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
356 &end, 0.0, FALSE, 0.0,0.0);
358 fio_client_ops.text_op(client, cmd);
362 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
364 printf("gfio_disk_util_op called\n");
365 fio_client_ops.disk_util(client, cmd);
368 extern int sum_stat_clients;
369 extern struct thread_stat client_ts;
370 extern struct group_run_stats client_gs;
372 static int sum_stat_nr;
374 static void gfio_thread_status_op(struct fio_client *client,
375 struct fio_net_cmd *cmd)
377 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
379 gfio_display_ts(client, &p->ts, &p->rs);
381 if (sum_stat_clients == 1)
384 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
385 sum_group_stats(&client_gs, &p->rs);
388 client_ts.groupid = p->ts.groupid;
390 if (++sum_stat_nr == sum_stat_clients) {
391 strcpy(client_ts.name, "All clients");
392 gfio_display_ts(client, &client_ts, &client_gs);
396 static void gfio_group_stats_op(struct fio_client *client,
397 struct fio_net_cmd *cmd)
399 printf("gfio_group_stats_op called\n");
400 fio_client_ops.group_stats(client, cmd);
403 static void gfio_update_eta(struct jobs_eta *je)
415 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
416 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
417 eta_to_str(eta_str, je->eta_sec);
420 sprintf(tmp, "%u", je->nr_running);
421 gtk_label_set_text(GTK_LABEL(ui.eta.jobs), tmp);
422 sprintf(tmp, "%u", je->files_open);
423 gtk_label_set_text(GTK_LABEL(ui.eta.files), tmp);
426 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
427 if (je->m_rate || je->t_rate) {
430 mr = num2str(je->m_rate, 4, 0, i2p);
431 tr = num2str(je->t_rate, 4, 0, i2p);
432 gtk_label_set_text(GTK_LABEL(ui.eta.
433 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
436 } else if (je->m_iops || je->t_iops)
437 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
439 gtk_label_set_text(GTK_LABEL(ui.eta.cr_bw), "---");
440 gtk_label_set_text(GTK_LABEL(ui.eta.cr_iops), "---");
441 gtk_label_set_text(GTK_LABEL(ui.eta.cw_bw), "---");
442 gtk_label_set_text(GTK_LABEL(ui.eta.cw_iops), "---");
445 if (je->eta_sec != INT_MAX && je->nr_running) {
449 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
450 strcpy(output, "-.-% done");
454 sprintf(output, "%3.1f%% done", perc);
457 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
458 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
460 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
461 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
463 gtk_label_set_text(GTK_LABEL(ui.eta.read_bw), rate_str[0]);
464 gtk_label_set_text(GTK_LABEL(ui.eta.read_iops), iops_str[0]);
465 gtk_label_set_text(GTK_LABEL(ui.eta.write_bw), rate_str[1]);
466 gtk_label_set_text(GTK_LABEL(ui.eta.write_iops), iops_str[1]);
475 char *dst = output + strlen(output);
477 sprintf(dst, " - %s", eta_str);
480 gfio_update_thread_status(output, perc);
483 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
485 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
486 const char *os, *arch;
489 os = fio_get_os_string(probe->os);
493 arch = fio_get_arch_string(probe->arch);
498 client->name = strdup((char *) probe->hostname);
500 gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
501 gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
502 gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
503 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
504 gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
507 static void gfio_update_thread_status(char *status_message, double perc)
509 static char message[100];
510 const char *m = message;
512 strncpy(message, status_message, sizeof(message) - 1);
513 gtk_progress_bar_set_text(
514 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
515 gtk_progress_bar_set_fraction(
516 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
518 gtk_widget_queue_draw(ui.window);
522 static void gfio_quit_op(struct fio_client *client)
524 struct gui *ui = client->client_data;
526 gfio_set_connected(ui, 0);
529 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
531 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
532 struct gui *ui = client->client_data;
536 p->iodepth = le32_to_cpu(p->iodepth);
537 p->rw = le32_to_cpu(p->rw);
539 for (i = 0; i < 2; i++) {
540 p->min_bs[i] = le32_to_cpu(p->min_bs[i]);
541 p->max_bs[i] = le32_to_cpu(p->max_bs[i]);
544 p->numjobs = le32_to_cpu(p->numjobs);
545 p->group_reporting = le32_to_cpu(p->group_reporting);
547 gtk_label_set_text(GTK_LABEL(ui->eta.name), (gchar *) p->jobname);
548 gtk_label_set_text(GTK_LABEL(ui->eta.iotype), ddir_str(p->rw));
549 gtk_label_set_text(GTK_LABEL(ui->eta.ioengine), (gchar *) p->ioengine);
551 sprintf(tmp, "%u", p->iodepth);
552 gtk_label_set_text(GTK_LABEL(ui->eta.iodepth), tmp);
555 static void gfio_client_timed_out(struct fio_client *client)
557 struct gui *ui = client->client_data;
558 GtkWidget *dialog, *label, *content;
563 gfio_set_connected(ui, 0);
566 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
568 dialog = gtk_dialog_new_with_buttons("Timed out!",
569 GTK_WINDOW(ui->window),
570 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
571 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
573 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
574 label = gtk_label_new((const gchar *) buf);
575 gtk_container_add(GTK_CONTAINER(content), label);
576 gtk_widget_show_all(dialog);
577 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
579 gtk_dialog_run(GTK_DIALOG(dialog));
580 gtk_widget_destroy(dialog);
585 struct client_ops gfio_client_ops = {
586 .text_op = gfio_text_op,
587 .disk_util = gfio_disk_util_op,
588 .thread_status = gfio_thread_status_op,
589 .group_stats = gfio_group_stats_op,
590 .eta = gfio_update_eta,
591 .probe = gfio_probe_op,
592 .quit = gfio_quit_op,
593 .add_job = gfio_add_job_op,
594 .timed_out = gfio_client_timed_out,
598 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
599 __attribute__((unused)) gpointer data)
604 static void *job_thread(void *arg)
606 fio_handle_clients(&gfio_client_ops);
610 static int send_job_files(struct gui *ui)
614 for (i = 0; i < ui->nr_job_files; i++) {
615 ret = fio_clients_send_ini(ui->job_files[i]);
619 free(ui->job_files[i]);
620 ui->job_files[i] = NULL;
622 while (i < ui->nr_job_files) {
623 free(ui->job_files[i]);
624 ui->job_files[i] = NULL;
631 static void start_job_thread(struct gui *ui)
633 if (send_job_files(ui)) {
634 printf("Yeah, I didn't really like those options too much.\n");
635 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
640 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
643 struct gui *ui = data;
645 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
646 start_job_thread(ui);
649 static void file_open(GtkWidget *w, gpointer data);
651 static void connect_clicked(GtkWidget *widget, gpointer data)
653 struct gui *ui = data;
655 if (!ui->connected) {
656 if (!ui->nr_job_files)
657 file_open(widget, data);
658 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
659 fio_clients_connect();
660 pthread_create(&ui->t, NULL, job_thread, NULL);
661 gfio_set_connected(ui, 1);
663 fio_clients_terminate();
664 gfio_set_connected(ui, 0);
669 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
670 struct button_spec *buttonspec)
672 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
673 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
674 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
675 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
676 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
679 static void add_buttons(struct gui *ui,
680 struct button_spec *buttonlist,
685 for (i = 0; i < nbuttons; i++)
686 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
689 static void on_info_bar_response(GtkWidget *widget, gint response,
692 if (response == GTK_RESPONSE_OK) {
693 gtk_widget_destroy(widget);
694 ui.error_info_bar = NULL;
698 void report_error(GError *error)
700 if (ui.error_info_bar == NULL) {
701 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
704 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
705 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
708 ui.error_label = gtk_label_new(error->message);
709 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
710 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
712 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
713 gtk_widget_show_all(ui.vbox);
716 snprintf(buffer, sizeof(buffer), "Failed to open file.");
717 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
721 static int get_connection_details(char **host, int *port, int *type,
724 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
728 dialog = gtk_dialog_new_with_buttons("Connection details",
729 GTK_WINDOW(ui.window),
730 GTK_DIALOG_DESTROY_WITH_PARENT,
731 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
732 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
734 frame = gtk_frame_new("Hostname / socket name");
735 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
736 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
738 box = gtk_vbox_new(FALSE, 6);
739 gtk_container_add(GTK_CONTAINER(frame), box);
741 hbox = gtk_hbox_new(TRUE, 10);
742 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
743 hentry = gtk_entry_new();
744 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
745 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
747 frame = gtk_frame_new("Port");
748 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
749 box = gtk_vbox_new(FALSE, 10);
750 gtk_container_add(GTK_CONTAINER(frame), box);
752 hbox = gtk_hbox_new(TRUE, 4);
753 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
754 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
756 frame = gtk_frame_new("Type");
757 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
758 box = gtk_vbox_new(FALSE, 10);
759 gtk_container_add(GTK_CONTAINER(frame), box);
761 hbox = gtk_hbox_new(TRUE, 4);
762 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
764 combo = gtk_combo_box_text_new();
765 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
766 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
767 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
768 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
770 gtk_container_add(GTK_CONTAINER(hbox), combo);
772 frame = gtk_frame_new("Options");
773 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
774 box = gtk_vbox_new(FALSE, 10);
775 gtk_container_add(GTK_CONTAINER(frame), box);
777 hbox = gtk_hbox_new(TRUE, 4);
778 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
780 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
781 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
782 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.");
783 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
785 gtk_widget_show_all(dialog);
787 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
788 gtk_widget_destroy(dialog);
792 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
793 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
795 typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
796 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
797 *type = Fio_client_ipv4;
798 else if (!strncmp(typeentry, "IPv6", 4))
799 *type = Fio_client_ipv6;
801 *type = Fio_client_socket;
804 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
806 gtk_widget_destroy(dialog);
810 static void file_open(GtkWidget *w, gpointer data)
813 GSList *filenames, *fn_glist;
814 GtkFileFilter *filter;
816 int port, type, server_start;
818 dialog = gtk_file_chooser_dialog_new("Open File",
819 GTK_WINDOW(ui.window),
820 GTK_FILE_CHOOSER_ACTION_OPEN,
821 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
822 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
824 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
826 filter = gtk_file_filter_new();
827 gtk_file_filter_add_pattern(filter, "*.fio");
828 gtk_file_filter_add_pattern(filter, "*.job");
829 gtk_file_filter_add_mime_type(filter, "text/fio");
830 gtk_file_filter_set_name(filter, "Fio job file");
831 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
833 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
834 gtk_widget_destroy(dialog);
838 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
840 gtk_widget_destroy(dialog);
842 if (get_connection_details(&host, &port, &type, &server_start))
845 filenames = fn_glist;
846 while (filenames != NULL) {
847 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
848 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
851 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
855 error = g_error_new(g_quark_from_string("fio"), 1,
856 "Failed to add client %s", host);
860 ui.client->client_data = &ui;
862 g_free(filenames->data);
863 filenames = g_slist_next(filenames);
867 g_slist_free(fn_glist);
870 static void file_save(GtkWidget *w, gpointer data)
874 dialog = gtk_file_chooser_dialog_new("Save File",
875 GTK_WINDOW(ui.window),
876 GTK_FILE_CHOOSER_ACTION_SAVE,
877 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
878 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
881 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
882 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
884 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
887 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
888 // save_job_file(filename);
891 gtk_widget_destroy(dialog);
894 static void preferences(GtkWidget *w, gpointer data)
896 GtkWidget *dialog, *frame, *box, **buttons;
899 dialog = gtk_dialog_new_with_buttons("Preferences",
900 GTK_WINDOW(ui.window),
901 GTK_DIALOG_DESTROY_WITH_PARENT,
902 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
903 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
906 frame = gtk_frame_new("Debug logging");
907 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
908 box = gtk_hbox_new(FALSE, 6);
909 gtk_container_add(GTK_CONTAINER(frame), box);
911 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
913 for (i = 0; i < FD_DEBUG_MAX; i++) {
914 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
915 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
916 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
919 gtk_widget_show_all(dialog);
921 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
922 gtk_widget_destroy(dialog);
926 for (i = 0; i < FD_DEBUG_MAX; i++) {
929 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
931 fio_debug |= (1UL << i);
934 gtk_widget_destroy(dialog);
937 static void about_dialog(GtkWidget *w, gpointer data)
939 gtk_show_about_dialog(NULL,
940 "program-name", "gfio",
941 "comments", "Gtk2 UI for fio",
943 "version", fio_version_string,
944 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
945 "logo-icon-name", "fio",
951 static GtkActionEntry menu_items[] = {
952 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
953 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
954 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
955 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
956 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
957 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
958 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
960 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
962 static const gchar *ui_string = " \
964 <menubar name=\"MainMenu\"> \
965 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
966 <menuitem name=\"Open\" action=\"OpenFile\" /> \
967 <menuitem name=\"Save\" action=\"SaveFile\" /> \
968 <separator name=\"Separator\"/> \
969 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
970 <separator name=\"Separator2\"/> \
971 <menuitem name=\"Quit\" action=\"Quit\" /> \
973 <menu name=\"Help\" action=\"HelpMenuAction\"> \
974 <menuitem name=\"About\" action=\"About\" /> \
980 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
982 GtkActionGroup *action_group = gtk_action_group_new("Menu");
985 action_group = gtk_action_group_new("Menu");
986 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
988 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
989 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
991 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
992 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
995 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
996 GtkWidget *vbox, GtkUIManager *ui_manager)
998 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1001 static void init_ui(int *argc, char **argv[], struct gui *ui)
1003 GtkSettings *settings;
1004 GtkUIManager *uimanager;
1005 GtkWidget *menu, *probe, *probe_frame, *probe_box;
1007 memset(ui, 0, sizeof(*ui));
1009 /* Magical g*thread incantation, you just need this thread stuff.
1010 * Without it, the update that happens in gfio_update_thread_status
1011 * doesn't really happen in a timely fashion, you need expose events
1013 if (!g_thread_supported())
1014 g_thread_init(NULL);
1017 gtk_init(argc, argv);
1018 settings = gtk_settings_get_default();
1019 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1022 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1023 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1024 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1026 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1027 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1029 ui->vbox = gtk_vbox_new(FALSE, 0);
1030 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1032 uimanager = gtk_ui_manager_new();
1033 menu = get_menubar_menu(ui->window, uimanager);
1034 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1037 * Set up alignments for widgets at the top of ui,
1038 * align top left, expand horizontally but not vertically
1040 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1041 ui->topvbox = gtk_vbox_new(FALSE, 3);
1042 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1043 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1045 probe = gtk_frame_new("Job");
1046 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1047 probe_frame = gtk_vbox_new(FALSE, 3);
1048 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1050 probe_box = gtk_hbox_new(FALSE, 3);
1051 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1052 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1053 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1054 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1055 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1057 probe_box = gtk_hbox_new(FALSE, 3);
1058 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1060 ui->eta.name = new_info_label_in_frame(probe_box, "Name");
1061 ui->eta.iotype = new_info_label_in_frame(probe_box, "IO");
1062 ui->eta.ioengine = new_info_label_in_frame(probe_box, "IO Engine");
1063 ui->eta.iodepth = new_info_label_in_frame(probe_box, "IO Depth");
1064 ui->eta.jobs = new_info_label_in_frame(probe_box, "Jobs");
1065 ui->eta.files = new_info_label_in_frame(probe_box, "Open files");
1067 probe_box = gtk_hbox_new(FALSE, 3);
1068 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1069 ui->eta.read_bw = new_info_label_in_frame(probe_box, "Read BW");
1070 ui->eta.read_iops = new_info_label_in_frame(probe_box, "IOPS");
1071 ui->eta.write_bw = new_info_label_in_frame(probe_box, "Write BW");
1072 ui->eta.write_iops = new_info_label_in_frame(probe_box, "IOPS");
1075 * Only add this if we have a commit rate
1078 probe_box = gtk_hbox_new(FALSE, 3);
1079 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1081 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1082 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1084 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1085 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1089 * Add a text box for text op messages
1091 ui->textview = gtk_text_view_new();
1092 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1093 gtk_text_buffer_set_text(ui->text, "", -1);
1094 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1095 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1096 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1097 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1098 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1099 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1100 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1104 * Set up alignments for widgets at the bottom of ui,
1105 * align bottom left, expand horizontally but not vertically
1107 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1108 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1109 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1110 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1113 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1116 * Set up thread status progress bar
1118 ui->thread_status_pb = gtk_progress_bar_new();
1119 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1120 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1121 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1124 gtk_widget_show_all(ui->window);
1127 int main(int argc, char *argv[], char *envp[])
1129 if (initialize_fio(envp))
1131 if (fio_init_options())
1134 init_ui(&argc, &argv, &ui);
1136 gdk_threads_enter();
1138 gdk_threads_leave();