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