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