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