gfio: make empty graph show grid lines, not "No good data"
[fio.git] / gfio.c
diff --git a/gfio.c b/gfio.c
index c7160ada468d139ea110fed0dba786e41af6601a..b1caa6780d51c21d17ddc1cecb0e33ed83a6d42c 100644 (file)
--- a/gfio.c
+++ b/gfio.c
@@ -2,6 +2,7 @@
  * gfio - gui front end for fio - the flexible io tester
  *
  * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> 
+ * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
  *
  * The license below covers all files distributed with fio unless otherwise
  * noted in the file itself.
 #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);
 
@@ -85,22 +91,80 @@ struct gui {
        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;
        GtkWidget *results_window;
+       GtkListStore *log_model;
+       GtkWidget *log_tree;
+       GtkWidget *log_view;
        GtkTextBuffer *text;
        struct probe_widget probe;
        struct eta_widget eta;
        int connected;
        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;
 } ui;
 
+struct gfio_client {
+       struct gui *ui;
+       GtkWidget *results_widget;
+       GtkWidget *disk_util_frame;
+};
+
+static void add_invisible_data(struct graph *g)
+{
+       /*
+        * This puts some invisible data into a graph so that it will
+        * initially have some grid lines instead of "No good data"
+        */
+       graph_add_label(g, "invisible");
+       graph_set_color(g, "invisible", INVISIBLE_COLOR, 0.0, 0.7);
+       graph_add_xy_data(g, "invisible", 0.0, 0.0);
+       graph_add_xy_data(g, "invisible", 1.0, 100.0);
+}
+
+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);
+       add_invisible_data(ui->iops_graph);
+}
+
+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);
+       add_invisible_data(ui->bandwidth_graph);
+}
+
 static void clear_ui_info(struct gui *ui)
 {
        gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
@@ -167,10 +231,12 @@ static void gfio_set_connected(struct gui *ui, int connected)
                gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
                ui->connected = 1;
                gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
+               gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
        } else {
                ui->connected = 0;
                gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
                gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
+               gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
        }
 }
 
@@ -235,6 +301,31 @@ GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char
        return col;
 }
 
+static void gfio_ui_setup_log(struct gui *ui)
+{
+       GtkTreeSelection *selection;
+       GtkListStore *model;
+       GtkWidget *tree_view;
+
+       model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
+
+       tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
+       gtk_widget_set_can_focus(tree_view, FALSE);
+
+       selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+       gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
+       g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
+               "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
+
+       tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
+       tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
+       tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
+       tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
+
+       ui->log_model = model;
+       ui->log_tree = tree_view;
+}
+
 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
                                               fio_fp64_t *plist,
                                               unsigned int len,
@@ -256,6 +347,9 @@ static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
        tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
        gtk_widget_set_can_focus(tree_view, FALSE);
 
+       g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
+               "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
+
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
        gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
 
@@ -268,8 +362,11 @@ static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
 
        gtk_list_store_append(model, &iter);
 
-       for (i = 0; i < len; i++)
+       for (i = 0; i < len; i++) {
+               if (scale)
+                       ovals[i] = (ovals[i] + 999) / 1000;
                gtk_list_store_set(model, &iter, i, ovals[i], -1);
+       }
 
        return tree_view;
 }
@@ -362,10 +459,10 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
 {
        const char *ddir_label[2] = { "Read", "Write" };
        GtkWidget *frame, *label, *box, *vbox, *main_vbox;
-       unsigned long min, max, runt;
+       unsigned long min[3], max[3], runt;
        unsigned long long bw, iops;
        unsigned int flags = 0;
-       double mean, dev;
+       double mean[3], dev[3];
        char *io_p, *bw_p, *iops_p;
        int i2p;
 
@@ -403,22 +500,22 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
        label = new_info_label_in_frame(box, "Runtime (msec)");
        label_set_int_value(label, ts->runtime[ddir]);
 
-       if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
+       if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
                double p_of_agg = 100.0;
                const char *bw_str = "KB";
                char tmp[32];
 
                if (rs->agg[ddir]) {
-                       p_of_agg = mean * 100 / (double) rs->agg[ddir];
+                       p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
                        if (p_of_agg > 100.0)
                                p_of_agg = 100.0;
                }
 
-               if (mean > 999999.9) {
-                       min /= 1000.0;
-                       max /= 1000.0;
-                       mean /= 1000.0;
-                       dev /= 1000.0;
+               if (mean[0] > 999999.9) {
+                       min[0] /= 1000.0;
+                       max[0] /= 1000.0;
+                       mean[0] /= 1000.0;
+                       dev[0] /= 1000.0;
                        bw_str = "MB";
                }
 
@@ -430,25 +527,25 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
                gtk_container_add(GTK_CONTAINER(frame), box);
 
                label = new_info_label_in_frame(box, "Minimum");
-               label_set_int_value(label, min);
+               label_set_int_value(label, min[0]);
                label = new_info_label_in_frame(box, "Maximum");
-               label_set_int_value(label, max);
+               label_set_int_value(label, max[0]);
                label = new_info_label_in_frame(box, "Percentage of jobs");
                sprintf(tmp, "%3.2f%%", p_of_agg);
                gtk_label_set_text(GTK_LABEL(label), tmp);
                label = new_info_label_in_frame(box, "Average");
-               sprintf(tmp, "%5.02f", mean);
+               sprintf(tmp, "%5.02f", mean[0]);
                gtk_label_set_text(GTK_LABEL(label), tmp);
                label = new_info_label_in_frame(box, "Standard deviation");
-               sprintf(tmp, "%5.02f", dev);
+               sprintf(tmp, "%5.02f", dev[0]);
                gtk_label_set_text(GTK_LABEL(label), tmp);
        }
 
-       if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
+       if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
                flags |= GFIO_SLAT;
-       if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
+       if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
                flags |= GFIO_CLAT;
-       if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
+       if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
                flags |= GFIO_LAT;
 
        if (flags) {
@@ -459,11 +556,11 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
                gtk_container_add(GTK_CONTAINER(frame), vbox);
 
                if (flags & GFIO_SLAT)
-                       gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
+                       gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
                if (flags & GFIO_CLAT)
-                       gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
+                       gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
                if (flags & GFIO_LAT)
-                       gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
+                       gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
        }
 
        if (ts->clat_percentiles)
@@ -507,6 +604,9 @@ static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
        tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
        gtk_widget_set_can_focus(tree_view, FALSE);
 
+       g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
+               "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
+
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
        gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
 
@@ -689,6 +789,9 @@ static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
        tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
        gtk_widget_set_can_focus(tree_view, FALSE);
 
+       g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
+               "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
+
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
        gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
 
@@ -736,11 +839,11 @@ static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
                            struct group_run_stats *rs)
 {
        GtkWidget *res_win, *box, *vbox, *entry;
-       struct gui *ui = client->client_data;
+       struct gfio_client *gc = client->client_data;
 
        gdk_threads_enter();
 
-       res_win = get_results_window(ui);
+       res_win = get_results_window(gc->ui);
 
        vbox = gtk_vbox_new(FALSE, 3);
 
@@ -749,6 +852,8 @@ static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
 
        gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
 
+       gc->results_widget = vbox;
+
        entry = new_info_entry_in_frame(box, "Name");
        gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
        if (strlen(ts->description)) {
@@ -773,33 +878,99 @@ static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
        gfio_show_cpu_usage(vbox, ts);
        gfio_show_io_depths(vbox, ts);
 
-       gtk_widget_show_all(ui->results_window);
+       gtk_widget_show_all(gc->ui->results_window);
        gdk_threads_leave();
 }
 
 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
 {
-#if 0
-       GtkTextBuffer *buffer;
-       GtkTextIter end;
+       struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
+       struct gfio_client *gc = client->client_data;
+       GtkTreeIter iter;
+       struct tm *tm;
+       time_t sec;
+       char tmp[64], timebuf[80];
+
+       sec = p->log_sec;
+       tm = localtime(&sec);
+       strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
+       sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
 
-       buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
        gdk_threads_enter();
-       gtk_text_buffer_get_end_iter(buffer, &end);
-       gtk_text_buffer_insert(buffer, &end, buf, -1);
+
+       gtk_list_store_append(gc->ui->log_model, &iter);
+       gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
+       gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
+       gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
+       gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
+
        gdk_threads_leave();
-       gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
-                                       &end, 0.0, FALSE, 0.0,0.0);
-#else
-       fio_client_ops.text_op(client, cmd);
-#endif
 }
 
 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
 {
+       struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
+       struct gfio_client *gc = client->client_data;
+       GtkWidget *box, *frame, *entry, *vbox;
+
        gdk_threads_enter();
-       printf("gfio_disk_util_op called\n");
-       fio_client_ops.disk_util(client, cmd);
+
+       if (!gc->results_widget) {
+               printf("no results!\n");
+               goto out;
+       }
+
+       if (!gc->disk_util_frame) {
+               gc->disk_util_frame = gtk_frame_new("Disk utilization");
+               gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
+       }
+
+       vbox = gtk_vbox_new(FALSE, 3);
+       gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
+
+       frame = gtk_frame_new((char *) p->dus.name);
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
+
+       box = gtk_vbox_new(FALSE, 3);
+       gtk_container_add(GTK_CONTAINER(frame), box);
+
+       frame = gtk_frame_new("Read");
+       gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
+       vbox = gtk_hbox_new(TRUE, 3);
+       gtk_container_add(GTK_CONTAINER(frame), vbox);
+       entry = new_info_entry_in_frame(vbox, "IOs");
+       entry_set_int_value(entry, p->dus.ios[0]);
+       entry = new_info_entry_in_frame(vbox, "Merges");
+       entry_set_int_value(entry, p->dus.merges[0]);
+       entry = new_info_entry_in_frame(vbox, "Sectors");
+       entry_set_int_value(entry, p->dus.sectors[0]);
+       entry = new_info_entry_in_frame(vbox, "Ticks");
+       entry_set_int_value(entry, p->dus.ticks[0]);
+
+       frame = gtk_frame_new("Write");
+       gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
+       vbox = gtk_hbox_new(TRUE, 3);
+       gtk_container_add(GTK_CONTAINER(frame), vbox);
+       entry = new_info_entry_in_frame(vbox, "IOs");
+       entry_set_int_value(entry, p->dus.ios[1]);
+       entry = new_info_entry_in_frame(vbox, "Merges");
+       entry_set_int_value(entry, p->dus.merges[1]);
+       entry = new_info_entry_in_frame(vbox, "Sectors");
+       entry_set_int_value(entry, p->dus.sectors[1]);
+       entry = new_info_entry_in_frame(vbox, "Ticks");
+       entry_set_int_value(entry, p->dus.ticks[1]);
+
+       frame = gtk_frame_new("Shared");
+       gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
+       vbox = gtk_hbox_new(TRUE, 3);
+       gtk_container_add(GTK_CONTAINER(frame), vbox);
+       entry = new_info_entry_in_frame(vbox, "IO ticks");
+       entry_set_int_value(entry, p->dus.io_ticks);
+       entry = new_info_entry_in_frame(vbox, "Time in queue");
+       entry_set_int_value(entry, p->dus.time_in_queue);
+
+       gtk_widget_show_all(gc->results_widget);
+out:
        gdk_threads_leave();
 }
 
@@ -840,6 +1011,32 @@ static void gfio_group_stats_op(struct fio_client *client,
        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;
@@ -907,6 +1104,11 @@ static void gfio_update_eta(struct jobs_eta *je)
                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]);
@@ -926,6 +1128,8 @@ static void gfio_update_eta(struct jobs_eta *je)
 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
 {
        struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
+       struct gfio_client *gc = client->client_data;
+       struct gui *ui = gc->ui;
        const char *os, *arch;
        char buf[64];
 
@@ -942,11 +1146,13 @@ static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
 
        gdk_threads_enter();
 
-       gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
-       gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
-       gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
+       gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
+       gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
+       gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
        sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
-       gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
+       gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
+
+       gfio_set_connected(ui, 1);
 
        gdk_threads_leave();
 }
@@ -966,17 +1172,18 @@ static void gfio_update_thread_status(char *status_message, double perc)
 
 static void gfio_quit_op(struct fio_client *client)
 {
-       struct gui *ui = client->client_data;
+       struct gfio_client *gc = client->client_data;
 
        gdk_threads_enter();
-       gfio_set_connected(ui, 0);
+       gfio_set_connected(gc->ui, 0);
        gdk_threads_leave();
 }
 
 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
 {
        struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
-       struct gui *ui = client->client_data;
+       struct gfio_client *gc = client->client_data;
+       struct gui *ui = gc->ui;
        char tmp[8];
        int i;
 
@@ -1005,19 +1212,19 @@ static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
 
 static void gfio_client_timed_out(struct fio_client *client)
 {
-       struct gui *ui = client->client_data;
+       struct gfio_client *gc = client->client_data;
        GtkWidget *dialog, *label, *content;
        char buf[256];
 
        gdk_threads_enter();
 
-       gfio_set_connected(ui, 0);
-       clear_ui_info(ui);
+       gfio_set_connected(gc->ui, 0);
+       clear_ui_info(gc->ui);
 
        sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
 
        dialog = gtk_dialog_new_with_buttons("Timed out!",
-                       GTK_WINDOW(ui->window),
+                       GTK_WINDOW(gc->ui->window),
                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                        GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
 
@@ -1088,6 +1295,24 @@ static void start_job_thread(struct gui *ui)
        }
 }
 
+static void *server_thread(void *arg)
+{
+       is_backend = 1;
+       gfio_server_running = 1;
+       fio_start_server(NULL);
+       gfio_server_running = 0;
+       return NULL;
+}
+
+static void gfio_start_server(struct gui *ui)
+{
+       if (!gfio_server_running) {
+               gfio_server_running = 1;
+               pthread_create(&ui->server_t, NULL, server_thread, NULL);
+               pthread_detach(ui->server_t);
+       }
+}
+
 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
                 gpointer data)
 {
@@ -1107,9 +1332,11 @@ static void connect_clicked(GtkWidget *widget, gpointer data)
                if (!ui->nr_job_files)
                        file_open(widget, data);
                gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
-               fio_clients_connect();
-               pthread_create(&ui->t, NULL, job_thread, NULL);
-               gfio_set_connected(ui, 1);
+               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);
+               }
        } else {
                fio_clients_terminate();
                gfio_set_connected(ui, 0);
@@ -1169,11 +1396,52 @@ void report_error(GError *error)
        }
 }
 
+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",
@@ -1191,9 +1459,9 @@ static int get_connection_details(char **host, int *port, int *type,
 
        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);
@@ -1212,13 +1480,13 @@ static int get_connection_details(char **host, int *port, int *type,
        hbox = gtk_hbox_new(TRUE, 4);
        gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
 
-       combo = gtk_combo_box_text_new();
-       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
-       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
-       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(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);
@@ -1228,10 +1496,16 @@ static int get_connection_details(char **host, int *port, int *type,
        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);
 
@@ -1240,10 +1514,10 @@ static int get_connection_details(char **host, int *port, int *type,
                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_text_get_active_text(GTK_COMBO_BOX_TEXT(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))
@@ -1252,22 +1526,34 @@ static int get_connection_details(char **host, int *port, int *type,
                *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;
 }
 
+static void gfio_client_added(struct gui *ui, struct fio_client *client)
+{
+       struct gfio_client *gc;
+
+       gc = malloc(sizeof(*gc));
+       memset(gc, 0, sizeof(*gc));
+       gc->ui = ui;
+
+       client->client_data = gc;
+}
+
 static void file_open(GtkWidget *w, gpointer data)
 {
        GtkWidget *dialog;
+       struct gui *ui = data;
        GSList *filenames, *fn_glist;
        GtkFileFilter *filter;
        char *host;
        int port, type, server_start;
 
        dialog = gtk_file_chooser_dialog_new("Open File",
-               GTK_WINDOW(ui.window),
+               GTK_WINDOW(ui->window),
                GTK_FILE_CHOOSER_ACTION_OPEN,
                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
@@ -1277,6 +1563,7 @@ static void file_open(GtkWidget *w, gpointer data)
        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);
@@ -1295,12 +1582,14 @@ static void file_open(GtkWidget *w, gpointer data)
 
        filenames = fn_glist;
        while (filenames != NULL) {
-               ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
-               ui.job_files[ui.nr_job_files] = strdup(filenames->data);
-               ui.nr_job_files++;
+               struct fio_client *client;
+
+               ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
+               ui->job_files[ui->nr_job_files] = strdup(filenames->data);
+               ui->nr_job_files++;
 
-               ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
-               if (!ui.client) {
+               client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
+               if (!client) {
                        GError *error;
 
                        error = g_error_new(g_quark_from_string("fio"), 1,
@@ -1308,22 +1597,26 @@ static void file_open(GtkWidget *w, gpointer data)
                        report_error(error);
                        g_error_free(error);
                }
-               ui.client->client_data = &ui;
+               gfio_client_added(ui, client);
                        
                g_free(filenames->data);
                filenames = g_slist_next(filenames);
        }
        free(host);
+
+       if (server_start)
+               gfio_start_server(ui);
 err:
        g_slist_free(fn_glist);
 }
 
 static void file_save(GtkWidget *w, gpointer data)
 {
+       struct gui *ui = data;
        GtkWidget *dialog;
 
        dialog = gtk_file_chooser_dialog_new("Save File",
-               GTK_WINDOW(ui.window),
+               GTK_WINDOW(ui->window),
                GTK_FILE_CHOOSER_ACTION_SAVE,
                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
@@ -1342,9 +1635,49 @@ static void file_save(GtkWidget *w, gpointer data)
        gtk_widget_destroy(dialog);
 }
 
+static void view_log_destroy(GtkWidget *w, gpointer data)
+{
+       struct gui *ui = (struct gui *) data;
+
+       gtk_widget_ref(ui->log_tree);
+       gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
+       gtk_widget_destroy(w);
+       ui->log_view = NULL;
+}
+
+static void view_log(GtkWidget *w, gpointer data)
+{
+       GtkWidget *win, *scroll, *vbox, *box;
+       struct gui *ui = (struct gui *) data;
+
+       if (ui->log_view)
+               return;
+
+       ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       gtk_window_set_title(GTK_WINDOW(win), "Log");
+       gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
+
+       scroll = gtk_scrolled_window_new(NULL, NULL);
+
+       gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
+
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+       box = gtk_hbox_new(TRUE, 0);
+       gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
+       g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
+       gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
+
+       vbox = gtk_vbox_new(TRUE, 5);
+       gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
+
+       gtk_container_add(GTK_CONTAINER(win), vbox);
+       gtk_widget_show_all(win);
+}
+
 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",
@@ -1356,17 +1689,35 @@ static void preferences(GtkWidget *w, gpointer data)
 
        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) {
@@ -1382,29 +1733,61 @@ static void preferences(GtkWidget *w, gpointer data)
                        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);
 }
 
 static void about_dialog(GtkWidget *w, gpointer data)
 {
+       const char *authors[] = {
+               "Jens Axboe <axboe@kernel.dk>",
+               "Stephen Carmeron <stephenmcameron@gmail.com>",
+               NULL
+       };
+       const char *license[] = {
+               "Fio is free software; you can redistribute it and/or modify "
+               "it under the terms of the GNU General Public License as published by "
+               "the Free Software Foundation; either version 2 of the License, or "
+               "(at your option) any later version.\n",
+               "Fio is distributed in the hope that it will be useful, "
+               "but WITHOUT ANY WARRANTY; without even the implied warranty of "
+               "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
+               "GNU General Public License for more details.\n",
+               "You should have received a copy of the GNU General Public License "
+               "along with Fio; if not, write to the Free Software Foundation, Inc., "
+               "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA\n"
+       };
+       char *license_trans;
+
+       license_trans = g_strconcat(license[0], "\n", license[1], "\n",
+                                    license[2], "\n", NULL);
+
        gtk_show_about_dialog(NULL,
                "program-name", "gfio",
                "comments", "Gtk2 UI for fio",
-               "license", "GPLv2",
+               "license", license_trans,
+               "website", "http://git.kernel.dk/?p=fio.git;a=summary",
+               "authors", authors,
                "version", fio_version_string,
-               "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
+               "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
                "logo-icon-name", "fio",
                /* Must be last: */
-               NULL, NULL,
+               "wrap-license", TRUE,
                NULL);
+
+       g_free (license_trans);
 }
 
 static GtkActionEntry menu_items[] = {
        { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
+       { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
        { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
        { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
        { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
        { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
+       { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
        { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
        { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
 };
@@ -1421,6 +1804,9 @@ static const gchar *ui_string = " \
                                <separator name=\"Separator2\"/> \
                                <menuitem name=\"Quit\" action=\"Quit\" /> \
                        </menu> \
+                       <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
+                               <menuitem name=\"Log\" action=\"ViewLog\" /> \
+                       </menu>\
                        <menu name=\"Help\" action=\"HelpMenuAction\"> \
                                <menuitem name=\"About\" action=\"About\" /> \
                        </menu> \
@@ -1428,13 +1814,14 @@ static const gchar *ui_string = " \
        </ui> \
 ";
 
-static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
+static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
+                                  struct gui *ui)
 {
        GtkActionGroup *action_group = gtk_action_group_new("Menu");
        GError *error = 0;
 
        action_group = gtk_action_group_new("Menu");
-       gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
+       gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
 
        gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
        gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
@@ -1454,6 +1841,7 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
        GtkSettings *settings;
        GtkUIManager *uimanager;
        GtkWidget *menu, *probe, *probe_frame, *probe_box;
+       GdkColor white;
 
        memset(ui, 0, sizeof(*ui));
 
@@ -1472,7 +1860,7 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
        
        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);
@@ -1481,7 +1869,7 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
        gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
 
        uimanager = gtk_ui_manager_new();
-       menu = get_menubar_menu(ui->window, uimanager);
+       menu = get_menubar_menu(ui->window, uimanager, ui);
        gfio_ui_setup(settings, menu, ui->vbox, uimanager);
 
        /*
@@ -1537,20 +1925,26 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
 #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);
+       gdk_color_parse("white", &white);
+       ui->drawing_area = gtk_drawing_area_new();
+       gtk_widget_set_size_request(GTK_WIDGET(ui->drawing_area), 
+                       DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
+       gtk_widget_modify_bg(ui->drawing_area, GTK_STATE_NORMAL, &white);
+       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
@@ -1571,6 +1965,7 @@ static void init_ui(int *argc, char **argv[], struct gui *ui)
        gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
        gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
 
+       gfio_ui_setup_log(ui);
 
        gtk_widget_show_all(ui->window);
 }