gfio: first attempt at actually automatically starting backend server
[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         }
1231 }
1232
1233 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1234                 gpointer data)
1235 {
1236         struct gui *ui = data;
1237
1238         gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1239         start_job_thread(ui);
1240 }
1241
1242 static void file_open(GtkWidget *w, gpointer data);
1243
1244 static void connect_clicked(GtkWidget *widget, gpointer data)
1245 {
1246         struct gui *ui = data;
1247
1248         if (!ui->connected) {
1249                 if (!ui->nr_job_files)
1250                         file_open(widget, data);
1251                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1252                 if (!fio_clients_connect()) {
1253                         pthread_create(&ui->t, NULL, job_thread, NULL);
1254                         gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1255                 }
1256         } else {
1257                 fio_clients_terminate();
1258                 gfio_set_connected(ui, 0);
1259                 clear_ui_info(ui);
1260         }
1261 }
1262
1263 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1264                         struct button_spec *buttonspec)
1265 {
1266         ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1267         g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1268         gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1269         gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1270         gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1271 }
1272
1273 static void add_buttons(struct gui *ui,
1274                                 struct button_spec *buttonlist,
1275                                 int nbuttons)
1276 {
1277         int i;
1278
1279         for (i = 0; i < nbuttons; i++)
1280                 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1281 }
1282
1283 static void on_info_bar_response(GtkWidget *widget, gint response,
1284                                  gpointer data)
1285 {
1286         if (response == GTK_RESPONSE_OK) {
1287                 gtk_widget_destroy(widget);
1288                 ui.error_info_bar = NULL;
1289         }
1290 }
1291
1292 void report_error(GError *error)
1293 {
1294         if (ui.error_info_bar == NULL) {
1295                 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1296                                                                GTK_RESPONSE_OK,
1297                                                                NULL);
1298                 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1299                 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1300                                               GTK_MESSAGE_ERROR);
1301                 
1302                 ui.error_label = gtk_label_new(error->message);
1303                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1304                 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1305                 
1306                 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1307                 gtk_widget_show_all(ui.vbox);
1308         } else {
1309                 char buffer[256];
1310                 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1311                 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1312         }
1313 }
1314
1315 static int get_connection_details(char **host, int *port, int *type,
1316                                   int *server_start)
1317 {
1318         GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1319         GtkWidget *button;
1320         char *typeentry;
1321
1322         dialog = gtk_dialog_new_with_buttons("Connection details",
1323                         GTK_WINDOW(ui.window),
1324                         GTK_DIALOG_DESTROY_WITH_PARENT,
1325                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1326                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1327
1328         frame = gtk_frame_new("Hostname / socket name");
1329         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1330         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1331
1332         box = gtk_vbox_new(FALSE, 6);
1333         gtk_container_add(GTK_CONTAINER(frame), box);
1334
1335         hbox = gtk_hbox_new(TRUE, 10);
1336         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1337         hentry = gtk_entry_new();
1338         gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1339         gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1340
1341         frame = gtk_frame_new("Port");
1342         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1343         box = gtk_vbox_new(FALSE, 10);
1344         gtk_container_add(GTK_CONTAINER(frame), box);
1345
1346         hbox = gtk_hbox_new(TRUE, 4);
1347         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1348         pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1349
1350         frame = gtk_frame_new("Type");
1351         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1352         box = gtk_vbox_new(FALSE, 10);
1353         gtk_container_add(GTK_CONTAINER(frame), box);
1354
1355         hbox = gtk_hbox_new(TRUE, 4);
1356         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1357
1358         combo = gtk_combo_box_new_text();
1359         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1360         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1361         gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
1362         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1363
1364         gtk_container_add(GTK_CONTAINER(hbox), combo);
1365
1366         frame = gtk_frame_new("Options");
1367         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1368         box = gtk_vbox_new(FALSE, 10);
1369         gtk_container_add(GTK_CONTAINER(frame), box);
1370
1371         hbox = gtk_hbox_new(TRUE, 4);
1372         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1373
1374         button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1375         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1376         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.");
1377         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1378
1379         gtk_widget_show_all(dialog);
1380
1381         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1382                 gtk_widget_destroy(dialog);
1383                 return 1;
1384         }
1385
1386         *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1387         *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1388
1389         typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
1390         if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1391                 *type = Fio_client_ipv4;
1392         else if (!strncmp(typeentry, "IPv6", 4))
1393                 *type = Fio_client_ipv6;
1394         else
1395                 *type = Fio_client_socket;
1396         g_free(typeentry);
1397
1398         *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1399
1400         gtk_widget_destroy(dialog);
1401         return 0;
1402 }
1403
1404 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1405 {
1406         struct gfio_client *gc;
1407
1408         gc = malloc(sizeof(*gc));
1409         memset(gc, 0, sizeof(*gc));
1410         gc->ui = ui;
1411
1412         client->client_data = gc;
1413 }
1414
1415 static void file_open(GtkWidget *w, gpointer data)
1416 {
1417         GtkWidget *dialog;
1418         struct gui *ui = data;
1419         GSList *filenames, *fn_glist;
1420         GtkFileFilter *filter;
1421         char *host;
1422         int port, type, server_start;
1423
1424         dialog = gtk_file_chooser_dialog_new("Open File",
1425                 GTK_WINDOW(ui->window),
1426                 GTK_FILE_CHOOSER_ACTION_OPEN,
1427                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1428                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1429                 NULL);
1430         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1431
1432         filter = gtk_file_filter_new();
1433         gtk_file_filter_add_pattern(filter, "*.fio");
1434         gtk_file_filter_add_pattern(filter, "*.job");
1435         gtk_file_filter_add_mime_type(filter, "text/fio");
1436         gtk_file_filter_set_name(filter, "Fio job file");
1437         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1438
1439         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1440                 gtk_widget_destroy(dialog);
1441                 return;
1442         }
1443
1444         fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1445
1446         gtk_widget_destroy(dialog);
1447
1448         if (get_connection_details(&host, &port, &type, &server_start))
1449                 goto err;
1450
1451         filenames = fn_glist;
1452         while (filenames != NULL) {
1453                 struct fio_client *client;
1454
1455                 ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1456                 ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1457                 ui->nr_job_files++;
1458
1459                 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1460                 if (!client) {
1461                         GError *error;
1462
1463                         error = g_error_new(g_quark_from_string("fio"), 1,
1464                                         "Failed to add client %s", host);
1465                         report_error(error);
1466                         g_error_free(error);
1467                 }
1468                 gfio_client_added(ui, client);
1469                         
1470                 g_free(filenames->data);
1471                 filenames = g_slist_next(filenames);
1472         }
1473         free(host);
1474
1475         if (server_start)
1476                 gfio_start_server(ui);
1477 err:
1478         g_slist_free(fn_glist);
1479 }
1480
1481 static void file_save(GtkWidget *w, gpointer data)
1482 {
1483         struct gui *ui = data;
1484         GtkWidget *dialog;
1485
1486         dialog = gtk_file_chooser_dialog_new("Save File",
1487                 GTK_WINDOW(ui->window),
1488                 GTK_FILE_CHOOSER_ACTION_SAVE,
1489                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1490                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1491                 NULL);
1492
1493         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1494         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1495
1496         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1497                 char *filename;
1498
1499                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1500                 // save_job_file(filename);
1501                 g_free(filename);
1502         }
1503         gtk_widget_destroy(dialog);
1504 }
1505
1506 static void view_log_destroy(GtkWidget *w, gpointer data)
1507 {
1508         struct gui *ui = (struct gui *) data;
1509
1510         gtk_widget_ref(ui->log_tree);
1511         gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1512         gtk_widget_destroy(w);
1513         ui->log_view = NULL;
1514 }
1515
1516 static void view_log(GtkWidget *w, gpointer data)
1517 {
1518         GtkWidget *win, *scroll, *vbox, *box;
1519         struct gui *ui = (struct gui *) data;
1520
1521         if (ui->log_view)
1522                 return;
1523
1524         ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1525         gtk_window_set_title(GTK_WINDOW(win), "Log");
1526         gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1527
1528         scroll = gtk_scrolled_window_new(NULL, NULL);
1529
1530         gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1531
1532         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1533
1534         box = gtk_hbox_new(TRUE, 0);
1535         gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1536         g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1537         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1538
1539         vbox = gtk_vbox_new(TRUE, 5);
1540         gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1541
1542         gtk_container_add(GTK_CONTAINER(win), vbox);
1543         gtk_widget_show_all(win);
1544 }
1545
1546 static void preferences(GtkWidget *w, gpointer data)
1547 {
1548         GtkWidget *dialog, *frame, *box, **buttons;
1549         int i;
1550
1551         dialog = gtk_dialog_new_with_buttons("Preferences",
1552                 GTK_WINDOW(ui.window),
1553                 GTK_DIALOG_DESTROY_WITH_PARENT,
1554                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1555                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1556                 NULL);
1557
1558         frame = gtk_frame_new("Debug logging");
1559         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1560         box = gtk_hbox_new(FALSE, 6);
1561         gtk_container_add(GTK_CONTAINER(frame), box);
1562
1563         buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1564
1565         for (i = 0; i < FD_DEBUG_MAX; i++) {
1566                 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1567                 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1568                 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1569         }
1570
1571         gtk_widget_show_all(dialog);
1572
1573         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1574                 gtk_widget_destroy(dialog);
1575                 return;
1576         }
1577
1578         for (i = 0; i < FD_DEBUG_MAX; i++) {
1579                 int set;
1580
1581                 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1582                 if (set)
1583                         fio_debug |= (1UL << i);
1584         }
1585
1586         gtk_widget_destroy(dialog);
1587 }
1588
1589 static void about_dialog(GtkWidget *w, gpointer data)
1590 {
1591         gtk_show_about_dialog(NULL,
1592                 "program-name", "gfio",
1593                 "comments", "Gtk2 UI for fio",
1594                 "license", "GPLv2",
1595                 "version", fio_version_string,
1596                 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1597                 "logo-icon-name", "fio",
1598                 /* Must be last: */
1599                 NULL, NULL,
1600                 NULL);
1601 }
1602
1603 static GtkActionEntry menu_items[] = {
1604         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1605         { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1606         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1607         { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
1608         { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
1609         { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1610         { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1611         { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1612         { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
1613 };
1614 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1615
1616 static const gchar *ui_string = " \
1617         <ui> \
1618                 <menubar name=\"MainMenu\"> \
1619                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1620                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1621                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1622                                 <separator name=\"Separator\"/> \
1623                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1624                                 <separator name=\"Separator2\"/> \
1625                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
1626                         </menu> \
1627                         <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1628                                 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1629                         </menu>\
1630                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
1631                                 <menuitem name=\"About\" action=\"About\" /> \
1632                         </menu> \
1633                 </menubar> \
1634         </ui> \
1635 ";
1636
1637 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1638                                    struct gui *ui)
1639 {
1640         GtkActionGroup *action_group = gtk_action_group_new("Menu");
1641         GError *error = 0;
1642
1643         action_group = gtk_action_group_new("Menu");
1644         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1645
1646         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1647         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1648
1649         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1650         return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1651 }
1652
1653 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1654                    GtkWidget *vbox, GtkUIManager *ui_manager)
1655 {
1656         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1657 }
1658
1659 static void init_ui(int *argc, char **argv[], struct gui *ui)
1660 {
1661         GtkSettings *settings;
1662         GtkUIManager *uimanager;
1663         GtkWidget *menu, *probe, *probe_frame, *probe_box;
1664
1665         memset(ui, 0, sizeof(*ui));
1666
1667         /* Magical g*thread incantation, you just need this thread stuff.
1668          * Without it, the update that happens in gfio_update_thread_status
1669          * doesn't really happen in a timely fashion, you need expose events
1670          */
1671         if (!g_thread_supported())
1672                 g_thread_init(NULL);
1673         gdk_threads_init();
1674
1675         gtk_init(argc, argv);
1676         settings = gtk_settings_get_default();
1677         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1678         g_type_init();
1679         
1680         ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1681         gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1682         gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1683
1684         g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1685         g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1686
1687         ui->vbox = gtk_vbox_new(FALSE, 0);
1688         gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1689
1690         uimanager = gtk_ui_manager_new();
1691         menu = get_menubar_menu(ui->window, uimanager, ui);
1692         gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1693
1694         /*
1695          * Set up alignments for widgets at the top of ui, 
1696          * align top left, expand horizontally but not vertically
1697          */
1698         ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1699         ui->topvbox = gtk_vbox_new(FALSE, 3);
1700         gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1701         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1702
1703         probe = gtk_frame_new("Job");
1704         gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1705         probe_frame = gtk_vbox_new(FALSE, 3);
1706         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1707
1708         probe_box = gtk_hbox_new(FALSE, 3);
1709         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1710         ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1711         ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1712         ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1713         ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1714
1715         probe_box = gtk_hbox_new(FALSE, 3);
1716         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1717
1718         ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1719         ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1720         ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1721         ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1722         ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1723         ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1724
1725         probe_box = gtk_hbox_new(FALSE, 3);
1726         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1727         ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1728         ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1729         ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1730         ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1731
1732         /*
1733          * Only add this if we have a commit rate
1734          */
1735 #if 0
1736         probe_box = gtk_hbox_new(FALSE, 3);
1737         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1738
1739         ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1740         ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1741
1742         ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1743         ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1744 #endif
1745
1746         /*
1747          * Add a text box for text op messages 
1748          */
1749         ui->textview = gtk_text_view_new();
1750         ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1751         gtk_text_buffer_set_text(ui->text, "", -1);
1752         gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1753         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1754         ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1755         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1756                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1757         gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1758         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1759                         TRUE, TRUE, 0);
1760
1761         /*
1762          * Set up alignments for widgets at the bottom of ui, 
1763          * align bottom left, expand horizontally but not vertically
1764          */
1765         ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1766         ui->buttonbox = gtk_hbox_new(FALSE, 0);
1767         gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1768         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1769                                         FALSE, FALSE, 0);
1770
1771         add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1772
1773         /*
1774          * Set up thread status progress bar
1775          */
1776         ui->thread_status_pb = gtk_progress_bar_new();
1777         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1778         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1779         gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1780
1781         gfio_ui_setup_log(ui);
1782
1783         gtk_widget_show_all(ui->window);
1784 }
1785
1786 int main(int argc, char *argv[], char *envp[])
1787 {
1788         if (initialize_fio(envp))
1789                 return 1;
1790         if (fio_init_options())
1791                 return 1;
1792
1793         init_ui(&argc, &argv, &ui);
1794
1795         gdk_threads_enter();
1796         gtk_main();
1797         gdk_threads_leave();
1798         return 0;
1799 }