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