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