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