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