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