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