client: send probe from fio_client_connect()
[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 #include <string.h>
27
28 #include <glib.h>
29 #include <cairo.h>
30 #include <gtk/gtk.h>
31
32 #include "fio.h"
33 #include "graph.h"
34
35 static int gfio_server_running;
36 static const char *gfio_graph_font;
37
38 static void view_log(GtkWidget *w, gpointer data);
39
40 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
41
42 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
43
44 static void connect_clicked(GtkWidget *widget, gpointer data);
45 static void start_job_clicked(GtkWidget *widget, gpointer data);
46 static void send_clicked(GtkWidget *widget, gpointer data);
47
48 static struct button_spec {
49         const char *buttontext;
50         clickfunction f;
51         const char *tooltiptext;
52         const int start_insensitive;
53 } buttonspeclist[] = {
54 #define CONNECT_BUTTON 0
55 #define SEND_BUTTON 1
56 #define START_JOB_BUTTON 2
57         { "Connect", connect_clicked, "Connect to host", 0 },
58         { "Send", send_clicked, "Send job description to host", 1 },
59         { "Start Job", start_job_clicked,
60                 "Start the current job on the server", 1 },
61 };
62
63 struct probe_widget {
64         GtkWidget *hostname;
65         GtkWidget *os;
66         GtkWidget *arch;
67         GtkWidget *fio_ver;
68 };
69
70 struct eta_widget {
71         GtkWidget *names;
72         GtkWidget *iotype;
73         GtkWidget *ioengine;
74         GtkWidget *iodepth;
75         GtkWidget *jobs;
76         GtkWidget *files;
77         GtkWidget *read_bw;
78         GtkWidget *read_iops;
79         GtkWidget *cr_bw;
80         GtkWidget *cr_iops;
81         GtkWidget *write_bw;
82         GtkWidget *write_iops;
83         GtkWidget *cw_bw;
84         GtkWidget *cw_iops;
85 };
86
87 struct gfio_graphs {
88 #define DRAWING_AREA_XDIM 1000
89 #define DRAWING_AREA_YDIM 400
90         GtkWidget *drawing_area;
91         int drawing_area_xdim;
92         int drawing_area_ydim;
93
94         struct graph *iops_graph;
95         struct graph *bandwidth_graph;
96 };
97
98 /*
99  * Main window widgets and data
100  */
101 struct gui {
102         GtkWidget *window;
103         GtkWidget *vbox;
104         GtkWidget *topvbox;
105         GtkWidget *topalign;
106         GtkWidget *bottomalign;
107         GtkWidget *thread_status_pb;
108         GtkWidget *buttonbox;
109         GtkWidget *scrolled_window;
110         GtkWidget *notebook;
111         GtkWidget *error_info_bar;
112         GtkWidget *error_label;
113         GtkListStore *log_model;
114         GtkWidget *log_tree;
115         GtkWidget *log_view;
116         struct gfio_graphs graphs;
117         struct probe_widget probe;
118         struct eta_widget eta;
119         pthread_t server_t;
120
121         pthread_t t;
122         int handler_running;
123
124         struct flist_head list;
125 } main_ui;
126
127 /*
128  * Notebook entry
129  */
130 struct gui_entry {
131         struct flist_head list;
132         struct gui *ui;
133
134         GtkWidget *vbox;
135         GtkWidget *topvbox;
136         GtkWidget *topalign;
137         GtkWidget *bottomalign;
138         GtkWidget *thread_status_pb;
139         GtkWidget *buttonbox;
140         GtkWidget *button[ARRAYSIZE(buttonspeclist)];
141         GtkWidget *scrolled_window;
142         GtkWidget *notebook;
143         GtkWidget *error_info_bar;
144         GtkWidget *error_label;
145         GtkWidget *results_notebook;
146         GtkWidget *results_window;
147         GtkListStore *log_model;
148         GtkWidget *log_tree;
149         GtkWidget *log_view;
150         struct gfio_graphs graphs;
151         struct probe_widget probe;
152         struct eta_widget eta;
153         GtkWidget *page_label;
154         gint page_num;
155         int connected;
156
157         struct gfio_client *client;
158         int nr_job_files;
159         char **job_files;
160 };
161
162 struct gfio_client {
163         struct gui_entry *ge;
164         struct fio_client *client;
165         GtkWidget *results_widget;
166         GtkWidget *disk_util_frame;
167         GtkWidget *err_entry;
168         unsigned int job_added;
169         struct thread_options o;
170 };
171
172 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
173 static void gfio_update_thread_status_all(char *status_message, double perc);
174 void report_error(GError *error);
175
176 static struct graph *setup_iops_graph(void)
177 {
178         struct graph *g;
179
180         g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
181         graph_title(g, "IOPS");
182         graph_x_title(g, "Time (secs)");
183         graph_y_title(g, "IOs / sec");
184         graph_add_label(g, "Read IOPS");
185         graph_add_label(g, "Write IOPS");
186         graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
187         graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
188         line_graph_set_data_count_limit(g, 100);
189         return g;
190 }
191
192 static struct graph *setup_bandwidth_graph(void)
193 {
194         struct graph *g;
195
196         g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
197         graph_title(g, "Bandwidth");
198         graph_x_title(g, "Time (secs)");
199         graph_y_title(g, "Kbytes / sec");
200         graph_add_label(g, "Read Bandwidth");
201         graph_add_label(g, "Write Bandwidth");
202         graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
203         graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
204         line_graph_set_data_count_limit(g, 100);
205         return g;
206 }
207
208 static void setup_graphs(struct gfio_graphs *g)
209 {
210         g->iops_graph = setup_iops_graph();
211         g->bandwidth_graph = setup_bandwidth_graph();
212 }
213
214 static void clear_ge_ui_info(struct gui_entry *ge)
215 {
216         gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
217         gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
218         gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
219         gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
220 #if 0
221         /* should we empty it... */
222         gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
223 #endif
224         gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
225         gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
226         gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
227         gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
228         gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
229         gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
230         gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
231         gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
232         gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
233 }
234
235 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
236 {
237         GtkWidget *entry, *frame;
238
239         frame = gtk_frame_new(label);
240         entry = gtk_combo_box_new_text();
241         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
242         gtk_container_add(GTK_CONTAINER(frame), entry);
243
244         return entry;
245 }
246
247 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
248 {
249         GtkWidget *entry, *frame;
250
251         frame = gtk_frame_new(label);
252         entry = gtk_entry_new();
253         gtk_entry_set_editable(GTK_ENTRY(entry), 0);
254         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
255         gtk_container_add(GTK_CONTAINER(frame), entry);
256
257         return entry;
258 }
259
260 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
261 {
262         GtkWidget *label_widget;
263         GtkWidget *frame;
264
265         frame = gtk_frame_new(label);
266         label_widget = gtk_label_new(NULL);
267         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
268         gtk_container_add(GTK_CONTAINER(frame), label_widget);
269
270         return label_widget;
271 }
272
273 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
274 {
275         GtkWidget *button, *box;
276
277         box = gtk_hbox_new(FALSE, 3);
278         gtk_container_add(GTK_CONTAINER(hbox), box);
279
280         button = gtk_spin_button_new_with_range(min, max, 1.0);
281         gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
282
283         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
284         gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
285
286         return button;
287 }
288
289 static void gfio_set_connected(struct gui_entry *ge, int connected)
290 {
291         if (connected) {
292                 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
293                 ge->connected = 1;
294                 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
295                 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
296         } else {
297                 ge->connected = 0;
298                 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
299                 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
300                 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
301                 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
302         }
303 }
304
305 static void label_set_int_value(GtkWidget *entry, unsigned int val)
306 {
307         char tmp[80];
308
309         sprintf(tmp, "%u", val);
310         gtk_label_set_text(GTK_LABEL(entry), tmp);
311 }
312
313 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
314 {
315         char tmp[80];
316
317         sprintf(tmp, "%u", val);
318         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
319 }
320
321 #define ALIGN_LEFT 1
322 #define ALIGN_RIGHT 2
323 #define INVISIBLE 4
324 #define UNSORTABLE 8
325
326 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
327 {
328         GtkCellRenderer *renderer;
329         GtkTreeViewColumn *col;
330         double xalign = 0.0; /* left as default */
331         PangoAlignment align;
332         gboolean visible;
333
334         align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
335                 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
336                 PANGO_ALIGN_CENTER;
337         visible = !(flags & INVISIBLE);
338
339         renderer = gtk_cell_renderer_text_new();
340         col = gtk_tree_view_column_new();
341
342         gtk_tree_view_column_set_title(col, title);
343         if (!(flags & UNSORTABLE))
344                 gtk_tree_view_column_set_sort_column_id(col, index);
345         gtk_tree_view_column_set_resizable(col, TRUE);
346         gtk_tree_view_column_pack_start(col, renderer, TRUE);
347         gtk_tree_view_column_add_attribute(col, renderer, "text", index);
348         gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
349         switch (align) {
350         case PANGO_ALIGN_LEFT:
351                 xalign = 0.0;
352                 break;
353         case PANGO_ALIGN_CENTER:
354                 xalign = 0.5;
355                 break;
356         case PANGO_ALIGN_RIGHT:
357                 xalign = 1.0;
358                 break;
359         }
360         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
361         gtk_tree_view_column_set_visible(col, visible);
362         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
363         return col;
364 }
365
366 static void gfio_ui_setup_log(struct gui *ui)
367 {
368         GtkTreeSelection *selection;
369         GtkListStore *model;
370         GtkWidget *tree_view;
371
372         model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
373
374         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
375         gtk_widget_set_can_focus(tree_view, FALSE);
376
377         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
378         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
379         g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
380                 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
381
382         tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
383         tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
384         tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
385         tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
386
387         ui->log_model = model;
388         ui->log_tree = tree_view;
389 }
390
391 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
392                                                fio_fp64_t *plist,
393                                                unsigned int len,
394                                                const char *base,
395                                                unsigned int scale)
396 {
397         GType types[FIO_IO_U_LIST_MAX_LEN];
398         GtkWidget *tree_view;
399         GtkTreeSelection *selection;
400         GtkListStore *model;
401         GtkTreeIter iter;
402         int i;
403
404         for (i = 0; i < len; i++)
405                 types[i] = G_TYPE_INT;
406
407         model = gtk_list_store_newv(len, types);
408
409         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
410         gtk_widget_set_can_focus(tree_view, FALSE);
411
412         g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
413                 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
414
415         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
416         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
417
418         for (i = 0; i < len; i++) {
419                 char fbuf[8];
420
421                 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
422                 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
423         }
424
425         gtk_list_store_append(model, &iter);
426
427         for (i = 0; i < len; i++) {
428                 if (scale)
429                         ovals[i] = (ovals[i] + 999) / 1000;
430                 gtk_list_store_set(model, &iter, i, ovals[i], -1);
431         }
432
433         return tree_view;
434 }
435
436 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
437                                        int ddir)
438 {
439         unsigned int *io_u_plat = ts->io_u_plat[ddir];
440         unsigned long nr = ts->clat_stat[ddir].samples;
441         fio_fp64_t *plist = ts->percentile_list;
442         unsigned int *ovals, len, minv, maxv, scale_down;
443         const char *base;
444         GtkWidget *tree_view, *frame, *hbox;
445         char tmp[64];
446
447         len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
448         if (!len)
449                 goto out;
450
451         /*
452          * We default to usecs, but if the value range is such that we
453          * should scale down to msecs, do that.
454          */
455         if (minv > 2000 && maxv > 99999) {
456                 scale_down = 1;
457                 base = "msec";
458         } else {
459                 scale_down = 0;
460                 base = "usec";
461         }
462
463         tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
464
465         sprintf(tmp, "Completion percentiles (%s)", base);
466         frame = gtk_frame_new(tmp);
467         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
468
469         hbox = gtk_hbox_new(FALSE, 3);
470         gtk_container_add(GTK_CONTAINER(frame), hbox);
471
472         gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
473 out:
474         if (ovals)
475                 free(ovals);
476 }
477
478 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
479                           unsigned long max, double mean, double dev)
480 {
481         const char *base = "(usec)";
482         GtkWidget *hbox, *label, *frame;
483         char *minp, *maxp;
484         char tmp[64];
485
486         if (!usec_to_msec(&min, &max, &mean, &dev))
487                 base = "(msec)";
488
489         minp = num2str(min, 6, 1, 0);
490         maxp = num2str(max, 6, 1, 0);
491
492         sprintf(tmp, "%s %s", name, base);
493         frame = gtk_frame_new(tmp);
494         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
495
496         hbox = gtk_hbox_new(FALSE, 3);
497         gtk_container_add(GTK_CONTAINER(frame), hbox);
498
499         label = new_info_label_in_frame(hbox, "Minimum");
500         gtk_label_set_text(GTK_LABEL(label), minp);
501         label = new_info_label_in_frame(hbox, "Maximum");
502         gtk_label_set_text(GTK_LABEL(label), maxp);
503         label = new_info_label_in_frame(hbox, "Average");
504         sprintf(tmp, "%5.02f", mean);
505         gtk_label_set_text(GTK_LABEL(label), tmp);
506         label = new_info_label_in_frame(hbox, "Standard deviation");
507         sprintf(tmp, "%5.02f", dev);
508         gtk_label_set_text(GTK_LABEL(label), tmp);
509
510         free(minp);
511         free(maxp);
512
513 }
514
515 #define GFIO_CLAT       1
516 #define GFIO_SLAT       2
517 #define GFIO_LAT        4
518
519 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
520                                   struct thread_stat *ts, int ddir)
521 {
522         const char *ddir_label[2] = { "Read", "Write" };
523         GtkWidget *frame, *label, *box, *vbox, *main_vbox;
524         unsigned long min[3], max[3], runt;
525         unsigned long long bw, iops;
526         unsigned int flags = 0;
527         double mean[3], dev[3];
528         char *io_p, *bw_p, *iops_p;
529         int i2p;
530
531         if (!ts->runtime[ddir])
532                 return;
533
534         i2p = is_power_of_2(rs->kb_base);
535         runt = ts->runtime[ddir];
536
537         bw = (1000 * ts->io_bytes[ddir]) / runt;
538         io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
539         bw_p = num2str(bw, 6, 1, i2p);
540
541         iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
542         iops_p = num2str(iops, 6, 1, 0);
543
544         box = gtk_hbox_new(FALSE, 3);
545         gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
546
547         frame = gtk_frame_new(ddir_label[ddir]);
548         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
549
550         main_vbox = gtk_vbox_new(FALSE, 3);
551         gtk_container_add(GTK_CONTAINER(frame), main_vbox);
552
553         box = gtk_hbox_new(FALSE, 3);
554         gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
555
556         label = new_info_label_in_frame(box, "IO");
557         gtk_label_set_text(GTK_LABEL(label), io_p);
558         label = new_info_label_in_frame(box, "Bandwidth");
559         gtk_label_set_text(GTK_LABEL(label), bw_p);
560         label = new_info_label_in_frame(box, "IOPS");
561         gtk_label_set_text(GTK_LABEL(label), iops_p);
562         label = new_info_label_in_frame(box, "Runtime (msec)");
563         label_set_int_value(label, ts->runtime[ddir]);
564
565         if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
566                 double p_of_agg = 100.0;
567                 const char *bw_str = "KB";
568                 char tmp[32];
569
570                 if (rs->agg[ddir]) {
571                         p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
572                         if (p_of_agg > 100.0)
573                                 p_of_agg = 100.0;
574                 }
575
576                 if (mean[0] > 999999.9) {
577                         min[0] /= 1000.0;
578                         max[0] /= 1000.0;
579                         mean[0] /= 1000.0;
580                         dev[0] /= 1000.0;
581                         bw_str = "MB";
582                 }
583
584                 sprintf(tmp, "Bandwidth (%s)", bw_str);
585                 frame = gtk_frame_new(tmp);
586                 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
587
588                 box = gtk_hbox_new(FALSE, 3);
589                 gtk_container_add(GTK_CONTAINER(frame), box);
590
591                 label = new_info_label_in_frame(box, "Minimum");
592                 label_set_int_value(label, min[0]);
593                 label = new_info_label_in_frame(box, "Maximum");
594                 label_set_int_value(label, max[0]);
595                 label = new_info_label_in_frame(box, "Percentage of jobs");
596                 sprintf(tmp, "%3.2f%%", p_of_agg);
597                 gtk_label_set_text(GTK_LABEL(label), tmp);
598                 label = new_info_label_in_frame(box, "Average");
599                 sprintf(tmp, "%5.02f", mean[0]);
600                 gtk_label_set_text(GTK_LABEL(label), tmp);
601                 label = new_info_label_in_frame(box, "Standard deviation");
602                 sprintf(tmp, "%5.02f", dev[0]);
603                 gtk_label_set_text(GTK_LABEL(label), tmp);
604         }
605
606         if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
607                 flags |= GFIO_SLAT;
608         if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
609                 flags |= GFIO_CLAT;
610         if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
611                 flags |= GFIO_LAT;
612
613         if (flags) {
614                 frame = gtk_frame_new("Latency");
615                 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
616
617                 vbox = gtk_vbox_new(FALSE, 3);
618                 gtk_container_add(GTK_CONTAINER(frame), vbox);
619
620                 if (flags & GFIO_SLAT)
621                         gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
622                 if (flags & GFIO_CLAT)
623                         gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
624                 if (flags & GFIO_LAT)
625                         gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
626         }
627
628         if (ts->clat_percentiles)
629                 gfio_show_clat_percentiles(main_vbox, ts, ddir);
630
631
632         free(io_p);
633         free(bw_p);
634         free(iops_p);
635 }
636
637 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
638                                           const char **labels)
639 {
640         GtkWidget *tree_view;
641         GtkTreeSelection *selection;
642         GtkListStore *model;
643         GtkTreeIter iter;
644         GType *types;
645         int i, skipped;
646
647         /*
648          * Check if all are empty, in which case don't bother
649          */
650         for (i = 0, skipped = 0; i < num; i++)
651                 if (lat[i] <= 0.0)
652                         skipped++;
653
654         if (skipped == num)
655                 return NULL;
656
657         types = malloc(num * sizeof(GType));
658
659         for (i = 0; i < num; i++)
660                 types[i] = G_TYPE_STRING;
661
662         model = gtk_list_store_newv(num, types);
663         free(types);
664         types = NULL;
665
666         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
667         gtk_widget_set_can_focus(tree_view, FALSE);
668
669         g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
670                 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
671
672         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
673         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
674
675         for (i = 0; i < num; i++)
676                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
677
678         gtk_list_store_append(model, &iter);
679
680         for (i = 0; i < num; i++) {
681                 char fbuf[32];
682
683                 if (lat[i] <= 0.0)
684                         sprintf(fbuf, "0.00");
685                 else
686                         sprintf(fbuf, "%3.2f%%", lat[i]);
687
688                 gtk_list_store_set(model, &iter, i, fbuf, -1);
689         }
690
691         return tree_view;
692 }
693
694 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
695 {
696         GtkWidget *box, *frame, *tree_view;
697         double io_u_lat_u[FIO_IO_U_LAT_U_NR];
698         double io_u_lat_m[FIO_IO_U_LAT_M_NR];
699         const char *uranges[] = { "2", "4", "10", "20", "50", "100",
700                                   "250", "500", "750", "1000", };
701         const char *mranges[] = { "2", "4", "10", "20", "50", "100",
702                                   "250", "500", "750", "1000", "2000",
703                                   ">= 2000", };
704
705         stat_calc_lat_u(ts, io_u_lat_u);
706         stat_calc_lat_m(ts, io_u_lat_m);
707
708         tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
709         if (tree_view) {
710                 frame = gtk_frame_new("Latency buckets (usec)");
711                 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
712
713                 box = gtk_hbox_new(FALSE, 3);
714                 gtk_container_add(GTK_CONTAINER(frame), box);
715                 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
716         }
717
718         tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
719         if (tree_view) {
720                 frame = gtk_frame_new("Latency buckets (msec)");
721                 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
722
723                 box = gtk_hbox_new(FALSE, 3);
724                 gtk_container_add(GTK_CONTAINER(frame), box);
725                 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
726         }
727 }
728
729 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
730 {
731         GtkWidget *box, *frame, *entry;
732         double usr_cpu, sys_cpu;
733         unsigned long runtime;
734         char tmp[32];
735
736         runtime = ts->total_run_time;
737         if (runtime) {
738                 double runt = (double) runtime;
739
740                 usr_cpu = (double) ts->usr_time * 100 / runt;
741                 sys_cpu = (double) ts->sys_time * 100 / runt;
742         } else {
743                 usr_cpu = 0;
744                 sys_cpu = 0;
745         }
746
747         frame = gtk_frame_new("OS resources");
748         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
749
750         box = gtk_hbox_new(FALSE, 3);
751         gtk_container_add(GTK_CONTAINER(frame), box);
752
753         entry = new_info_entry_in_frame(box, "User CPU");
754         sprintf(tmp, "%3.2f%%", usr_cpu);
755         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
756         entry = new_info_entry_in_frame(box, "System CPU");
757         sprintf(tmp, "%3.2f%%", sys_cpu);
758         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
759         entry = new_info_entry_in_frame(box, "Context switches");
760         entry_set_int_value(entry, ts->ctx);
761         entry = new_info_entry_in_frame(box, "Major faults");
762         entry_set_int_value(entry, ts->majf);
763         entry = new_info_entry_in_frame(box, "Minor faults");
764         entry_set_int_value(entry, ts->minf);
765 }
766 static void gfio_add_sc_depths_tree(GtkListStore *model,
767                                     struct thread_stat *ts, unsigned int len,
768                                     int submit)
769 {
770         double io_u_dist[FIO_IO_U_MAP_NR];
771         GtkTreeIter iter;
772         /* Bits 0, and 3-8 */
773         const int add_mask = 0x1f9;
774         int i, j;
775
776         if (submit)
777                 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
778         else
779                 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
780
781         gtk_list_store_append(model, &iter);
782
783         gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
784
785         for (i = 1, j = 0; i < len; i++) {
786                 char fbuf[32];
787
788                 if (!(add_mask & (1UL << (i - 1))))
789                         sprintf(fbuf, "0.0%%");
790                 else {
791                         sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
792                         j++;
793                 }
794
795                 gtk_list_store_set(model, &iter, i, fbuf, -1);
796         }
797
798 }
799
800 static void gfio_add_total_depths_tree(GtkListStore *model,
801                                        struct thread_stat *ts, unsigned int len)
802 {
803         double io_u_dist[FIO_IO_U_MAP_NR];
804         GtkTreeIter iter;
805         /* Bits 1-6, and 8 */
806         const int add_mask = 0x17e;
807         int i, j;
808
809         stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
810
811         gtk_list_store_append(model, &iter);
812
813         gtk_list_store_set(model, &iter, 0, "Total", -1);
814
815         for (i = 1, j = 0; i < len; i++) {
816                 char fbuf[32];
817
818                 if (!(add_mask & (1UL << (i - 1))))
819                         sprintf(fbuf, "0.0%%");
820                 else {
821                         sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
822                         j++;
823                 }
824
825                 gtk_list_store_set(model, &iter, i, fbuf, -1);
826         }
827
828 }
829
830 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
831 {
832         GtkWidget *frame, *box, *tree_view;
833         GtkTreeSelection *selection;
834         GtkListStore *model;
835         GType types[FIO_IO_U_MAP_NR + 1];
836         int i;
837 #define NR_LABELS       10
838         const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
839
840         frame = gtk_frame_new("IO depths");
841         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
842
843         box = gtk_hbox_new(FALSE, 3);
844         gtk_container_add(GTK_CONTAINER(frame), box);
845
846         for (i = 0; i < NR_LABELS; i++)
847                 types[i] = G_TYPE_STRING;
848
849         model = gtk_list_store_newv(NR_LABELS, types);
850
851         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
852         gtk_widget_set_can_focus(tree_view, FALSE);
853
854         g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
855                 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
856
857         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
858         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
859
860         for (i = 0; i < NR_LABELS; i++)
861                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
862
863         gfio_add_total_depths_tree(model, ts, NR_LABELS);
864         gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
865         gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
866
867         gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
868 }
869
870 static gboolean results_window_delete(GtkWidget *w, gpointer data)
871 {
872         struct gui_entry *ge = (struct gui_entry *) data;
873
874         gtk_widget_destroy(w);
875         ge->results_window = NULL;
876         ge->results_notebook = NULL;
877         return TRUE;
878 }
879
880 static GtkWidget *get_results_window(struct gui_entry *ge)
881 {
882         GtkWidget *win, *notebook;
883
884         if (ge->results_window)
885                 return ge->results_notebook;
886
887         win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
888         gtk_window_set_title(GTK_WINDOW(win), "Results");
889         gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
890         g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
891         g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
892
893         notebook = gtk_notebook_new();
894         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
895         gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
896         gtk_container_add(GTK_CONTAINER(win), notebook);
897
898         ge->results_window = win;
899         ge->results_notebook = notebook;
900         return ge->results_notebook;
901 }
902
903 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
904                             struct group_run_stats *rs)
905 {
906         GtkWidget *res_win, *box, *vbox, *entry, *scroll;
907         struct gfio_client *gc = client->client_data;
908
909         gdk_threads_enter();
910
911         res_win = get_results_window(gc->ge);
912
913         scroll = gtk_scrolled_window_new(NULL, NULL);
914         gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
915         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
916
917         vbox = gtk_vbox_new(FALSE, 3);
918
919         box = gtk_hbox_new(FALSE, 0);
920         gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
921
922         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
923
924         gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
925
926         gc->results_widget = vbox;
927
928         entry = new_info_entry_in_frame(box, "Name");
929         gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
930         if (strlen(ts->description)) {
931                 entry = new_info_entry_in_frame(box, "Description");
932                 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
933         }
934         entry = new_info_entry_in_frame(box, "Group ID");
935         entry_set_int_value(entry, ts->groupid);
936         entry = new_info_entry_in_frame(box, "Jobs");
937         entry_set_int_value(entry, ts->members);
938         gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
939         entry_set_int_value(entry, ts->error);
940         entry = new_info_entry_in_frame(box, "PID");
941         entry_set_int_value(entry, ts->pid);
942
943         if (ts->io_bytes[DDIR_READ])
944                 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
945         if (ts->io_bytes[DDIR_WRITE])
946                 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
947
948         gfio_show_latency_buckets(vbox, ts);
949         gfio_show_cpu_usage(vbox, ts);
950         gfio_show_io_depths(vbox, ts);
951
952         gtk_widget_show_all(gc->ge->results_window);
953         gdk_threads_leave();
954 }
955
956 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
957 {
958         struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
959         struct gui *ui = &main_ui;
960         GtkTreeIter iter;
961         struct tm *tm;
962         time_t sec;
963         char tmp[64], timebuf[80];
964
965         sec = p->log_sec;
966         tm = localtime(&sec);
967         strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
968         sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
969
970         gdk_threads_enter();
971
972         gtk_list_store_append(ui->log_model, &iter);
973         gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
974         gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
975         gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
976         gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
977
978         if (p->level == FIO_LOG_ERR)
979                 view_log(NULL, (gpointer) ui);
980
981         gdk_threads_leave();
982 }
983
984 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
985 {
986         struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
987         struct gfio_client *gc = client->client_data;
988         GtkWidget *box, *frame, *entry, *vbox;
989         double util;
990         char tmp[16];
991
992         gdk_threads_enter();
993
994         if (!gc->results_widget)
995                 goto out;
996
997         if (!gc->disk_util_frame) {
998                 gc->disk_util_frame = gtk_frame_new("Disk utilization");
999                 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1000         }
1001
1002         vbox = gtk_vbox_new(FALSE, 3);
1003         gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1004
1005         frame = gtk_frame_new((char *) p->dus.name);
1006         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1007
1008         box = gtk_vbox_new(FALSE, 3);
1009         gtk_container_add(GTK_CONTAINER(frame), box);
1010
1011         frame = gtk_frame_new("Read");
1012         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1013         vbox = gtk_hbox_new(TRUE, 3);
1014         gtk_container_add(GTK_CONTAINER(frame), vbox);
1015         entry = new_info_entry_in_frame(vbox, "IOs");
1016         entry_set_int_value(entry, p->dus.ios[0]);
1017         entry = new_info_entry_in_frame(vbox, "Merges");
1018         entry_set_int_value(entry, p->dus.merges[0]);
1019         entry = new_info_entry_in_frame(vbox, "Sectors");
1020         entry_set_int_value(entry, p->dus.sectors[0]);
1021         entry = new_info_entry_in_frame(vbox, "Ticks");
1022         entry_set_int_value(entry, p->dus.ticks[0]);
1023
1024         frame = gtk_frame_new("Write");
1025         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1026         vbox = gtk_hbox_new(TRUE, 3);
1027         gtk_container_add(GTK_CONTAINER(frame), vbox);
1028         entry = new_info_entry_in_frame(vbox, "IOs");
1029         entry_set_int_value(entry, p->dus.ios[1]);
1030         entry = new_info_entry_in_frame(vbox, "Merges");
1031         entry_set_int_value(entry, p->dus.merges[1]);
1032         entry = new_info_entry_in_frame(vbox, "Sectors");
1033         entry_set_int_value(entry, p->dus.sectors[1]);
1034         entry = new_info_entry_in_frame(vbox, "Ticks");
1035         entry_set_int_value(entry, p->dus.ticks[1]);
1036
1037         frame = gtk_frame_new("Shared");
1038         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1039         vbox = gtk_hbox_new(TRUE, 3);
1040         gtk_container_add(GTK_CONTAINER(frame), vbox);
1041         entry = new_info_entry_in_frame(vbox, "IO ticks");
1042         entry_set_int_value(entry, p->dus.io_ticks);
1043         entry = new_info_entry_in_frame(vbox, "Time in queue");
1044         entry_set_int_value(entry, p->dus.time_in_queue);
1045
1046         util = 0.0;
1047         if (p->dus.msec)
1048                 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1049         if (util > 100.0)
1050                 util = 100.0;
1051
1052         sprintf(tmp, "%3.2f%%", util);
1053         entry = new_info_entry_in_frame(vbox, "Disk utilization");
1054         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1055
1056         gtk_widget_show_all(gc->results_widget);
1057 out:
1058         gdk_threads_leave();
1059 }
1060
1061 extern int sum_stat_clients;
1062 extern struct thread_stat client_ts;
1063 extern struct group_run_stats client_gs;
1064
1065 static int sum_stat_nr;
1066
1067 static void gfio_thread_status_op(struct fio_client *client,
1068                                   struct fio_net_cmd *cmd)
1069 {
1070         struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1071
1072         gfio_display_ts(client, &p->ts, &p->rs);
1073
1074         if (sum_stat_clients == 1)
1075                 return;
1076
1077         sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1078         sum_group_stats(&client_gs, &p->rs);
1079
1080         client_ts.members++;
1081         client_ts.groupid = p->ts.groupid;
1082
1083         if (++sum_stat_nr == sum_stat_clients) {
1084                 strcpy(client_ts.name, "All clients");
1085                 gfio_display_ts(client, &client_ts, &client_gs);
1086         }
1087 }
1088
1089 static void gfio_group_stats_op(struct fio_client *client,
1090                                 struct fio_net_cmd *cmd)
1091 {
1092         /* We're ignoring group stats for now */
1093 }
1094
1095 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1096                                    gpointer data)
1097 {
1098         struct gfio_graphs *g = data;
1099
1100         g->drawing_area_xdim = w->allocation.width;
1101         g->drawing_area_ydim = w->allocation.height;
1102         return TRUE;
1103 }
1104
1105 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1106 {
1107         struct gfio_graphs *g = p;
1108         cairo_t *cr;
1109
1110         graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1111                                         g->drawing_area_ydim);
1112         graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1113                                         g->drawing_area_ydim);
1114         cr = gdk_cairo_create(w->window);
1115
1116         cairo_set_source_rgb(cr, 0, 0, 0);
1117
1118         cairo_save(cr);
1119         cairo_translate(cr, 0, 0);
1120         line_graph_draw(g->bandwidth_graph, cr);
1121         cairo_stroke(cr);
1122         cairo_restore(cr);
1123
1124         cairo_save(cr);
1125         cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1126         line_graph_draw(g->iops_graph, cr);
1127         cairo_stroke(cr);
1128         cairo_restore(cr);
1129         cairo_destroy(cr);
1130
1131         return FALSE;
1132 }
1133
1134 /*
1135  * Client specific ETA
1136  */
1137 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1138 {
1139         struct gfio_client *gc = client->client_data;
1140         struct gui_entry *ge = gc->ge;
1141         static int eta_good;
1142         char eta_str[128];
1143         char output[256];
1144         char tmp[32];
1145         double perc = 0.0;
1146         int i2p = 0;
1147
1148         gdk_threads_enter();
1149
1150         eta_str[0] = '\0';
1151         output[0] = '\0';
1152
1153         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1154                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1155                 eta_to_str(eta_str, je->eta_sec);
1156         }
1157
1158         sprintf(tmp, "%u", je->nr_running);
1159         gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1160         sprintf(tmp, "%u", je->files_open);
1161         gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1162
1163 #if 0
1164         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1165         if (je->m_rate || je->t_rate) {
1166                 char *tr, *mr;
1167
1168                 mr = num2str(je->m_rate, 4, 0, i2p);
1169                 tr = num2str(je->t_rate, 4, 0, i2p);
1170                 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1171                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1172                 free(tr);
1173                 free(mr);
1174         } else if (je->m_iops || je->t_iops)
1175                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1176
1177         gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1178         gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1179         gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1180         gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1181 #endif
1182
1183         if (je->eta_sec != INT_MAX && je->nr_running) {
1184                 char *iops_str[2];
1185                 char *rate_str[2];
1186
1187                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1188                         strcpy(output, "-.-% done");
1189                 else {
1190                         eta_good = 1;
1191                         perc *= 100.0;
1192                         sprintf(output, "%3.1f%% done", perc);
1193                 }
1194
1195                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1196                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1197
1198                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1199                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1200
1201                 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1202                 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1203                 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1204                 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1205
1206                 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1207                 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1208                 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1209                 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1210
1211                 free(rate_str[0]);
1212                 free(rate_str[1]);
1213                 free(iops_str[0]);
1214                 free(iops_str[1]);
1215         }
1216
1217         if (eta_str[0]) {
1218                 char *dst = output + strlen(output);
1219
1220                 sprintf(dst, " - %s", eta_str);
1221         }
1222                 
1223         gfio_update_thread_status(ge, output, perc);
1224         gdk_threads_leave();
1225 }
1226
1227 /*
1228  * Update ETA in main window for all clients
1229  */
1230 static void gfio_update_all_eta(struct jobs_eta *je)
1231 {
1232         struct gui *ui = &main_ui;
1233         static int eta_good;
1234         char eta_str[128];
1235         char output[256];
1236         double perc = 0.0;
1237         int i2p = 0;
1238
1239         gdk_threads_enter();
1240
1241         eta_str[0] = '\0';
1242         output[0] = '\0';
1243
1244         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1245                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1246                 eta_to_str(eta_str, je->eta_sec);
1247         }
1248
1249 #if 0
1250         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1251         if (je->m_rate || je->t_rate) {
1252                 char *tr, *mr;
1253
1254                 mr = num2str(je->m_rate, 4, 0, i2p);
1255                 tr = num2str(je->t_rate, 4, 0, i2p);
1256                 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1257                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1258                 free(tr);
1259                 free(mr);
1260         } else if (je->m_iops || je->t_iops)
1261                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1262
1263         gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1264         gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1265         gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1266         gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1267 #endif
1268
1269         entry_set_int_value(ui->eta.jobs, je->nr_running);
1270
1271         if (je->eta_sec != INT_MAX && je->nr_running) {
1272                 char *iops_str[2];
1273                 char *rate_str[2];
1274
1275                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1276                         strcpy(output, "-.-% done");
1277                 else {
1278                         eta_good = 1;
1279                         perc *= 100.0;
1280                         sprintf(output, "%3.1f%% done", perc);
1281                 }
1282
1283                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1284                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1285
1286                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1287                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1288
1289                 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1290                 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1291                 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1292                 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1293
1294                 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1295                 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1296                 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1297                 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1298
1299                 free(rate_str[0]);
1300                 free(rate_str[1]);
1301                 free(iops_str[0]);
1302                 free(iops_str[1]);
1303         }
1304
1305         if (eta_str[0]) {
1306                 char *dst = output + strlen(output);
1307
1308                 sprintf(dst, " - %s", eta_str);
1309         }
1310                 
1311         gfio_update_thread_status_all(output, perc);
1312         gdk_threads_leave();
1313 }
1314
1315 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1316 {
1317         struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1318         struct gfio_client *gc = client->client_data;
1319         struct gui_entry *ge = gc->ge;
1320         const char *os, *arch;
1321         char buf[64];
1322
1323         os = fio_get_os_string(probe->os);
1324         if (!os)
1325                 os = "unknown";
1326
1327         arch = fio_get_arch_string(probe->arch);
1328         if (!arch)
1329                 os = "unknown";
1330
1331         if (!client->name)
1332                 client->name = strdup((char *) probe->hostname);
1333
1334         gdk_threads_enter();
1335
1336         gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1337         gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1338         gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1339         sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1340         gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1341
1342         gfio_set_connected(ge, 1);
1343
1344         gdk_threads_leave();
1345 }
1346
1347 static void gfio_update_thread_status(struct gui_entry *ge,
1348                                       char *status_message, double perc)
1349 {
1350         static char message[100];
1351         const char *m = message;
1352
1353         strncpy(message, status_message, sizeof(message) - 1);
1354         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1355         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1356         gtk_widget_queue_draw(main_ui.window);
1357 }
1358
1359 static void gfio_update_thread_status_all(char *status_message, double perc)
1360 {
1361         struct gui *ui = &main_ui;
1362         static char message[100];
1363         const char *m = message;
1364
1365         strncpy(message, status_message, sizeof(message) - 1);
1366         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1367         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1368         gtk_widget_queue_draw(ui->window);
1369 }
1370
1371 static void gfio_quit_op(struct fio_client *client)
1372 {
1373         struct gfio_client *gc = client->client_data;
1374
1375         gdk_threads_enter();
1376         gfio_set_connected(gc->ge, 0);
1377         gdk_threads_leave();
1378 }
1379
1380 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1381 {
1382         struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1383         struct gfio_client *gc = client->client_data;
1384         struct thread_options *o = &gc->o;
1385         struct gui_entry *ge = gc->ge;
1386         char tmp[8];
1387
1388         convert_thread_options_to_cpu(o, &p->top);
1389
1390         gdk_threads_enter();
1391
1392         gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1393
1394         gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1395         gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1396
1397         gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1398         gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1399
1400         sprintf(tmp, "%u", o->iodepth);
1401         gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1402
1403         gc->job_added++;
1404
1405         gdk_threads_leave();
1406 }
1407
1408 static void gfio_client_timed_out(struct fio_client *client)
1409 {
1410         struct gfio_client *gc = client->client_data;
1411         GtkWidget *dialog, *label, *content;
1412         char buf[256];
1413
1414         gdk_threads_enter();
1415
1416         gfio_set_connected(gc->ge, 0);
1417         clear_ge_ui_info(gc->ge);
1418
1419         sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1420
1421         dialog = gtk_dialog_new_with_buttons("Timed out!",
1422                         GTK_WINDOW(main_ui.window),
1423                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1424                         GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1425
1426         /* gtk_dialog_get_content_area() is 2.14 and newer */
1427         content = GTK_DIALOG(dialog)->vbox;
1428
1429         label = gtk_label_new((const gchar *) buf);
1430         gtk_container_add(GTK_CONTAINER(content), label);
1431         gtk_widget_show_all(dialog);
1432         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1433
1434         gtk_dialog_run(GTK_DIALOG(dialog));
1435         gtk_widget_destroy(dialog);
1436
1437         gdk_threads_leave();
1438 }
1439
1440 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1441 {
1442         struct gfio_client *gc = client->client_data;
1443
1444         gdk_threads_enter();
1445
1446         gfio_set_connected(gc->ge, 0);
1447
1448         if (gc->err_entry)
1449                 entry_set_int_value(gc->err_entry, client->error);
1450
1451         gdk_threads_leave();
1452 }
1453
1454 struct client_ops gfio_client_ops = {
1455         .text_op                = gfio_text_op,
1456         .disk_util              = gfio_disk_util_op,
1457         .thread_status          = gfio_thread_status_op,
1458         .group_stats            = gfio_group_stats_op,
1459         .jobs_eta               = gfio_update_client_eta,
1460         .eta                    = gfio_update_all_eta,
1461         .probe                  = gfio_probe_op,
1462         .quit                   = gfio_quit_op,
1463         .add_job                = gfio_add_job_op,
1464         .timed_out              = gfio_client_timed_out,
1465         .stop                   = gfio_client_stop,
1466         .stay_connected         = 1,
1467 };
1468
1469 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1470                 __attribute__((unused)) gpointer data)
1471 {
1472         gtk_main_quit();
1473 }
1474
1475 static void *job_thread(void *arg)
1476 {
1477         struct gui *ui = arg;
1478
1479         ui->handler_running = 1;
1480         fio_handle_clients(&gfio_client_ops);
1481         ui->handler_running = 0;
1482         return NULL;
1483 }
1484
1485 static int send_job_files(struct gui_entry *ge)
1486 {
1487         struct gfio_client *gc = ge->client;
1488         int i, ret = 0;
1489
1490         for (i = 0; i < ge->nr_job_files; i++) {
1491                 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1492                 if (ret < 0) {
1493                         GError *error;
1494
1495                         error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1496                         report_error(error);
1497                         g_error_free(error);
1498                         break;
1499                 } else if (ret)
1500                         break;
1501
1502                 free(ge->job_files[i]);
1503                 ge->job_files[i] = NULL;
1504         }
1505         while (i < ge->nr_job_files) {
1506                 free(ge->job_files[i]);
1507                 ge->job_files[i] = NULL;
1508                 i++;
1509         }
1510
1511         return ret;
1512 }
1513
1514 static void *server_thread(void *arg)
1515 {
1516         is_backend = 1;
1517         gfio_server_running = 1;
1518         fio_start_server(NULL);
1519         gfio_server_running = 0;
1520         return NULL;
1521 }
1522
1523 static void gfio_start_server(void)
1524 {
1525         struct gui *ui = &main_ui;
1526
1527         if (!gfio_server_running) {
1528                 gfio_server_running = 1;
1529                 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1530                 pthread_detach(ui->server_t);
1531         }
1532 }
1533
1534 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1535                 gpointer data)
1536 {
1537         struct gui_entry *ge = data;
1538         struct gfio_client *gc = ge->client;
1539
1540         gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1541         fio_start_client(gc->client);
1542 }
1543
1544 static void file_open(GtkWidget *w, gpointer data);
1545
1546 static void connect_clicked(GtkWidget *widget, gpointer data)
1547 {
1548         struct gui_entry *ge = data;
1549         struct gfio_client *gc = ge->client;
1550
1551         if (!ge->connected) {
1552                 int ret;
1553
1554                 if (!ge->nr_job_files)
1555                         file_open(widget, data);
1556                 if (!ge->nr_job_files)
1557                         return;
1558
1559                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1560                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1561                 ret = fio_client_connect(gc->client);
1562                 if (!ret) {
1563                         if (!ge->ui->handler_running)
1564                                 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1565                         gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1566                         gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1567                 } else {
1568                         GError *error;
1569
1570                         error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1571                         report_error(error);
1572                         g_error_free(error);
1573                 }
1574         } else {
1575                 fio_client_terminate(gc->client);
1576                 gfio_set_connected(ge, 0);
1577                 clear_ge_ui_info(ge);
1578         }
1579 }
1580
1581 static void send_clicked(GtkWidget *widget, gpointer data)
1582 {
1583         struct gui_entry *ge = data;
1584
1585         if (send_job_files(ge)) {
1586                 GError *error;
1587
1588                 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1589                 report_error(error);
1590                 g_error_free(error);
1591
1592                 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1593         }
1594
1595         gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1596         gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1597 }
1598
1599 static GtkWidget *add_button(GtkWidget *buttonbox,
1600                              struct button_spec *buttonspec, gpointer data)
1601 {
1602         GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1603
1604         g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1605         gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1606         gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1607         gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1608
1609         return button;
1610 }
1611
1612 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1613                         int nbuttons)
1614 {
1615         int i;
1616
1617         for (i = 0; i < nbuttons; i++)
1618                 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1619 }
1620
1621 static void on_info_bar_response(GtkWidget *widget, gint response,
1622                                  gpointer data)
1623 {
1624         struct gui *ui = &main_ui;
1625
1626         if (response == GTK_RESPONSE_OK) {
1627                 gtk_widget_destroy(widget);
1628                 ui->error_info_bar = NULL;
1629         }
1630 }
1631
1632 void report_error(GError *error)
1633 {
1634         struct gui *ui = &main_ui;
1635
1636         if (ui->error_info_bar == NULL) {
1637                 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1638                                                                GTK_RESPONSE_OK,
1639                                                                NULL);
1640                 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1641                 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1642                                               GTK_MESSAGE_ERROR);
1643                 
1644                 ui->error_label = gtk_label_new(error->message);
1645                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1646                 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1647                 
1648                 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1649                 gtk_widget_show_all(ui->vbox);
1650         } else {
1651                 char buffer[256];
1652                 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1653                 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1654         }
1655 }
1656
1657 struct connection_widgets
1658 {
1659         GtkWidget *hentry;
1660         GtkWidget *combo;
1661         GtkWidget *button;
1662 };
1663
1664 static void hostname_cb(GtkEntry *entry, gpointer data)
1665 {
1666         struct connection_widgets *cw = data;
1667         int uses_net = 0, is_localhost = 0;
1668         const gchar *text;
1669         gchar *ctext;
1670
1671         /*
1672          * Check whether to display the 'auto start backend' box
1673          * or not. Show it if we are a localhost and using network,
1674          * or using a socket.
1675          */
1676         ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1677         if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1678                 uses_net = 1;
1679         g_free(ctext);
1680
1681         if (uses_net) {
1682                 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1683                 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1684                     !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1685                     !strcmp(text, "ip6-loopback"))
1686                         is_localhost = 1;
1687         }
1688
1689         if (!uses_net || is_localhost) {
1690                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1691                 gtk_widget_set_sensitive(cw->button, 1);
1692         } else {
1693                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1694                 gtk_widget_set_sensitive(cw->button, 0);
1695         }
1696 }
1697
1698 static int get_connection_details(char **host, int *port, int *type,
1699                                   int *server_start)
1700 {
1701         GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1702         struct connection_widgets cw;
1703         char *typeentry;
1704
1705         dialog = gtk_dialog_new_with_buttons("Connection details",
1706                         GTK_WINDOW(main_ui.window),
1707                         GTK_DIALOG_DESTROY_WITH_PARENT,
1708                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1709                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1710
1711         frame = gtk_frame_new("Hostname / socket name");
1712         /* gtk_dialog_get_content_area() is 2.14 and newer */
1713         vbox = GTK_DIALOG(dialog)->vbox;
1714         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1715
1716         box = gtk_vbox_new(FALSE, 6);
1717         gtk_container_add(GTK_CONTAINER(frame), box);
1718
1719         hbox = gtk_hbox_new(TRUE, 10);
1720         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1721         cw.hentry = gtk_entry_new();
1722         gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1723         gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1724
1725         frame = gtk_frame_new("Port");
1726         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1727         box = gtk_vbox_new(FALSE, 10);
1728         gtk_container_add(GTK_CONTAINER(frame), box);
1729
1730         hbox = gtk_hbox_new(TRUE, 4);
1731         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1732         pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1733
1734         frame = gtk_frame_new("Type");
1735         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1736         box = gtk_vbox_new(FALSE, 10);
1737         gtk_container_add(GTK_CONTAINER(frame), box);
1738
1739         hbox = gtk_hbox_new(TRUE, 4);
1740         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1741
1742         cw.combo = gtk_combo_box_new_text();
1743         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1744         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1745         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1746         gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1747
1748         gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1749
1750         frame = gtk_frame_new("Options");
1751         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1752         box = gtk_vbox_new(FALSE, 10);
1753         gtk_container_add(GTK_CONTAINER(frame), box);
1754
1755         hbox = gtk_hbox_new(TRUE, 4);
1756         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1757
1758         cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1759         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1760         gtk_widget_set_tooltip_text(cw.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.");
1761         gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1762
1763         /*
1764          * Connect edit signal, so we can show/not-show the auto start button
1765          */
1766         g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1767         g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1768
1769         gtk_widget_show_all(dialog);
1770
1771         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1772                 gtk_widget_destroy(dialog);
1773                 return 1;
1774         }
1775
1776         *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1777         *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1778
1779         typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1780         if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1781                 *type = Fio_client_ipv4;
1782         else if (!strncmp(typeentry, "IPv6", 4))
1783                 *type = Fio_client_ipv6;
1784         else
1785                 *type = Fio_client_socket;
1786         g_free(typeentry);
1787
1788         *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1789
1790         gtk_widget_destroy(dialog);
1791         return 0;
1792 }
1793
1794 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1795 {
1796         struct gfio_client *gc;
1797
1798         gc = malloc(sizeof(*gc));
1799         memset(gc, 0, sizeof(*gc));
1800         gc->ge = ge;
1801         gc->client = fio_get_client(client);
1802
1803         ge->client = gc;
1804
1805         client->client_data = gc;
1806 }
1807
1808 static GtkWidget *new_client_page(struct gui_entry *ge);
1809
1810 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1811 {
1812         struct gui_entry *ge;
1813
1814         ge = malloc(sizeof(*ge));
1815         memset(ge, 0, sizeof(*ge));
1816         INIT_FLIST_HEAD(&ge->list);
1817         flist_add_tail(&ge->list, &ui->list);
1818         ge->ui = ui;
1819         return ge;
1820 }
1821
1822 /*
1823  * FIXME: need more handling here
1824  */
1825 static void ge_destroy(GtkWidget *w, gpointer data)
1826 {
1827         struct gui_entry *ge = data;
1828         struct gfio_client *gc = ge->client;
1829
1830         if (gc->client)
1831                 fio_put_client(gc->client);
1832
1833         flist_del(&ge->list);
1834         free(ge);
1835 }
1836
1837 static struct gui_entry *get_new_ge_with_tab(const char *name)
1838 {
1839         struct gui_entry *ge;
1840
1841         ge = alloc_new_gui_entry(&main_ui);
1842
1843         ge->vbox = new_client_page(ge);
1844         g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1845
1846         ge->page_label = gtk_label_new(name);
1847         ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1848
1849         gtk_widget_show_all(main_ui.window);
1850         return ge;
1851 }
1852
1853 static void file_new(GtkWidget *w, gpointer data)
1854 {
1855         get_new_ge_with_tab("Untitled");
1856 }
1857
1858 /*
1859  * Return the 'ge' corresponding to the tab. If the active tab is the
1860  * main tab, open a new tab.
1861  */
1862 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1863 {
1864         struct flist_head *entry;
1865         struct gui_entry *ge;
1866
1867         if (!cur_page)
1868                 return get_new_ge_with_tab("Untitled");
1869
1870         flist_for_each(entry, &main_ui.list) {
1871                 ge = flist_entry(entry, struct gui_entry, list);
1872                 if (ge->page_num == cur_page)
1873                         return ge;
1874         }
1875
1876         return NULL;
1877 }
1878
1879 static void file_open(GtkWidget *w, gpointer data)
1880 {
1881         struct gui *ui = data;
1882         GtkWidget *dialog;
1883         GSList *filenames, *fn_glist;
1884         GtkFileFilter *filter;
1885         char *host;
1886         int port, type, server_start;
1887         struct gui_entry *ge;
1888         gint cur_page;
1889
1890         /*
1891          * Creates new tab if current tab is the main window, or the
1892          * current tab already has a client.
1893          */
1894         cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1895         ge = get_ge_from_page(cur_page);
1896         if (ge->client)
1897                 ge = get_new_ge_with_tab("Untitled");
1898
1899         gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1900
1901         dialog = gtk_file_chooser_dialog_new("Open File",
1902                 GTK_WINDOW(ui->window),
1903                 GTK_FILE_CHOOSER_ACTION_OPEN,
1904                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1905                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1906                 NULL);
1907         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1908
1909         filter = gtk_file_filter_new();
1910         gtk_file_filter_add_pattern(filter, "*.fio");
1911         gtk_file_filter_add_pattern(filter, "*.job");
1912         gtk_file_filter_add_pattern(filter, "*.ini");
1913         gtk_file_filter_add_mime_type(filter, "text/fio");
1914         gtk_file_filter_set_name(filter, "Fio job file");
1915         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1916
1917         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1918                 gtk_widget_destroy(dialog);
1919                 return;
1920         }
1921
1922         fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1923
1924         gtk_widget_destroy(dialog);
1925
1926         if (get_connection_details(&host, &port, &type, &server_start))
1927                 goto err;
1928
1929         filenames = fn_glist;
1930         while (filenames != NULL) {
1931                 struct fio_client *client;
1932
1933                 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1934                 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1935                 ge->nr_job_files++;
1936
1937                 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1938                 if (!client) {
1939                         GError *error;
1940
1941                         error = g_error_new(g_quark_from_string("fio"), 1,
1942                                         "Failed to add client %s", host);
1943                         report_error(error);
1944                         g_error_free(error);
1945                 }
1946                 gfio_client_added(ge, client);
1947                         
1948                 g_free(filenames->data);
1949                 filenames = g_slist_next(filenames);
1950         }
1951         free(host);
1952
1953         if (server_start)
1954                 gfio_start_server();
1955 err:
1956         g_slist_free(fn_glist);
1957 }
1958
1959 static void file_save(GtkWidget *w, gpointer data)
1960 {
1961         struct gui *ui = data;
1962         GtkWidget *dialog;
1963
1964         dialog = gtk_file_chooser_dialog_new("Save File",
1965                 GTK_WINDOW(ui->window),
1966                 GTK_FILE_CHOOSER_ACTION_SAVE,
1967                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1968                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1969                 NULL);
1970
1971         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1972         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1973
1974         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1975                 char *filename;
1976
1977                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1978                 // save_job_file(filename);
1979                 g_free(filename);
1980         }
1981         gtk_widget_destroy(dialog);
1982 }
1983
1984 static void view_log_destroy(GtkWidget *w, gpointer data)
1985 {
1986         struct gui *ui = (struct gui *) data;
1987
1988         gtk_widget_ref(ui->log_tree);
1989         gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1990         gtk_widget_destroy(w);
1991         ui->log_view = NULL;
1992 }
1993
1994 static void view_log(GtkWidget *w, gpointer data)
1995 {
1996         GtkWidget *win, *scroll, *vbox, *box;
1997         struct gui *ui = (struct gui *) data;
1998
1999         if (ui->log_view)
2000                 return;
2001
2002         ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2003         gtk_window_set_title(GTK_WINDOW(win), "Log");
2004         gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2005
2006         scroll = gtk_scrolled_window_new(NULL, NULL);
2007
2008         gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2009
2010         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2011
2012         box = gtk_hbox_new(TRUE, 0);
2013         gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2014         g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2015         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2016
2017         vbox = gtk_vbox_new(TRUE, 5);
2018         gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2019
2020         gtk_container_add(GTK_CONTAINER(win), vbox);
2021         gtk_widget_show_all(win);
2022 }
2023
2024 static void preferences(GtkWidget *w, gpointer data)
2025 {
2026         GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2027         int i;
2028
2029         dialog = gtk_dialog_new_with_buttons("Preferences",
2030                 GTK_WINDOW(main_ui.window),
2031                 GTK_DIALOG_DESTROY_WITH_PARENT,
2032                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2033                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2034                 NULL);
2035
2036         frame = gtk_frame_new("Debug logging");
2037         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2038
2039         vbox = gtk_vbox_new(FALSE, 6);
2040         gtk_container_add(GTK_CONTAINER(frame), vbox);
2041
2042         box = gtk_hbox_new(FALSE, 6);
2043         gtk_container_add(GTK_CONTAINER(vbox), box);
2044
2045         buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2046
2047         for (i = 0; i < FD_DEBUG_MAX; i++) {
2048                 if (i == 7) {
2049                         box = gtk_hbox_new(FALSE, 6);
2050                         gtk_container_add(GTK_CONTAINER(vbox), box);
2051                 }
2052
2053
2054                 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2055                 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2056                 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2057         }
2058
2059         frame = gtk_frame_new("Graph font");
2060         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2061         vbox = gtk_vbox_new(FALSE, 6);
2062         gtk_container_add(GTK_CONTAINER(frame), vbox);
2063
2064         font = gtk_font_button_new();
2065         gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2066
2067         gtk_widget_show_all(dialog);
2068
2069         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2070                 gtk_widget_destroy(dialog);
2071                 return;
2072         }
2073
2074         for (i = 0; i < FD_DEBUG_MAX; i++) {
2075                 int set;
2076
2077                 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2078                 if (set)
2079                         fio_debug |= (1UL << i);
2080         }
2081
2082         gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2083         gtk_widget_destroy(dialog);
2084 }
2085
2086 static void about_dialog(GtkWidget *w, gpointer data)
2087 {
2088         const char *authors[] = {
2089                 "Jens Axboe <axboe@kernel.dk>",
2090                 "Stephen Carmeron <stephenmcameron@gmail.com>",
2091                 NULL
2092         };
2093         const char *license[] = {
2094                 "Fio is free software; you can redistribute it and/or modify "
2095                 "it under the terms of the GNU General Public License as published by "
2096                 "the Free Software Foundation; either version 2 of the License, or "
2097                 "(at your option) any later version.\n",
2098                 "Fio is distributed in the hope that it will be useful, "
2099                 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2100                 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
2101                 "GNU General Public License for more details.\n",
2102                 "You should have received a copy of the GNU General Public License "
2103                 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2104                 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA\n"
2105         };
2106         char *license_trans;
2107
2108         license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2109                                      license[2], "\n", NULL);
2110
2111         gtk_show_about_dialog(NULL,
2112                 "program-name", "gfio",
2113                 "comments", "Gtk2 UI for fio",
2114                 "license", license_trans,
2115                 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2116                 "authors", authors,
2117                 "version", fio_version_string,
2118                 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2119                 "logo-icon-name", "fio",
2120                 /* Must be last: */
2121                 "wrap-license", TRUE,
2122                 NULL);
2123
2124         g_free(license_trans);
2125 }
2126
2127 static GtkActionEntry menu_items[] = {
2128         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2129         { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2130         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2131         { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2132         { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
2133         { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
2134         { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2135         { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2136         { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2137         { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
2138 };
2139 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2140
2141 static const gchar *ui_string = " \
2142         <ui> \
2143                 <menubar name=\"MainMenu\"> \
2144                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2145                                 <menuitem name=\"New\" action=\"NewFile\" /> \
2146                                 <separator name=\"Separator1\"/> \
2147                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2148                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2149                                 <separator name=\"Separator2\"/> \
2150                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2151                                 <separator name=\"Separator3\"/> \
2152                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
2153                         </menu> \
2154                         <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2155                                 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2156                         </menu>\
2157                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
2158                                 <menuitem name=\"About\" action=\"About\" /> \
2159                         </menu> \
2160                 </menubar> \
2161         </ui> \
2162 ";
2163
2164 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2165                                    struct gui *ui)
2166 {
2167         GtkActionGroup *action_group = gtk_action_group_new("Menu");
2168         GError *error = 0;
2169
2170         action_group = gtk_action_group_new("Menu");
2171         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2172
2173         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2174         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2175
2176         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2177         return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2178 }
2179
2180 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2181                    GtkWidget *vbox, GtkUIManager *ui_manager)
2182 {
2183         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2184 }
2185
2186 static GtkWidget *new_client_page(struct gui_entry *ge)
2187 {
2188         GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2189         GdkColor white;
2190
2191         main_vbox = gtk_vbox_new(FALSE, 3);
2192
2193         ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2194         ge->topvbox = gtk_vbox_new(FALSE, 3);
2195         gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2196         gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2197
2198         probe = gtk_frame_new("Job");
2199         gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2200         probe_frame = gtk_vbox_new(FALSE, 3);
2201         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2202
2203         probe_box = gtk_hbox_new(FALSE, 3);
2204         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2205         ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2206         ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2207         ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2208         ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2209
2210         probe_box = gtk_hbox_new(FALSE, 3);
2211         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2212
2213         ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2214         ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2215         ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2216         ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2217         ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2218         ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2219
2220         probe_box = gtk_hbox_new(FALSE, 3);
2221         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2222         ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2223         ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2224         ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2225         ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2226
2227         /*
2228          * Only add this if we have a commit rate
2229          */
2230 #if 0
2231         probe_box = gtk_hbox_new(FALSE, 3);
2232         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2233
2234         ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2235         ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2236
2237         ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2238         ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2239 #endif
2240
2241         /*
2242          * Set up a drawing area and IOPS and bandwidth graphs
2243          */
2244         gdk_color_parse("white", &white);
2245         ge->graphs.drawing_area = gtk_drawing_area_new();
2246         ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2247         ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2248         gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2249                 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2250         gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2251         g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2252                                 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2253         g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2254                                 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2255         ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2256         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2257                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2258         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2259                                         ge->graphs.drawing_area);
2260         gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2261                         TRUE, TRUE, 0);
2262
2263         setup_graphs(&ge->graphs);
2264
2265         /*
2266          * Set up alignments for widgets at the bottom of ui, 
2267          * align bottom left, expand horizontally but not vertically
2268          */
2269         ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2270         ge->buttonbox = gtk_hbox_new(FALSE, 0);
2271         gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2272         gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2273                                         FALSE, FALSE, 0);
2274
2275         add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2276
2277         /*
2278          * Set up thread status progress bar
2279          */
2280         ge->thread_status_pb = gtk_progress_bar_new();
2281         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2282         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2283         gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2284
2285
2286         return main_vbox;
2287 }
2288
2289 static GtkWidget *new_main_page(struct gui *ui)
2290 {
2291         GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2292         GdkColor white;
2293
2294         main_vbox = gtk_vbox_new(FALSE, 3);
2295
2296         /*
2297          * Set up alignments for widgets at the top of ui,
2298          * align top left, expand horizontally but not vertically
2299          */
2300         ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2301         ui->topvbox = gtk_vbox_new(FALSE, 0);
2302         gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2303         gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2304
2305         probe = gtk_frame_new("Run statistics");
2306         gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2307         probe_frame = gtk_vbox_new(FALSE, 3);
2308         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2309
2310         probe_box = gtk_hbox_new(FALSE, 3);
2311         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2312         ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2313         ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2314         ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2315         ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2316         ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2317
2318         /*
2319          * Only add this if we have a commit rate
2320          */
2321 #if 0
2322         probe_box = gtk_hbox_new(FALSE, 3);
2323         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2324
2325         ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2326         ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2327
2328         ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2329         ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2330 #endif
2331
2332         /*
2333          * Set up a drawing area and IOPS and bandwidth graphs
2334          */
2335         gdk_color_parse("white", &white);
2336         ui->graphs.drawing_area = gtk_drawing_area_new();
2337         ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2338         ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2339         gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2340                 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2341         gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2342         g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2343                         G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2344         g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2345                         G_CALLBACK(on_config_drawing_area), &ui->graphs);
2346         ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2347         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2348                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2349         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2350                                         ui->graphs.drawing_area);
2351         gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2352                         TRUE, TRUE, 0);
2353
2354         setup_graphs(&ui->graphs);
2355
2356         /*
2357          * Set up alignments for widgets at the bottom of ui, 
2358          * align bottom left, expand horizontally but not vertically
2359          */
2360         ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2361         ui->buttonbox = gtk_hbox_new(FALSE, 0);
2362         gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2363         gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2364                                         FALSE, FALSE, 0);
2365
2366         /*
2367          * Set up thread status progress bar
2368          */
2369         ui->thread_status_pb = gtk_progress_bar_new();
2370         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2371         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2372         gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2373
2374         return main_vbox;
2375 }
2376
2377 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2378                                      guint page, gpointer data)
2379
2380 {
2381         return TRUE;
2382 }
2383
2384 static void init_ui(int *argc, char **argv[], struct gui *ui)
2385 {
2386         GtkSettings *settings;
2387         GtkUIManager *uimanager;
2388         GtkWidget *menu, *vbox;
2389
2390         /* Magical g*thread incantation, you just need this thread stuff.
2391          * Without it, the update that happens in gfio_update_thread_status
2392          * doesn't really happen in a timely fashion, you need expose events
2393          */
2394         if (!g_thread_supported())
2395                 g_thread_init(NULL);
2396         gdk_threads_init();
2397
2398         gtk_init(argc, argv);
2399         settings = gtk_settings_get_default();
2400         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2401         g_type_init();
2402         
2403         ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2404         gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2405         gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2406
2407         g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2408         g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2409
2410         ui->vbox = gtk_vbox_new(FALSE, 0);
2411         gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2412
2413         uimanager = gtk_ui_manager_new();
2414         menu = get_menubar_menu(ui->window, uimanager, ui);
2415         gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2416
2417         ui->notebook = gtk_notebook_new();
2418         g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2419         gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2420         gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2421         gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2422
2423         vbox = new_main_page(ui);
2424
2425         gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2426
2427         gfio_ui_setup_log(ui);
2428
2429         gtk_widget_show_all(ui->window);
2430 }
2431
2432 int main(int argc, char *argv[], char *envp[])
2433 {
2434         if (initialize_fio(envp))
2435                 return 1;
2436         if (fio_init_options())
2437                 return 1;
2438
2439         memset(&main_ui, 0, sizeof(main_ui));
2440         INIT_FLIST_HEAD(&main_ui.list);
2441
2442         init_ui(&argc, &argv, &main_ui);
2443
2444         gdk_threads_enter();
2445         gtk_main();
2446         gdk_threads_leave();
2447         return 0;
2448 }