a699887e058818e07bacb1f521c68e7cca378d7b
[fio.git] / gfio.c
1 /*
2  * gfio - gui front end for fio - the flexible io tester
3  *
4  * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> 
5  *
6  * The license below covers all files distributed with fio unless otherwise
7  * noted in the file itself.
8  *
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.
12  *
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.
17  *
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
21  *
22  */
23 #include <locale.h>
24 #include <malloc.h>
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28
29 #include "fio.h"
30
31 static void gfio_update_thread_status(char *status_message, double perc);
32
33 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
34
35 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
36
37 static void connect_clicked(GtkWidget *widget, gpointer data);
38 static void start_job_clicked(GtkWidget *widget, gpointer data);
39
40 static struct button_spec {
41         const char *buttontext;
42         clickfunction f;
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 },
49         { "Start Job",
50                 start_job_clicked,
51                 "Send current fio job to fio server to be executed", 1 },
52 };
53
54 struct probe_widget {
55         GtkWidget *hostname;
56         GtkWidget *os;
57         GtkWidget *arch;
58         GtkWidget *fio_ver;
59 };
60
61 struct eta_widget {
62         GtkWidget *name;
63         GtkWidget *iotype;
64         GtkWidget *ioengine;
65         GtkWidget *iodepth;
66         GtkWidget *jobs;
67         GtkWidget *files;
68         GtkWidget *read_bw;
69         GtkWidget *read_iops;
70         GtkWidget *cr_bw;
71         GtkWidget *cr_iops;
72         GtkWidget *write_bw;
73         GtkWidget *write_iops;
74         GtkWidget *cw_bw;
75         GtkWidget *cw_iops;
76 };
77
78 struct gui {
79         GtkWidget *window;
80         GtkWidget *vbox;
81         GtkWidget *topvbox;
82         GtkWidget *topalign;
83         GtkWidget *bottomalign;
84         GtkWidget *thread_status_pb;
85         GtkWidget *buttonbox;
86         GtkWidget *button[ARRAYSIZE(buttonspeclist)];
87         GtkWidget *scrolled_window;
88         GtkWidget *textview;
89         GtkWidget *error_info_bar;
90         GtkWidget *error_label;
91         GtkWidget *results_notebook;
92         GtkWidget *results_window;
93         GtkListStore *log_model;
94         GtkWidget *log_tree;
95         GtkTextBuffer *text;
96         struct probe_widget probe;
97         struct eta_widget eta;
98         int connected;
99         pthread_t t;
100
101         struct fio_client *client;
102         int nr_job_files;
103         char **job_files;
104 } ui;
105
106 struct gfio_client {
107         struct gui *ui;
108         GtkWidget *results_widget;
109         GtkWidget *disk_util_frame;
110 };
111
112 static void clear_ui_info(struct gui *ui)
113 {
114         gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
115         gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
116         gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
117         gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
118         gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
119         gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
120         gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
121         gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
122         gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
123         gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
124         gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
125         gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
126         gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
127         gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
128 }
129
130 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
131 {
132         GtkWidget *entry, *frame;
133
134         frame = gtk_frame_new(label);
135         entry = gtk_entry_new();
136         gtk_entry_set_editable(GTK_ENTRY(entry), 0);
137         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
138         gtk_container_add(GTK_CONTAINER(frame), entry);
139
140         return entry;
141 }
142
143 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
144 {
145         GtkWidget *label_widget;
146         GtkWidget *frame;
147
148         frame = gtk_frame_new(label);
149         label_widget = gtk_label_new(NULL);
150         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
151         gtk_container_add(GTK_CONTAINER(frame), label_widget);
152
153         return label_widget;
154 }
155
156 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
157 {
158         GtkWidget *button, *box;
159
160         box = gtk_hbox_new(FALSE, 3);
161         gtk_container_add(GTK_CONTAINER(hbox), box);
162
163         button = gtk_spin_button_new_with_range(min, max, 1.0);
164         gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
165
166         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
167         gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
168
169         return button;
170 }
171
172 static void gfio_set_connected(struct gui *ui, int connected)
173 {
174         if (connected) {
175                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
176                 ui->connected = 1;
177                 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
178                 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 1);
179         } else {
180                 ui->connected = 0;
181                 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
182                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
183         }
184 }
185
186 static void label_set_int_value(GtkWidget *entry, unsigned int val)
187 {
188         char tmp[80];
189
190         sprintf(tmp, "%u", val);
191         gtk_label_set_text(GTK_LABEL(entry), tmp);
192 }
193
194 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
195 {
196         char tmp[80];
197
198         sprintf(tmp, "%u", val);
199         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
200 }
201
202 #define ALIGN_LEFT 1
203 #define ALIGN_RIGHT 2
204 #define INVISIBLE 4
205 #define UNSORTABLE 8
206
207 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
208 {
209         GtkCellRenderer *renderer;
210         GtkTreeViewColumn *col;
211         double xalign = 0.0; /* left as default */
212         PangoAlignment align;
213         gboolean visible;
214
215         align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
216                 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
217                 PANGO_ALIGN_CENTER;
218         visible = !(flags & INVISIBLE);
219
220         renderer = gtk_cell_renderer_text_new();
221         col = gtk_tree_view_column_new();
222
223         gtk_tree_view_column_set_title(col, title);
224         if (!(flags & UNSORTABLE))
225                 gtk_tree_view_column_set_sort_column_id(col, index);
226         gtk_tree_view_column_set_resizable(col, TRUE);
227         gtk_tree_view_column_pack_start(col, renderer, TRUE);
228         gtk_tree_view_column_add_attribute(col, renderer, "text", index);
229         gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
230         switch (align) {
231         case PANGO_ALIGN_LEFT:
232                 xalign = 0.0;
233                 break;
234         case PANGO_ALIGN_CENTER:
235                 xalign = 0.5;
236                 break;
237         case PANGO_ALIGN_RIGHT:
238                 xalign = 1.0;
239                 break;
240         }
241         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
242         gtk_tree_view_column_set_visible(col, visible);
243         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
244         return col;
245 }
246
247 static void gfio_ui_setup_log(struct gui *ui)
248 {
249         GtkTreeSelection *selection;
250         GtkListStore *model;
251         GtkWidget *tree_view;
252
253         model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
254
255         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
256         gtk_widget_set_can_focus(tree_view, FALSE);
257
258         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
259         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
260
261         tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
262         tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
263         tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
264         tree_view_column(tree_view, 3, "Text", ALIGN_RIGHT | UNSORTABLE);
265
266         ui->log_model = model;
267         ui->log_tree = tree_view;
268 }
269
270 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
271                                                fio_fp64_t *plist,
272                                                unsigned int len,
273                                                const char *base,
274                                                unsigned int scale)
275 {
276         GType types[FIO_IO_U_LIST_MAX_LEN];
277         GtkWidget *tree_view;
278         GtkTreeSelection *selection;
279         GtkListStore *model;
280         GtkTreeIter iter;
281         int i;
282
283         for (i = 0; i < len; i++)
284                 types[i] = G_TYPE_INT;
285
286         model = gtk_list_store_newv(len, types);
287
288         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
289         gtk_widget_set_can_focus(tree_view, FALSE);
290
291         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
292         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
293
294         for (i = 0; i < len; i++) {
295                 char fbuf[8];
296
297                 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
298                 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
299         }
300
301         gtk_list_store_append(model, &iter);
302
303         for (i = 0; i < len; i++) {
304                 if (scale)
305                         ovals[i] = (ovals[i] + 999) / 1000;
306                 gtk_list_store_set(model, &iter, i, ovals[i], -1);
307         }
308
309         return tree_view;
310 }
311
312 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
313                                        int ddir)
314 {
315         unsigned int *io_u_plat = ts->io_u_plat[ddir];
316         unsigned long nr = ts->clat_stat[ddir].samples;
317         fio_fp64_t *plist = ts->percentile_list;
318         unsigned int *ovals, len, minv, maxv, scale_down;
319         const char *base;
320         GtkWidget *tree_view, *frame, *hbox;
321         char tmp[64];
322
323         len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
324         if (!len)
325                 goto out;
326
327         /*
328          * We default to usecs, but if the value range is such that we
329          * should scale down to msecs, do that.
330          */
331         if (minv > 2000 && maxv > 99999) {
332                 scale_down = 1;
333                 base = "msec";
334         } else {
335                 scale_down = 0;
336                 base = "usec";
337         }
338
339         tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
340
341         sprintf(tmp, "Completion percentiles (%s)", base);
342         frame = gtk_frame_new(tmp);
343         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
344
345         hbox = gtk_hbox_new(FALSE, 3);
346         gtk_container_add(GTK_CONTAINER(frame), hbox);
347
348         gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
349 out:
350         if (ovals)
351                 free(ovals);
352 }
353
354 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
355                           unsigned long max, double mean, double dev)
356 {
357         const char *base = "(usec)";
358         GtkWidget *hbox, *label, *frame;
359         char *minp, *maxp;
360         char tmp[64];
361
362         if (!usec_to_msec(&min, &max, &mean, &dev))
363                 base = "(msec)";
364
365         minp = num2str(min, 6, 1, 0);
366         maxp = num2str(max, 6, 1, 0);
367
368         sprintf(tmp, "%s %s", name, base);
369         frame = gtk_frame_new(tmp);
370         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
371
372         hbox = gtk_hbox_new(FALSE, 3);
373         gtk_container_add(GTK_CONTAINER(frame), hbox);
374
375         label = new_info_label_in_frame(hbox, "Minimum");
376         gtk_label_set_text(GTK_LABEL(label), minp);
377         label = new_info_label_in_frame(hbox, "Maximum");
378         gtk_label_set_text(GTK_LABEL(label), maxp);
379         label = new_info_label_in_frame(hbox, "Average");
380         sprintf(tmp, "%5.02f", mean);
381         gtk_label_set_text(GTK_LABEL(label), tmp);
382         label = new_info_label_in_frame(hbox, "Standard deviation");
383         sprintf(tmp, "%5.02f", dev);
384         gtk_label_set_text(GTK_LABEL(label), tmp);
385
386         free(minp);
387         free(maxp);
388
389 }
390
391 #define GFIO_CLAT       1
392 #define GFIO_SLAT       2
393 #define GFIO_LAT        4
394
395 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
396                                   struct thread_stat *ts, int ddir)
397 {
398         const char *ddir_label[2] = { "Read", "Write" };
399         GtkWidget *frame, *label, *box, *vbox, *main_vbox;
400         unsigned long min[3], max[3], runt;
401         unsigned long long bw, iops;
402         unsigned int flags = 0;
403         double mean[3], dev[3];
404         char *io_p, *bw_p, *iops_p;
405         int i2p;
406
407         if (!ts->runtime[ddir])
408                 return;
409
410         i2p = is_power_of_2(rs->kb_base);
411         runt = ts->runtime[ddir];
412
413         bw = (1000 * ts->io_bytes[ddir]) / runt;
414         io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
415         bw_p = num2str(bw, 6, 1, i2p);
416
417         iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
418         iops_p = num2str(iops, 6, 1, 0);
419
420         box = gtk_hbox_new(FALSE, 3);
421         gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
422
423         frame = gtk_frame_new(ddir_label[ddir]);
424         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
425
426         main_vbox = gtk_vbox_new(FALSE, 3);
427         gtk_container_add(GTK_CONTAINER(frame), main_vbox);
428
429         box = gtk_hbox_new(FALSE, 3);
430         gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
431
432         label = new_info_label_in_frame(box, "IO");
433         gtk_label_set_text(GTK_LABEL(label), io_p);
434         label = new_info_label_in_frame(box, "Bandwidth");
435         gtk_label_set_text(GTK_LABEL(label), bw_p);
436         label = new_info_label_in_frame(box, "IOPS");
437         gtk_label_set_text(GTK_LABEL(label), iops_p);
438         label = new_info_label_in_frame(box, "Runtime (msec)");
439         label_set_int_value(label, ts->runtime[ddir]);
440
441         if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
442                 double p_of_agg = 100.0;
443                 const char *bw_str = "KB";
444                 char tmp[32];
445
446                 if (rs->agg[ddir]) {
447                         p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
448                         if (p_of_agg > 100.0)
449                                 p_of_agg = 100.0;
450                 }
451
452                 if (mean[0] > 999999.9) {
453                         min[0] /= 1000.0;
454                         max[0] /= 1000.0;
455                         mean[0] /= 1000.0;
456                         dev[0] /= 1000.0;
457                         bw_str = "MB";
458                 }
459
460                 sprintf(tmp, "Bandwidth (%s)", bw_str);
461                 frame = gtk_frame_new(tmp);
462                 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
463
464                 box = gtk_hbox_new(FALSE, 3);
465                 gtk_container_add(GTK_CONTAINER(frame), box);
466
467                 label = new_info_label_in_frame(box, "Minimum");
468                 label_set_int_value(label, min[0]);
469                 label = new_info_label_in_frame(box, "Maximum");
470                 label_set_int_value(label, max[0]);
471                 label = new_info_label_in_frame(box, "Percentage of jobs");
472                 sprintf(tmp, "%3.2f%%", p_of_agg);
473                 gtk_label_set_text(GTK_LABEL(label), tmp);
474                 label = new_info_label_in_frame(box, "Average");
475                 sprintf(tmp, "%5.02f", mean[0]);
476                 gtk_label_set_text(GTK_LABEL(label), tmp);
477                 label = new_info_label_in_frame(box, "Standard deviation");
478                 sprintf(tmp, "%5.02f", dev[0]);
479                 gtk_label_set_text(GTK_LABEL(label), tmp);
480         }
481
482         if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
483                 flags |= GFIO_SLAT;
484         if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
485                 flags |= GFIO_CLAT;
486         if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
487                 flags |= GFIO_LAT;
488
489         if (flags) {
490                 frame = gtk_frame_new("Latency");
491                 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
492
493                 vbox = gtk_vbox_new(FALSE, 3);
494                 gtk_container_add(GTK_CONTAINER(frame), vbox);
495
496                 if (flags & GFIO_SLAT)
497                         gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
498                 if (flags & GFIO_CLAT)
499                         gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
500                 if (flags & GFIO_LAT)
501                         gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
502         }
503
504         if (ts->clat_percentiles)
505                 gfio_show_clat_percentiles(main_vbox, ts, ddir);
506
507
508         free(io_p);
509         free(bw_p);
510         free(iops_p);
511 }
512
513 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
514                                           const char **labels)
515 {
516         GtkWidget *tree_view;
517         GtkTreeSelection *selection;
518         GtkListStore *model;
519         GtkTreeIter iter;
520         GType *types;
521         int i, skipped;
522
523         /*
524          * Check if all are empty, in which case don't bother
525          */
526         for (i = 0, skipped = 0; i < num; i++)
527                 if (lat[i] <= 0.0)
528                         skipped++;
529
530         if (skipped == num)
531                 return NULL;
532
533         types = malloc(num * sizeof(GType));
534
535         for (i = 0; i < num; i++)
536                 types[i] = G_TYPE_STRING;
537
538         model = gtk_list_store_newv(num, types);
539         free(types);
540         types = NULL;
541
542         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
543         gtk_widget_set_can_focus(tree_view, FALSE);
544
545         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
546         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
547
548         for (i = 0; i < num; i++)
549                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
550
551         gtk_list_store_append(model, &iter);
552
553         for (i = 0; i < num; i++) {
554                 char fbuf[32];
555
556                 if (lat[i] <= 0.0)
557                         sprintf(fbuf, "0.00");
558                 else
559                         sprintf(fbuf, "%3.2f%%", lat[i]);
560
561                 gtk_list_store_set(model, &iter, i, fbuf, -1);
562         }
563
564         return tree_view;
565 }
566
567 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
568 {
569         GtkWidget *box, *frame, *tree_view;
570         double io_u_lat_u[FIO_IO_U_LAT_U_NR];
571         double io_u_lat_m[FIO_IO_U_LAT_M_NR];
572         const char *uranges[] = { "2", "4", "10", "20", "50", "100",
573                                   "250", "500", "750", "1000", };
574         const char *mranges[] = { "2", "4", "10", "20", "50", "100",
575                                   "250", "500", "750", "1000", "2000",
576                                   ">= 2000", };
577
578         stat_calc_lat_u(ts, io_u_lat_u);
579         stat_calc_lat_m(ts, io_u_lat_m);
580
581         tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
582         if (tree_view) {
583                 frame = gtk_frame_new("Latency buckets (usec)");
584                 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
585
586                 box = gtk_hbox_new(FALSE, 3);
587                 gtk_container_add(GTK_CONTAINER(frame), box);
588                 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
589         }
590
591         tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
592         if (tree_view) {
593                 frame = gtk_frame_new("Latency buckets (msec)");
594                 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
595
596                 box = gtk_hbox_new(FALSE, 3);
597                 gtk_container_add(GTK_CONTAINER(frame), box);
598                 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
599         }
600 }
601
602 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
603 {
604         GtkWidget *box, *frame, *entry;
605         double usr_cpu, sys_cpu;
606         unsigned long runtime;
607         char tmp[32];
608
609         runtime = ts->total_run_time;
610         if (runtime) {
611                 double runt = (double) runtime;
612
613                 usr_cpu = (double) ts->usr_time * 100 / runt;
614                 sys_cpu = (double) ts->sys_time * 100 / runt;
615         } else {
616                 usr_cpu = 0;
617                 sys_cpu = 0;
618         }
619
620         frame = gtk_frame_new("OS resources");
621         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
622
623         box = gtk_hbox_new(FALSE, 3);
624         gtk_container_add(GTK_CONTAINER(frame), box);
625
626         entry = new_info_entry_in_frame(box, "User CPU");
627         sprintf(tmp, "%3.2f%%", usr_cpu);
628         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
629         entry = new_info_entry_in_frame(box, "System CPU");
630         sprintf(tmp, "%3.2f%%", sys_cpu);
631         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
632         entry = new_info_entry_in_frame(box, "Context switches");
633         entry_set_int_value(entry, ts->ctx);
634         entry = new_info_entry_in_frame(box, "Major faults");
635         entry_set_int_value(entry, ts->majf);
636         entry = new_info_entry_in_frame(box, "Minor faults");
637         entry_set_int_value(entry, ts->minf);
638 }
639 static void gfio_add_sc_depths_tree(GtkListStore *model,
640                                     struct thread_stat *ts, unsigned int len,
641                                     int submit)
642 {
643         double io_u_dist[FIO_IO_U_MAP_NR];
644         GtkTreeIter iter;
645         /* Bits 0, and 3-8 */
646         const int add_mask = 0x1f9;
647         int i, j;
648
649         if (submit)
650                 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
651         else
652                 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
653
654         gtk_list_store_append(model, &iter);
655
656         gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
657
658         for (i = 1, j = 0; i < len; i++) {
659                 char fbuf[32];
660
661                 if (!(add_mask & (1UL << (i - 1))))
662                         sprintf(fbuf, "0.0%%");
663                 else {
664                         sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
665                         j++;
666                 }
667
668                 gtk_list_store_set(model, &iter, i, fbuf, -1);
669         }
670
671 }
672
673 static void gfio_add_total_depths_tree(GtkListStore *model,
674                                        struct thread_stat *ts, unsigned int len)
675 {
676         double io_u_dist[FIO_IO_U_MAP_NR];
677         GtkTreeIter iter;
678         /* Bits 1-6, and 8 */
679         const int add_mask = 0x17e;
680         int i, j;
681
682         stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
683
684         gtk_list_store_append(model, &iter);
685
686         gtk_list_store_set(model, &iter, 0, "Total", -1);
687
688         for (i = 1, j = 0; i < len; i++) {
689                 char fbuf[32];
690
691                 if (!(add_mask & (1UL << (i - 1))))
692                         sprintf(fbuf, "0.0%%");
693                 else {
694                         sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
695                         j++;
696                 }
697
698                 gtk_list_store_set(model, &iter, i, fbuf, -1);
699         }
700
701 }
702
703 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
704 {
705         GtkWidget *frame, *box, *tree_view;
706         GtkTreeSelection *selection;
707         GtkListStore *model;
708         GType types[FIO_IO_U_MAP_NR + 1];
709         int i;
710 #define NR_LABELS       10
711         const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
712
713         frame = gtk_frame_new("IO depths");
714         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
715
716         box = gtk_hbox_new(FALSE, 3);
717         gtk_container_add(GTK_CONTAINER(frame), box);
718
719         for (i = 0; i < NR_LABELS; i++)
720                 types[i] = G_TYPE_STRING;
721
722         model = gtk_list_store_newv(NR_LABELS, types);
723
724         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
725         gtk_widget_set_can_focus(tree_view, FALSE);
726
727         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
728         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
729
730         for (i = 0; i < NR_LABELS; i++)
731                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
732
733         gfio_add_total_depths_tree(model, ts, NR_LABELS);
734         gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
735         gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
736
737         gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
738 }
739
740 static gboolean results_window_delete(GtkWidget *w, gpointer data)
741 {
742         struct gui *ui = (struct gui *) data;
743
744         gtk_widget_destroy(w);
745         ui->results_window = NULL;
746         ui->results_notebook = NULL;
747         return TRUE;
748 }
749
750 static GtkWidget *get_results_window(struct gui *ui)
751 {
752         GtkWidget *win, *notebook;
753
754         if (ui->results_window)
755                 return ui->results_notebook;
756
757         win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
758         gtk_window_set_title(GTK_WINDOW(win), "Results");
759         g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ui);
760         g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ui);
761
762         notebook = gtk_notebook_new();
763         gtk_container_add(GTK_CONTAINER(win), notebook);
764
765         ui->results_window = win;
766         ui->results_notebook = notebook;
767         return ui->results_notebook;
768 }
769
770 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
771                             struct group_run_stats *rs)
772 {
773         GtkWidget *res_win, *box, *vbox, *entry;
774         struct gfio_client *gc = client->client_data;
775
776         gdk_threads_enter();
777
778         res_win = get_results_window(gc->ui);
779
780         vbox = gtk_vbox_new(FALSE, 3);
781
782         box = gtk_hbox_new(TRUE, 3);
783         gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
784
785         gtk_notebook_append_page(GTK_NOTEBOOK(res_win), vbox, gtk_label_new(ts->name));
786
787         gc->results_widget = vbox;
788
789         entry = new_info_entry_in_frame(box, "Name");
790         gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
791         if (strlen(ts->description)) {
792                 entry = new_info_entry_in_frame(box, "Description");
793                 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
794         }
795         entry = new_info_entry_in_frame(box, "Group ID");
796         entry_set_int_value(entry, ts->groupid);
797         entry = new_info_entry_in_frame(box, "Jobs");
798         entry_set_int_value(entry, ts->members);
799         entry = new_info_entry_in_frame(box, "Error");
800         entry_set_int_value(entry, ts->error);
801         entry = new_info_entry_in_frame(box, "PID");
802         entry_set_int_value(entry, ts->pid);
803
804         if (ts->io_bytes[DDIR_READ])
805                 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
806         if (ts->io_bytes[DDIR_WRITE])
807                 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
808
809         gfio_show_latency_buckets(vbox, ts);
810         gfio_show_cpu_usage(vbox, ts);
811         gfio_show_io_depths(vbox, ts);
812
813         gtk_widget_show_all(gc->ui->results_window);
814         gdk_threads_leave();
815 }
816
817 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
818 {
819         struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
820         struct gfio_client *gc = client->client_data;
821         GtkTreeIter iter;
822         struct tm *tm;
823         time_t sec;
824         char tmp[64], timebuf[80];
825
826         sec = p->log_sec;
827         tm = localtime(&sec);
828         strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
829         sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
830
831         gdk_threads_enter();
832
833         gtk_list_store_append(gc->ui->log_model, &iter);
834         gtk_list_store_set(gc->ui->log_model, &iter, 0, timebuf, -1);
835         gtk_list_store_set(gc->ui->log_model, &iter, 1, client->hostname, -1);
836         gtk_list_store_set(gc->ui->log_model, &iter, 2, p->level, -1);
837         gtk_list_store_set(gc->ui->log_model, &iter, 3, p->buf, -1);
838
839         gdk_threads_leave();
840 }
841
842 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
843 {
844         struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
845         struct gfio_client *gc = client->client_data;
846         GtkWidget *box, *frame, *entry, *vbox;
847
848         gdk_threads_enter();
849
850         if (!gc->results_widget) {
851                 printf("no results!\n");
852                 goto out;
853         }
854
855         if (!gc->disk_util_frame) {
856                 gc->disk_util_frame = gtk_frame_new("Disk utilization");
857                 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
858         }
859
860         vbox = gtk_vbox_new(FALSE, 3);
861         gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
862
863         frame = gtk_frame_new((char *) p->dus.name);
864         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
865
866         box = gtk_vbox_new(FALSE, 3);
867         gtk_container_add(GTK_CONTAINER(frame), box);
868
869         frame = gtk_frame_new("Read");
870         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
871         vbox = gtk_hbox_new(TRUE, 3);
872         gtk_container_add(GTK_CONTAINER(frame), vbox);
873         entry = new_info_entry_in_frame(vbox, "IOs");
874         entry_set_int_value(entry, p->dus.ios[0]);
875         entry = new_info_entry_in_frame(vbox, "Merges");
876         entry_set_int_value(entry, p->dus.merges[0]);
877         entry = new_info_entry_in_frame(vbox, "Sectors");
878         entry_set_int_value(entry, p->dus.sectors[0]);
879         entry = new_info_entry_in_frame(vbox, "Ticks");
880         entry_set_int_value(entry, p->dus.ticks[0]);
881
882         frame = gtk_frame_new("Write");
883         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
884         vbox = gtk_hbox_new(TRUE, 3);
885         gtk_container_add(GTK_CONTAINER(frame), vbox);
886         entry = new_info_entry_in_frame(vbox, "IOs");
887         entry_set_int_value(entry, p->dus.ios[1]);
888         entry = new_info_entry_in_frame(vbox, "Merges");
889         entry_set_int_value(entry, p->dus.merges[1]);
890         entry = new_info_entry_in_frame(vbox, "Sectors");
891         entry_set_int_value(entry, p->dus.sectors[1]);
892         entry = new_info_entry_in_frame(vbox, "Ticks");
893         entry_set_int_value(entry, p->dus.ticks[1]);
894
895         frame = gtk_frame_new("Shared");
896         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
897         vbox = gtk_hbox_new(TRUE, 3);
898         gtk_container_add(GTK_CONTAINER(frame), vbox);
899         entry = new_info_entry_in_frame(vbox, "IO ticks");
900         entry_set_int_value(entry, p->dus.io_ticks);
901         entry = new_info_entry_in_frame(vbox, "Time in queue");
902         entry_set_int_value(entry, p->dus.time_in_queue);
903
904         gtk_widget_show_all(gc->results_widget);
905 out:
906         gdk_threads_leave();
907 }
908
909 extern int sum_stat_clients;
910 extern struct thread_stat client_ts;
911 extern struct group_run_stats client_gs;
912
913 static int sum_stat_nr;
914
915 static void gfio_thread_status_op(struct fio_client *client,
916                                   struct fio_net_cmd *cmd)
917 {
918         struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
919
920         gfio_display_ts(client, &p->ts, &p->rs);
921
922         if (sum_stat_clients == 1)
923                 return;
924
925         sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
926         sum_group_stats(&client_gs, &p->rs);
927
928         client_ts.members++;
929         client_ts.groupid = p->ts.groupid;
930
931         if (++sum_stat_nr == sum_stat_clients) {
932                 strcpy(client_ts.name, "All clients");
933                 gfio_display_ts(client, &client_ts, &client_gs);
934         }
935 }
936
937 static void gfio_group_stats_op(struct fio_client *client,
938                                 struct fio_net_cmd *cmd)
939 {
940         gdk_threads_enter();
941         printf("gfio_group_stats_op called\n");
942         fio_client_ops.group_stats(client, cmd);
943         gdk_threads_leave();
944 }
945
946 static void gfio_update_eta(struct jobs_eta *je)
947 {
948         static int eta_good;
949         char eta_str[128];
950         char output[256];
951         char tmp[32];
952         double perc = 0.0;
953         int i2p = 0;
954
955         gdk_threads_enter();
956
957         eta_str[0] = '\0';
958         output[0] = '\0';
959
960         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
961                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
962                 eta_to_str(eta_str, je->eta_sec);
963         }
964
965         sprintf(tmp, "%u", je->nr_running);
966         gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
967         sprintf(tmp, "%u", je->files_open);
968         gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
969
970 #if 0
971         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
972         if (je->m_rate || je->t_rate) {
973                 char *tr, *mr;
974
975                 mr = num2str(je->m_rate, 4, 0, i2p);
976                 tr = num2str(je->t_rate, 4, 0, i2p);
977                 gtk_entry_set_text(GTK_ENTRY(ui.eta);
978                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
979                 free(tr);
980                 free(mr);
981         } else if (je->m_iops || je->t_iops)
982                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
983
984         gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
985         gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
986         gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
987         gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
988 #endif
989
990         if (je->eta_sec != INT_MAX && je->nr_running) {
991                 char *iops_str[2];
992                 char *rate_str[2];
993
994                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
995                         strcpy(output, "-.-% done");
996                 else {
997                         eta_good = 1;
998                         perc *= 100.0;
999                         sprintf(output, "%3.1f%% done", perc);
1000                 }
1001
1002                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1003                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1004
1005                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1006                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1007
1008                 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1009                 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1010                 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1011                 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1012
1013                 free(rate_str[0]);
1014                 free(rate_str[1]);
1015                 free(iops_str[0]);
1016                 free(iops_str[1]);
1017         }
1018
1019         if (eta_str[0]) {
1020                 char *dst = output + strlen(output);
1021
1022                 sprintf(dst, " - %s", eta_str);
1023         }
1024                 
1025         gfio_update_thread_status(output, perc);
1026         gdk_threads_leave();
1027 }
1028
1029 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1030 {
1031         struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1032         struct gfio_client *gc = client->client_data;
1033         struct gui *ui = gc->ui;
1034         const char *os, *arch;
1035         char buf[64];
1036
1037         os = fio_get_os_string(probe->os);
1038         if (!os)
1039                 os = "unknown";
1040
1041         arch = fio_get_arch_string(probe->arch);
1042         if (!arch)
1043                 os = "unknown";
1044
1045         if (!client->name)
1046                 client->name = strdup((char *) probe->hostname);
1047
1048         gdk_threads_enter();
1049
1050         gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1051         gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1052         gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1053         sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1054         gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1055
1056         gfio_set_connected(ui, 1);
1057
1058         gdk_threads_leave();
1059 }
1060
1061 static void gfio_update_thread_status(char *status_message, double perc)
1062 {
1063         static char message[100];
1064         const char *m = message;
1065
1066         strncpy(message, status_message, sizeof(message) - 1);
1067         gtk_progress_bar_set_text(
1068                 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1069         gtk_progress_bar_set_fraction(
1070                 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1071         gtk_widget_queue_draw(ui.window);
1072 }
1073
1074 static void gfio_quit_op(struct fio_client *client)
1075 {
1076         struct gfio_client *gc = client->client_data;
1077
1078         gdk_threads_enter();
1079         gfio_set_connected(gc->ui, 0);
1080         gdk_threads_leave();
1081 }
1082
1083 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1084 {
1085         struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1086         struct gfio_client *gc = client->client_data;
1087         struct gui *ui = gc->ui;
1088         char tmp[8];
1089         int i;
1090
1091         p->iodepth              = le32_to_cpu(p->iodepth);
1092         p->rw                   = le32_to_cpu(p->rw);
1093
1094         for (i = 0; i < 2; i++) {
1095                 p->min_bs[i]    = le32_to_cpu(p->min_bs[i]);
1096                 p->max_bs[i]    = le32_to_cpu(p->max_bs[i]);
1097         }
1098
1099         p->numjobs              = le32_to_cpu(p->numjobs);
1100         p->group_reporting      = le32_to_cpu(p->group_reporting);
1101
1102         gdk_threads_enter();
1103
1104         gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1105         gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1106         gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1107
1108         sprintf(tmp, "%u", p->iodepth);
1109         gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1110
1111         gdk_threads_leave();
1112 }
1113
1114 static void gfio_client_timed_out(struct fio_client *client)
1115 {
1116         struct gfio_client *gc = client->client_data;
1117         GtkWidget *dialog, *label, *content;
1118         char buf[256];
1119
1120         gdk_threads_enter();
1121
1122         gfio_set_connected(gc->ui, 0);
1123         clear_ui_info(gc->ui);
1124
1125         sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1126
1127         dialog = gtk_dialog_new_with_buttons("Timed out!",
1128                         GTK_WINDOW(gc->ui->window),
1129                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1130                         GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1131
1132         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1133         label = gtk_label_new((const gchar *) buf);
1134         gtk_container_add(GTK_CONTAINER(content), label);
1135         gtk_widget_show_all(dialog);
1136         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1137
1138         gtk_dialog_run(GTK_DIALOG(dialog));
1139         gtk_widget_destroy(dialog);
1140
1141         gdk_threads_leave();
1142 }
1143
1144 struct client_ops gfio_client_ops = {
1145         .text_op                = gfio_text_op,
1146         .disk_util              = gfio_disk_util_op,
1147         .thread_status          = gfio_thread_status_op,
1148         .group_stats            = gfio_group_stats_op,
1149         .eta                    = gfio_update_eta,
1150         .probe                  = gfio_probe_op,
1151         .quit                   = gfio_quit_op,
1152         .add_job                = gfio_add_job_op,
1153         .timed_out              = gfio_client_timed_out,
1154         .stay_connected         = 1,
1155 };
1156
1157 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1158                 __attribute__((unused)) gpointer data)
1159 {
1160         gtk_main_quit();
1161 }
1162
1163 static void *job_thread(void *arg)
1164 {
1165         fio_handle_clients(&gfio_client_ops);
1166         return NULL;
1167 }
1168
1169 static int send_job_files(struct gui *ui)
1170 {
1171         int i, ret = 0;
1172
1173         for (i = 0; i < ui->nr_job_files; i++) {
1174                 ret = fio_clients_send_ini(ui->job_files[i]);
1175                 if (ret)
1176                         break;
1177
1178                 free(ui->job_files[i]);
1179                 ui->job_files[i] = NULL;
1180         }
1181         while (i < ui->nr_job_files) {
1182                 free(ui->job_files[i]);
1183                 ui->job_files[i] = NULL;
1184                 i++;
1185         }
1186
1187         return ret;
1188 }
1189
1190 static void start_job_thread(struct gui *ui)
1191 {
1192         if (send_job_files(ui)) {
1193                 printf("Yeah, I didn't really like those options too much.\n");
1194                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1195                 return;
1196         }
1197 }
1198
1199 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1200                 gpointer data)
1201 {
1202         struct gui *ui = data;
1203
1204         gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1205         start_job_thread(ui);
1206 }
1207
1208 static void file_open(GtkWidget *w, gpointer data);
1209
1210 static void connect_clicked(GtkWidget *widget, gpointer data)
1211 {
1212         struct gui *ui = data;
1213
1214         if (!ui->connected) {
1215                 if (!ui->nr_job_files)
1216                         file_open(widget, data);
1217                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1218                 fio_clients_connect();
1219                 pthread_create(&ui->t, NULL, job_thread, NULL);
1220                 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1221         } else {
1222                 fio_clients_terminate();
1223                 gfio_set_connected(ui, 0);
1224                 clear_ui_info(ui);
1225         }
1226 }
1227
1228 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1229                         struct button_spec *buttonspec)
1230 {
1231         ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1232         g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1233         gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1234         gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1235         gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1236 }
1237
1238 static void add_buttons(struct gui *ui,
1239                                 struct button_spec *buttonlist,
1240                                 int nbuttons)
1241 {
1242         int i;
1243
1244         for (i = 0; i < nbuttons; i++)
1245                 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1246 }
1247
1248 static void on_info_bar_response(GtkWidget *widget, gint response,
1249                                  gpointer data)
1250 {
1251         if (response == GTK_RESPONSE_OK) {
1252                 gtk_widget_destroy(widget);
1253                 ui.error_info_bar = NULL;
1254         }
1255 }
1256
1257 void report_error(GError *error)
1258 {
1259         if (ui.error_info_bar == NULL) {
1260                 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1261                                                                GTK_RESPONSE_OK,
1262                                                                NULL);
1263                 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1264                 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1265                                               GTK_MESSAGE_ERROR);
1266                 
1267                 ui.error_label = gtk_label_new(error->message);
1268                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1269                 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1270                 
1271                 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1272                 gtk_widget_show_all(ui.vbox);
1273         } else {
1274                 char buffer[256];
1275                 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1276                 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1277         }
1278 }
1279
1280 static int get_connection_details(char **host, int *port, int *type,
1281                                   int *server_start)
1282 {
1283         GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1284         GtkWidget *button;
1285         char *typeentry;
1286
1287         dialog = gtk_dialog_new_with_buttons("Connection details",
1288                         GTK_WINDOW(ui.window),
1289                         GTK_DIALOG_DESTROY_WITH_PARENT,
1290                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1291                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1292
1293         frame = gtk_frame_new("Hostname / socket name");
1294         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1295         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1296
1297         box = gtk_vbox_new(FALSE, 6);
1298         gtk_container_add(GTK_CONTAINER(frame), box);
1299
1300         hbox = gtk_hbox_new(TRUE, 10);
1301         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1302         hentry = gtk_entry_new();
1303         gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1304         gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1305
1306         frame = gtk_frame_new("Port");
1307         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1308         box = gtk_vbox_new(FALSE, 10);
1309         gtk_container_add(GTK_CONTAINER(frame), box);
1310
1311         hbox = gtk_hbox_new(TRUE, 4);
1312         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1313         pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1314
1315         frame = gtk_frame_new("Type");
1316         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1317         box = gtk_vbox_new(FALSE, 10);
1318         gtk_container_add(GTK_CONTAINER(frame), box);
1319
1320         hbox = gtk_hbox_new(TRUE, 4);
1321         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1322
1323         combo = gtk_combo_box_text_new();
1324         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1325         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1326         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1327         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1328
1329         gtk_container_add(GTK_CONTAINER(hbox), combo);
1330
1331         frame = gtk_frame_new("Options");
1332         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1333         box = gtk_vbox_new(FALSE, 10);
1334         gtk_container_add(GTK_CONTAINER(frame), box);
1335
1336         hbox = gtk_hbox_new(TRUE, 4);
1337         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1338
1339         button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1340         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1341         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.");
1342         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1343
1344         gtk_widget_show_all(dialog);
1345
1346         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1347                 gtk_widget_destroy(dialog);
1348                 return 1;
1349         }
1350
1351         *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1352         *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1353
1354         typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1355         if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1356                 *type = Fio_client_ipv4;
1357         else if (!strncmp(typeentry, "IPv6", 4))
1358                 *type = Fio_client_ipv6;
1359         else
1360                 *type = Fio_client_socket;
1361         g_free(typeentry);
1362
1363         *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1364
1365         gtk_widget_destroy(dialog);
1366         return 0;
1367 }
1368
1369 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1370 {
1371         struct gfio_client *gc;
1372
1373         gc = malloc(sizeof(*gc));
1374         memset(gc, 0, sizeof(*gc));
1375         gc->ui = ui;
1376
1377         client->client_data = gc;
1378 }
1379
1380 static void file_open(GtkWidget *w, gpointer data)
1381 {
1382         GtkWidget *dialog;
1383         GSList *filenames, *fn_glist;
1384         GtkFileFilter *filter;
1385         char *host;
1386         int port, type, server_start;
1387
1388         dialog = gtk_file_chooser_dialog_new("Open File",
1389                 GTK_WINDOW(ui.window),
1390                 GTK_FILE_CHOOSER_ACTION_OPEN,
1391                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1392                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1393                 NULL);
1394         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1395
1396         filter = gtk_file_filter_new();
1397         gtk_file_filter_add_pattern(filter, "*.fio");
1398         gtk_file_filter_add_pattern(filter, "*.job");
1399         gtk_file_filter_add_mime_type(filter, "text/fio");
1400         gtk_file_filter_set_name(filter, "Fio job file");
1401         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1402
1403         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1404                 gtk_widget_destroy(dialog);
1405                 return;
1406         }
1407
1408         fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1409
1410         gtk_widget_destroy(dialog);
1411
1412         if (get_connection_details(&host, &port, &type, &server_start))
1413                 goto err;
1414
1415         filenames = fn_glist;
1416         while (filenames != NULL) {
1417                 struct fio_client *client;
1418
1419                 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1420                 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1421                 ui.nr_job_files++;
1422
1423                 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1424                 if (!client) {
1425                         GError *error;
1426
1427                         error = g_error_new(g_quark_from_string("fio"), 1,
1428                                         "Failed to add client %s", host);
1429                         report_error(error);
1430                         g_error_free(error);
1431                 }
1432                 gfio_client_added(&ui, client);
1433                         
1434                 g_free(filenames->data);
1435                 filenames = g_slist_next(filenames);
1436         }
1437         free(host);
1438 err:
1439         g_slist_free(fn_glist);
1440 }
1441
1442 static void file_save(GtkWidget *w, gpointer data)
1443 {
1444         GtkWidget *dialog;
1445
1446         dialog = gtk_file_chooser_dialog_new("Save File",
1447                 GTK_WINDOW(ui.window),
1448                 GTK_FILE_CHOOSER_ACTION_SAVE,
1449                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1450                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1451                 NULL);
1452
1453         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1454         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1455
1456         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1457                 char *filename;
1458
1459                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1460                 // save_job_file(filename);
1461                 g_free(filename);
1462         }
1463         gtk_widget_destroy(dialog);
1464 }
1465
1466 static void view_log_destroy(GtkWidget *w, gpointer data)
1467 {
1468         struct gui *ui = (struct gui *) data;
1469
1470         gtk_widget_ref(ui->log_tree);
1471         gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1472         gtk_widget_destroy(w);
1473 }
1474
1475 static void view_log(GtkWidget *w, gpointer data)
1476 {
1477         GtkWidget *win, *box;
1478
1479         win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1480         gtk_window_set_title(GTK_WINDOW(win), "Log");
1481
1482         box = gtk_hbox_new(FALSE, 3);
1483         gtk_container_add(GTK_CONTAINER(win), box);
1484
1485         g_signal_connect(box, "delete-event", G_CALLBACK(view_log_destroy), (gpointer) &ui);
1486         g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), (gpointer) &ui);
1487         gtk_container_add(GTK_CONTAINER(box), ui.log_tree);
1488         gtk_widget_show_all(win);
1489 }
1490
1491 static void preferences(GtkWidget *w, gpointer data)
1492 {
1493         GtkWidget *dialog, *frame, *box, **buttons;
1494         int i;
1495
1496         dialog = gtk_dialog_new_with_buttons("Preferences",
1497                 GTK_WINDOW(ui.window),
1498                 GTK_DIALOG_DESTROY_WITH_PARENT,
1499                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1500                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1501                 NULL);
1502
1503         frame = gtk_frame_new("Debug logging");
1504         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1505         box = gtk_hbox_new(FALSE, 6);
1506         gtk_container_add(GTK_CONTAINER(frame), box);
1507
1508         buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1509
1510         for (i = 0; i < FD_DEBUG_MAX; i++) {
1511                 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1512                 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1513                 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1514         }
1515
1516         gtk_widget_show_all(dialog);
1517
1518         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1519                 gtk_widget_destroy(dialog);
1520                 return;
1521         }
1522
1523         for (i = 0; i < FD_DEBUG_MAX; i++) {
1524                 int set;
1525
1526                 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1527                 if (set)
1528                         fio_debug |= (1UL << i);
1529         }
1530
1531         gtk_widget_destroy(dialog);
1532 }
1533
1534 static void about_dialog(GtkWidget *w, gpointer data)
1535 {
1536         gtk_show_about_dialog(NULL,
1537                 "program-name", "gfio",
1538                 "comments", "Gtk2 UI for fio",
1539                 "license", "GPLv2",
1540                 "version", fio_version_string,
1541                 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1542                 "logo-icon-name", "fio",
1543                 /* Must be last: */
1544                 NULL, NULL,
1545                 NULL);
1546 }
1547
1548 static GtkActionEntry menu_items[] = {
1549         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1550         { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1551         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1552         { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
1553         { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
1554         { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1555         { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1556         { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1557         { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
1558 };
1559 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1560
1561 static const gchar *ui_string = " \
1562         <ui> \
1563                 <menubar name=\"MainMenu\"> \
1564                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1565                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1566                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1567                                 <separator name=\"Separator\"/> \
1568                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1569                                 <separator name=\"Separator2\"/> \
1570                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
1571                         </menu> \
1572                         <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1573                                 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1574                         </menu>\
1575                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
1576                                 <menuitem name=\"About\" action=\"About\" /> \
1577                         </menu> \
1578                 </menubar> \
1579         </ui> \
1580 ";
1581
1582 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1583 {
1584         GtkActionGroup *action_group = gtk_action_group_new("Menu");
1585         GError *error = 0;
1586
1587         action_group = gtk_action_group_new("Menu");
1588         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1589
1590         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1591         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1592
1593         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1594         return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1595 }
1596
1597 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1598                    GtkWidget *vbox, GtkUIManager *ui_manager)
1599 {
1600         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1601 }
1602
1603 static void init_ui(int *argc, char **argv[], struct gui *ui)
1604 {
1605         GtkSettings *settings;
1606         GtkUIManager *uimanager;
1607         GtkWidget *menu, *probe, *probe_frame, *probe_box;
1608
1609         memset(ui, 0, sizeof(*ui));
1610
1611         /* Magical g*thread incantation, you just need this thread stuff.
1612          * Without it, the update that happens in gfio_update_thread_status
1613          * doesn't really happen in a timely fashion, you need expose events
1614          */
1615         if (!g_thread_supported())
1616                 g_thread_init(NULL);
1617         gdk_threads_init();
1618
1619         gtk_init(argc, argv);
1620         settings = gtk_settings_get_default();
1621         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1622         g_type_init();
1623         
1624         ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1625         gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1626         gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1627
1628         g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1629         g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1630
1631         ui->vbox = gtk_vbox_new(FALSE, 0);
1632         gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1633
1634         uimanager = gtk_ui_manager_new();
1635         menu = get_menubar_menu(ui->window, uimanager);
1636         gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1637
1638         /*
1639          * Set up alignments for widgets at the top of ui, 
1640          * align top left, expand horizontally but not vertically
1641          */
1642         ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1643         ui->topvbox = gtk_vbox_new(FALSE, 3);
1644         gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1645         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1646
1647         probe = gtk_frame_new("Job");
1648         gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1649         probe_frame = gtk_vbox_new(FALSE, 3);
1650         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1651
1652         probe_box = gtk_hbox_new(FALSE, 3);
1653         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1654         ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1655         ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1656         ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1657         ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1658
1659         probe_box = gtk_hbox_new(FALSE, 3);
1660         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1661
1662         ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1663         ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1664         ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1665         ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1666         ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1667         ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1668
1669         probe_box = gtk_hbox_new(FALSE, 3);
1670         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1671         ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1672         ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1673         ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1674         ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1675
1676         /*
1677          * Only add this if we have a commit rate
1678          */
1679 #if 0
1680         probe_box = gtk_hbox_new(FALSE, 3);
1681         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1682
1683         ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1684         ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1685
1686         ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1687         ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1688 #endif
1689
1690         /*
1691          * Add a text box for text op messages 
1692          */
1693         ui->textview = gtk_text_view_new();
1694         ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1695         gtk_text_buffer_set_text(ui->text, "", -1);
1696         gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1697         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1698         ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1699         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1700                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1701         gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1702         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1703                         TRUE, TRUE, 0);
1704
1705         /*
1706          * Set up alignments for widgets at the bottom of ui, 
1707          * align bottom left, expand horizontally but not vertically
1708          */
1709         ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1710         ui->buttonbox = gtk_hbox_new(FALSE, 0);
1711         gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1712         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1713                                         FALSE, FALSE, 0);
1714
1715         add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1716
1717         /*
1718          * Set up thread status progress bar
1719          */
1720         ui->thread_status_pb = gtk_progress_bar_new();
1721         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1722         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1723         gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1724
1725         gfio_ui_setup_log(ui);
1726
1727         gtk_widget_show_all(ui->window);
1728 }
1729
1730 int main(int argc, char *argv[], char *envp[])
1731 {
1732         if (initialize_fio(envp))
1733                 return 1;
1734         if (fio_init_options())
1735                 return 1;
1736
1737         init_ui(&argc, &argv, &ui);
1738
1739         gdk_threads_enter();
1740         gtk_main();
1741         gdk_threads_leave();
1742         return 0;
1743 }