#include <malloc.h>
#include <glib.h>
+#include <cairo.h>
#include <gtk/gtk.h>
#include "fio.h"
+#include "graph.h"
static int gfio_server_running;
+static const char *gfio_graph_font;
static void gfio_update_thread_status(char *status_message, double perc);
GtkWidget *buttonbox;
GtkWidget *button[ARRAYSIZE(buttonspeclist)];
GtkWidget *scrolled_window;
- GtkWidget *textview;
+#define DRAWING_AREA_XDIM 1000
+#define DRAWING_AREA_YDIM 400
+ GtkWidget *drawing_area;
GtkWidget *error_info_bar;
GtkWidget *error_label;
GtkWidget *results_notebook;
pthread_t t;
pthread_t server_t;
+ struct graph *iops_graph;
+ struct graph *bandwidth_graph;
struct fio_client *client;
int nr_job_files;
char **job_files;
GtkWidget *disk_util_frame;
};
+static void setup_iops_graph(struct gui *ui)
+{
+ if (ui->iops_graph)
+ graph_free(ui->iops_graph);
+ ui->iops_graph = graph_new(DRAWING_AREA_XDIM / 2.0,
+ DRAWING_AREA_YDIM, gfio_graph_font);
+ graph_title(ui->iops_graph, "IOPS");
+ graph_x_title(ui->iops_graph, "Time");
+ graph_y_title(ui->iops_graph, "IOPS");
+ graph_add_label(ui->iops_graph, "Read IOPS");
+ graph_add_label(ui->iops_graph, "Write IOPS");
+ graph_set_color(ui->iops_graph, "Read IOPS", 0.7, 0.0, 0.0);
+ graph_set_color(ui->iops_graph, "Write IOPS", 0.0, 0.0, 0.7);
+}
+
+static void setup_bandwidth_graph(struct gui *ui)
+{
+ if (ui->bandwidth_graph)
+ graph_free(ui->bandwidth_graph);
+ ui->bandwidth_graph = graph_new(DRAWING_AREA_XDIM / 2.0,
+ DRAWING_AREA_YDIM, gfio_graph_font);
+ graph_title(ui->bandwidth_graph, "Bandwidth");
+ graph_x_title(ui->bandwidth_graph, "Time");
+ graph_y_title(ui->bandwidth_graph, "Bandwidth");
+ graph_add_label(ui->bandwidth_graph, "Read Bandwidth");
+ graph_add_label(ui->bandwidth_graph, "Write Bandwidth");
+ graph_set_color(ui->bandwidth_graph, "Read Bandwidth", 0.7, 0.0, 0.0);
+ graph_set_color(ui->bandwidth_graph, "Write Bandwidth", 0.0, 0.0, 0.7);
+}
+
static void clear_ui_info(struct gui *ui)
{
gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
gdk_threads_leave();
}
+static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
+{
+ struct gui *ui = (struct gui *) p;
+ cairo_t *cr;
+
+ cr = gdk_cairo_create(w->window);
+
+ cairo_set_source_rgb(cr, 0, 0, 0);
+
+ cairo_save(cr);
+ cairo_translate(cr, 0, 0);
+ line_graph_draw(ui->bandwidth_graph, cr);
+ cairo_stroke(cr);
+ cairo_restore(cr);
+
+ cairo_save(cr);
+ cairo_translate(cr, DRAWING_AREA_XDIM / 2.0, 0);
+ // DRAWING_AREA_YDIM * 0.05);
+ line_graph_draw(ui->iops_graph, cr);
+ cairo_stroke(cr);
+ cairo_restore(cr);
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+
static void gfio_update_eta(struct jobs_eta *je)
{
static int eta_good;
gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
+ graph_add_xy_data(ui.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
+ graph_add_xy_data(ui.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
+ graph_add_xy_data(ui.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
+ graph_add_xy_data(ui.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
+
free(rate_str[0]);
free(rate_str[1]);
free(iops_str[0]);
if (!gfio_server_running) {
gfio_server_running = 1;
pthread_create(&ui->server_t, NULL, server_thread, NULL);
+ pthread_detach(ui->server_t);
}
}
if (!ui->nr_job_files)
file_open(widget, data);
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
if (!fio_clients_connect()) {
pthread_create(&ui->t, NULL, job_thread, NULL);
gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
}
}
+struct connection_widgets
+{
+ GtkWidget *hentry;
+ GtkWidget *combo;
+ GtkWidget *button;
+};
+
+static void hostname_cb(GtkEntry *entry, gpointer data)
+{
+ struct connection_widgets *cw = data;
+ int uses_net = 0, is_localhost = 0;
+ const gchar *text;
+ gchar *ctext;
+
+ /*
+ * Check whether to display the 'auto start backend' box
+ * or not. Show it if we are a localhost and using network,
+ * or using a socket.
+ */
+ ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
+ if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
+ uses_net = 1;
+ g_free(ctext);
+
+ if (uses_net) {
+ text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
+ if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
+ !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
+ !strcmp(text, "ip6-loopback"))
+ is_localhost = 1;
+ }
+
+ if (!uses_net || is_localhost) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
+ gtk_widget_set_sensitive(cw->button, 1);
+ } else {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
+ gtk_widget_set_sensitive(cw->button, 0);
+ }
+}
+
static int get_connection_details(char **host, int *port, int *type,
int *server_start)
{
- GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
- GtkWidget *button;
+ GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
+ struct connection_widgets cw;
char *typeentry;
dialog = gtk_dialog_new_with_buttons("Connection details",
hbox = gtk_hbox_new(TRUE, 10);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
- hentry = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
- gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
+ cw.hentry = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
+ gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
frame = gtk_frame_new("Port");
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
hbox = gtk_hbox_new(TRUE, 4);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
- combo = gtk_combo_box_new_text();
- gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
- gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
- gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
- gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
+ cw.combo = gtk_combo_box_new_text();
+ gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
- gtk_container_add(GTK_CONTAINER(hbox), combo);
+ gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
frame = gtk_frame_new("Options");
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
hbox = gtk_hbox_new(TRUE, 4);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
- button = gtk_check_button_new_with_label("Auto-spawn fio backend");
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
- 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.");
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
+ cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
+ gtk_widget_set_tooltip_text(cw.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.");
+ gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
+
+ /*
+ * Connect edit signal, so we can show/not-show the auto start button
+ */
+ g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
+ g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
gtk_widget_show_all(dialog);
return 1;
}
- *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
+ *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
*port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
- typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
+ typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
if (!typeentry || !strncmp(typeentry, "IPv4", 4))
*type = Fio_client_ipv4;
else if (!strncmp(typeentry, "IPv6", 4))
*type = Fio_client_socket;
g_free(typeentry);
- *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
+ *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
gtk_widget_destroy(dialog);
return 0;
filter = gtk_file_filter_new();
gtk_file_filter_add_pattern(filter, "*.fio");
gtk_file_filter_add_pattern(filter, "*.job");
+ gtk_file_filter_add_pattern(filter, "*.ini");
gtk_file_filter_add_mime_type(filter, "text/fio");
gtk_file_filter_set_name(filter, "Fio job file");
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
static void preferences(GtkWidget *w, gpointer data)
{
- GtkWidget *dialog, *frame, *box, **buttons;
+ GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
int i;
dialog = gtk_dialog_new_with_buttons("Preferences",
frame = gtk_frame_new("Debug logging");
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+
+ vbox = gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+
box = gtk_hbox_new(FALSE, 6);
- gtk_container_add(GTK_CONTAINER(frame), box);
+ gtk_container_add(GTK_CONTAINER(vbox), box);
buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
for (i = 0; i < FD_DEBUG_MAX; i++) {
+ if (i == 7) {
+ box = gtk_hbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(vbox), box);
+ }
+
+
buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
}
+ frame = gtk_frame_new("Graph font");
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+ vbox = gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+
+ font = gtk_font_button_new();
+ gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
+
gtk_widget_show_all(dialog);
if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
fio_debug |= (1UL << i);
}
+ gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
+ printf("got font %s\n", gfio_graph_font);
+
gtk_widget_destroy(dialog);
}
ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
- gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
+ gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 700);
g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
#endif
/*
- * Add a text box for text op messages
+ * Set up a drawing area and IOPS and bandwidth graphs
*/
- ui->textview = gtk_text_view_new();
- ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
- gtk_text_buffer_set_text(ui->text, "", -1);
- gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
- gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
+ ui->drawing_area = gtk_drawing_area_new();
+ gtk_widget_set_size_request(GTK_WIDGET(ui->drawing_area),
+ DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
+ g_signal_connect(G_OBJECT(ui->drawing_area), "expose_event",
+ G_CALLBACK (on_expose_drawing_area), ui);
ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
- gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
+ ui->drawing_area);
gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
TRUE, TRUE, 0);
+ setup_iops_graph(ui);
+ setup_bandwidth_graph(ui);
+
/*
* Set up alignments for widgets at the bottom of ui,
* align bottom left, expand horizontally but not vertically