gfio: put results from same job file into a notebook
[fio.git] / gfio.c
diff --git a/gfio.c b/gfio.c
index 3602aa335b8f95596bf7ca1582e2c38af109635c..c7160ada468d139ea110fed0dba786e41af6601a 100644 (file)
--- a/gfio.c
+++ b/gfio.c
@@ -88,6 +88,8 @@ struct gui {
        GtkWidget *textview;
        GtkWidget *error_info_bar;
        GtkWidget *error_label;
+       GtkWidget *results_notebook;
+       GtkWidget *results_window;
        GtkTextBuffer *text;
        struct probe_widget probe;
        struct eta_widget eta;
@@ -401,31 +403,6 @@ 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->slat_stat[ddir], &min, &max, &mean, &dev))
-               flags |= GFIO_SLAT;
-       if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
-               flags |= GFIO_CLAT;
-       if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
-               flags |= GFIO_LAT;
-
-       if (flags) {
-               frame = gtk_frame_new("Latency");
-               gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
-
-               vbox = gtk_vbox_new(FALSE, 3);
-               gtk_container_add(GTK_CONTAINER(frame), vbox);
-
-               if (flags & GFIO_SLAT)
-                       gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
-               if (flags & GFIO_CLAT)
-                       gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
-               if (flags & GFIO_LAT)
-                       gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
-       }
-
-       if (ts->clat_percentiles)
-               gfio_show_clat_percentiles(main_vbox, ts, ddir);
-
        if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
                double p_of_agg = 100.0;
                const char *bw_str = "KB";
@@ -467,35 +444,311 @@ static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
                gtk_label_set_text(GTK_LABEL(label), tmp);
        }
 
+       if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
+               flags |= GFIO_SLAT;
+       if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
+               flags |= GFIO_CLAT;
+       if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
+               flags |= GFIO_LAT;
+
+       if (flags) {
+               frame = gtk_frame_new("Latency");
+               gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
+
+               vbox = gtk_vbox_new(FALSE, 3);
+               gtk_container_add(GTK_CONTAINER(frame), vbox);
+
+               if (flags & GFIO_SLAT)
+                       gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
+               if (flags & GFIO_CLAT)
+                       gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
+               if (flags & GFIO_LAT)
+                       gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
+       }
+
+       if (ts->clat_percentiles)
+               gfio_show_clat_percentiles(main_vbox, ts, ddir);
+
+
        free(io_p);
        free(bw_p);
        free(iops_p);
 }
 
+static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
+                                         const char **labels)
+{
+       GtkWidget *tree_view;
+       GtkTreeSelection *selection;
+       GtkListStore *model;
+       GtkTreeIter iter;
+       GType *types;
+       int i, skipped;
+
+       /*
+        * Check if all are empty, in which case don't bother
+        */
+       for (i = 0, skipped = 0; i < num; i++)
+               if (lat[i] <= 0.0)
+                       skipped++;
+
+       if (skipped == num)
+               return NULL;
+
+       types = malloc(num * sizeof(GType));
+
+       for (i = 0; i < num; i++)
+               types[i] = G_TYPE_STRING;
+
+       model = gtk_list_store_newv(num, types);
+       free(types);
+       types = NULL;
+
+       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);
+
+       for (i = 0; i < num; i++)
+               tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
+
+       gtk_list_store_append(model, &iter);
+
+       for (i = 0; i < num; i++) {
+               char fbuf[32];
+
+               if (lat[i] <= 0.0)
+                       sprintf(fbuf, "0.00");
+               else
+                       sprintf(fbuf, "%3.2f%%", lat[i]);
+
+               gtk_list_store_set(model, &iter, i, fbuf, -1);
+       }
+
+       return tree_view;
+}
+
+static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
+{
+       GtkWidget *box, *frame, *tree_view;
+       double io_u_lat_u[FIO_IO_U_LAT_U_NR];
+       double io_u_lat_m[FIO_IO_U_LAT_M_NR];
+       const char *uranges[] = { "2", "4", "10", "20", "50", "100",
+                                 "250", "500", "750", "1000", };
+       const char *mranges[] = { "2", "4", "10", "20", "50", "100",
+                                 "250", "500", "750", "1000", "2000",
+                                 ">= 2000", };
+
+       stat_calc_lat_u(ts, io_u_lat_u);
+       stat_calc_lat_m(ts, io_u_lat_m);
+
+       tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
+       if (tree_view) {
+               frame = gtk_frame_new("Latency buckets (usec)");
+               gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+               box = gtk_hbox_new(FALSE, 3);
+               gtk_container_add(GTK_CONTAINER(frame), box);
+               gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
+       }
+
+       tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
+       if (tree_view) {
+               frame = gtk_frame_new("Latency buckets (msec)");
+               gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+               box = gtk_hbox_new(FALSE, 3);
+               gtk_container_add(GTK_CONTAINER(frame), box);
+               gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
+       }
+}
+
+static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
+{
+       GtkWidget *box, *frame, *entry;
+       double usr_cpu, sys_cpu;
+       unsigned long runtime;
+       char tmp[32];
+
+       runtime = ts->total_run_time;
+       if (runtime) {
+               double runt = (double) runtime;
+
+               usr_cpu = (double) ts->usr_time * 100 / runt;
+               sys_cpu = (double) ts->sys_time * 100 / runt;
+       } else {
+               usr_cpu = 0;
+               sys_cpu = 0;
+       }
+
+       frame = gtk_frame_new("OS resources");
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+       box = gtk_hbox_new(FALSE, 3);
+       gtk_container_add(GTK_CONTAINER(frame), box);
+
+       entry = new_info_entry_in_frame(box, "User CPU");
+       sprintf(tmp, "%3.2f%%", usr_cpu);
+       gtk_entry_set_text(GTK_ENTRY(entry), tmp);
+       entry = new_info_entry_in_frame(box, "System CPU");
+       sprintf(tmp, "%3.2f%%", sys_cpu);
+       gtk_entry_set_text(GTK_ENTRY(entry), tmp);
+       entry = new_info_entry_in_frame(box, "Context switches");
+       entry_set_int_value(entry, ts->ctx);
+       entry = new_info_entry_in_frame(box, "Major faults");
+       entry_set_int_value(entry, ts->majf);
+       entry = new_info_entry_in_frame(box, "Minor faults");
+       entry_set_int_value(entry, ts->minf);
+}
+static void gfio_add_sc_depths_tree(GtkListStore *model,
+                                   struct thread_stat *ts, unsigned int len,
+                                   int submit)
+{
+       double io_u_dist[FIO_IO_U_MAP_NR];
+       GtkTreeIter iter;
+       /* Bits 0, and 3-8 */
+       const int add_mask = 0x1f9;
+       int i, j;
+
+       if (submit)
+               stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
+       else
+               stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
+
+       gtk_list_store_append(model, &iter);
+
+       gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
+
+       for (i = 1, j = 0; i < len; i++) {
+               char fbuf[32];
+
+               if (!(add_mask & (1UL << (i - 1))))
+                       sprintf(fbuf, "0.0%%");
+               else {
+                       sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
+                       j++;
+               }
+
+               gtk_list_store_set(model, &iter, i, fbuf, -1);
+       }
+
+}
+
+static void gfio_add_total_depths_tree(GtkListStore *model,
+                                      struct thread_stat *ts, unsigned int len)
+{
+       double io_u_dist[FIO_IO_U_MAP_NR];
+       GtkTreeIter iter;
+       /* Bits 1-6, and 8 */
+       const int add_mask = 0x17e;
+       int i, j;
+
+       stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
+
+       gtk_list_store_append(model, &iter);
+
+       gtk_list_store_set(model, &iter, 0, "Total", -1);
+
+       for (i = 1, j = 0; i < len; i++) {
+               char fbuf[32];
+
+               if (!(add_mask & (1UL << (i - 1))))
+                       sprintf(fbuf, "0.0%%");
+               else {
+                       sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
+                       j++;
+               }
+
+               gtk_list_store_set(model, &iter, i, fbuf, -1);
+       }
+
+}
+
+static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
+{
+       GtkWidget *frame, *box, *tree_view;
+       GtkTreeSelection *selection;
+       GtkListStore *model;
+       GType types[FIO_IO_U_MAP_NR + 1];
+       int i;
+#define NR_LABELS      10
+       const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
+
+       frame = gtk_frame_new("IO depths");
+       gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
+
+       box = gtk_hbox_new(FALSE, 3);
+       gtk_container_add(GTK_CONTAINER(frame), box);
+
+       for (i = 0; i < NR_LABELS; i++)
+               types[i] = G_TYPE_STRING;
+
+       model = gtk_list_store_newv(NR_LABELS, types);
+
+       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);
+
+       for (i = 0; i < NR_LABELS; i++)
+               tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
+
+       gfio_add_total_depths_tree(model, ts, NR_LABELS);
+       gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
+       gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
+
+       gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
+}
+
+static gboolean results_window_delete(GtkWidget *w, gpointer data)
+{
+       struct gui *ui = (struct gui *) data;
+
+       gtk_widget_destroy(w);
+       ui->results_window = NULL;
+       ui->results_notebook = NULL;
+       return TRUE;
+}
+
+static GtkWidget *get_results_window(struct gui *ui)
+{
+       GtkWidget *win, *notebook;
+
+       if (ui->results_window)
+               return ui->results_notebook;
+
+       win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       gtk_window_set_title(GTK_WINDOW(win), "Results");
+       g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
+       g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
+
+       notebook = gtk_notebook_new();
+       gtk_container_add(GTK_CONTAINER(win), notebook);
+
+       ui->results_window = win;
+       ui->results_notebook = notebook;
+       return ui->results_notebook;
+}
+
 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
                            struct group_run_stats *rs)
 {
-       GtkWidget *dialog, *box, *vbox, *entry, *content;
+       GtkWidget *res_win, *box, *vbox, *entry;
        struct gui *ui = client->client_data;
 
        gdk_threads_enter();
 
-       dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
-                       GTK_DIALOG_DESTROY_WITH_PARENT,
-                       GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
-
-       g_signal_connect_swapped(dialog, "response",
-                             G_CALLBACK(gtk_widget_destroy),
-                             dialog);
-
-       content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+       res_win = get_results_window(ui);
 
        vbox = gtk_vbox_new(FALSE, 3);
-       gtk_container_add(GTK_CONTAINER(content), vbox);
 
        box = gtk_hbox_new(TRUE, 3);
        gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
 
+       gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
+
        entry = new_info_entry_in_frame(box, "Name");
        gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
        if (strlen(ts->description)) {
@@ -516,8 +769,11 @@ static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
        if (ts->io_bytes[DDIR_WRITE])
                gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
 
-       gtk_widget_show_all(dialog);
+       gfio_show_latency_buckets(vbox, ts);
+       gfio_show_cpu_usage(vbox, ts);
+       gfio_show_io_depths(vbox, ts);
 
+       gtk_widget_show_all(ui->results_window);
        gdk_threads_leave();
 }
 
@@ -541,8 +797,10 @@ static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
 
 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
 {
+       gdk_threads_enter();
        printf("gfio_disk_util_op called\n");
        fio_client_ops.disk_util(client, cmd);
+       gdk_threads_leave();
 }
 
 extern int sum_stat_clients;
@@ -576,8 +834,10 @@ static void gfio_thread_status_op(struct fio_client *client,
 static void gfio_group_stats_op(struct fio_client *client,
                                struct fio_net_cmd *cmd)
 {
+       gdk_threads_enter();
        printf("gfio_group_stats_op called\n");
        fio_client_ops.group_stats(client, cmd);
+       gdk_threads_leave();
 }
 
 static void gfio_update_eta(struct jobs_eta *je)
@@ -589,6 +849,8 @@ static void gfio_update_eta(struct jobs_eta *je)
        double perc = 0.0;
        int i2p = 0;
 
+       gdk_threads_enter();
+
        eta_str[0] = '\0';
        output[0] = '\0';
 
@@ -658,6 +920,7 @@ static void gfio_update_eta(struct jobs_eta *je)
        }
                
        gfio_update_thread_status(output, perc);
+       gdk_threads_leave();
 }
 
 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
@@ -677,11 +940,15 @@ static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
        if (!client->name)
                client->name = strdup((char *) probe->hostname);
 
+       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);
        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);
+
+       gdk_threads_leave();
 }
 
 static void gfio_update_thread_status(char *status_message, double perc)
@@ -694,16 +961,16 @@ static void gfio_update_thread_status(char *status_message, double perc)
                GTK_PROGRESS_BAR(ui.thread_status_pb), m);
        gtk_progress_bar_set_fraction(
                GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
-       gdk_threads_enter();
        gtk_widget_queue_draw(ui.window);
-       gdk_threads_leave();
 }
 
 static void gfio_quit_op(struct fio_client *client)
 {
        struct gui *ui = client->client_data;
 
+       gdk_threads_enter();
        gfio_set_connected(ui, 0);
+       gdk_threads_leave();
 }
 
 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
@@ -724,12 +991,16 @@ static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
        p->numjobs              = le32_to_cpu(p->numjobs);
        p->group_reporting      = le32_to_cpu(p->group_reporting);
 
+       gdk_threads_enter();
+
        gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
        gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
        gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
 
        sprintf(tmp, "%u", p->iodepth);
        gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
+
+       gdk_threads_leave();
 }
 
 static void gfio_client_timed_out(struct fio_client *client)