gfio: add font selection and resize original window
[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");
132         graph_y_title(ui->iops_graph, "IOPS");
133         graph_add_label(ui->iops_graph, "Read IOPS");
134         graph_add_label(ui->iops_graph, "Write IOPS");
135         graph_set_color(ui->iops_graph, "Read IOPS", 0.7, 0.0, 0.0);
136         graph_set_color(ui->iops_graph, "Write IOPS", 0.0, 0.0, 0.7);
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");
147         graph_y_title(ui->bandwidth_graph, "Bandwidth");
148         graph_add_label(ui->bandwidth_graph, "Read Bandwidth");
149         graph_add_label(ui->bandwidth_graph, "Write Bandwidth");
150         graph_set_color(ui->bandwidth_graph, "Read Bandwidth", 0.7, 0.0, 0.0);
151         graph_set_color(ui->bandwidth_graph, "Write Bandwidth", 0.0, 0.0, 0.7);
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                 printf("no results!\n");
906                 goto out;
907         }
908
909         if (!gc->disk_util_frame) {
910                 gc->disk_util_frame = gtk_frame_new("Disk utilization");
911                 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
912         }
913
914         vbox = gtk_vbox_new(FALSE, 3);
915         gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
916
917         frame = gtk_frame_new((char *) p->dus.name);
918         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
919
920         box = gtk_vbox_new(FALSE, 3);
921         gtk_container_add(GTK_CONTAINER(frame), box);
922
923         frame = gtk_frame_new("Read");
924         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
925         vbox = gtk_hbox_new(TRUE, 3);
926         gtk_container_add(GTK_CONTAINER(frame), vbox);
927         entry = new_info_entry_in_frame(vbox, "IOs");
928         entry_set_int_value(entry, p->dus.ios[0]);
929         entry = new_info_entry_in_frame(vbox, "Merges");
930         entry_set_int_value(entry, p->dus.merges[0]);
931         entry = new_info_entry_in_frame(vbox, "Sectors");
932         entry_set_int_value(entry, p->dus.sectors[0]);
933         entry = new_info_entry_in_frame(vbox, "Ticks");
934         entry_set_int_value(entry, p->dus.ticks[0]);
935
936         frame = gtk_frame_new("Write");
937         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
938         vbox = gtk_hbox_new(TRUE, 3);
939         gtk_container_add(GTK_CONTAINER(frame), vbox);
940         entry = new_info_entry_in_frame(vbox, "IOs");
941         entry_set_int_value(entry, p->dus.ios[1]);
942         entry = new_info_entry_in_frame(vbox, "Merges");
943         entry_set_int_value(entry, p->dus.merges[1]);
944         entry = new_info_entry_in_frame(vbox, "Sectors");
945         entry_set_int_value(entry, p->dus.sectors[1]);
946         entry = new_info_entry_in_frame(vbox, "Ticks");
947         entry_set_int_value(entry, p->dus.ticks[1]);
948
949         frame = gtk_frame_new("Shared");
950         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
951         vbox = gtk_hbox_new(TRUE, 3);
952         gtk_container_add(GTK_CONTAINER(frame), vbox);
953         entry = new_info_entry_in_frame(vbox, "IO ticks");
954         entry_set_int_value(entry, p->dus.io_ticks);
955         entry = new_info_entry_in_frame(vbox, "Time in queue");
956         entry_set_int_value(entry, p->dus.time_in_queue);
957
958         gtk_widget_show_all(gc->results_widget);
959 out:
960         gdk_threads_leave();
961 }
962
963 extern int sum_stat_clients;
964 extern struct thread_stat client_ts;
965 extern struct group_run_stats client_gs;
966
967 static int sum_stat_nr;
968
969 static void gfio_thread_status_op(struct fio_client *client,
970                                   struct fio_net_cmd *cmd)
971 {
972         struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
973
974         gfio_display_ts(client, &p->ts, &p->rs);
975
976         if (sum_stat_clients == 1)
977                 return;
978
979         sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
980         sum_group_stats(&client_gs, &p->rs);
981
982         client_ts.members++;
983         client_ts.groupid = p->ts.groupid;
984
985         if (++sum_stat_nr == sum_stat_clients) {
986                 strcpy(client_ts.name, "All clients");
987                 gfio_display_ts(client, &client_ts, &client_gs);
988         }
989 }
990
991 static void gfio_group_stats_op(struct fio_client *client,
992                                 struct fio_net_cmd *cmd)
993 {
994         gdk_threads_enter();
995         printf("gfio_group_stats_op called\n");
996         fio_client_ops.group_stats(client, cmd);
997         gdk_threads_leave();
998 }
999
1000 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1001 {
1002         struct gui *ui = (struct gui *) p;
1003         cairo_t *cr;
1004
1005         cr = gdk_cairo_create(w->window);
1006
1007         cairo_set_source_rgb(cr, 0, 0, 0);
1008
1009         cairo_save(cr);
1010         cairo_translate(cr, 0, 0);
1011         line_graph_draw(ui->bandwidth_graph, cr);
1012         cairo_stroke(cr);
1013         cairo_restore(cr);
1014
1015         cairo_save(cr);
1016         cairo_translate(cr, DRAWING_AREA_XDIM / 2.0, 0);
1017                                 // DRAWING_AREA_YDIM * 0.05);
1018         line_graph_draw(ui->iops_graph, cr);
1019         cairo_stroke(cr);
1020         cairo_restore(cr);
1021         cairo_destroy(cr);
1022
1023         return FALSE;
1024 }
1025
1026 static void gfio_update_eta(struct jobs_eta *je)
1027 {
1028         static int eta_good;
1029         char eta_str[128];
1030         char output[256];
1031         char tmp[32];
1032         double perc = 0.0;
1033         int i2p = 0;
1034
1035         gdk_threads_enter();
1036
1037         eta_str[0] = '\0';
1038         output[0] = '\0';
1039
1040         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1041                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1042                 eta_to_str(eta_str, je->eta_sec);
1043         }
1044
1045         sprintf(tmp, "%u", je->nr_running);
1046         gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
1047         sprintf(tmp, "%u", je->files_open);
1048         gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
1049
1050 #if 0
1051         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1052         if (je->m_rate || je->t_rate) {
1053                 char *tr, *mr;
1054
1055                 mr = num2str(je->m_rate, 4, 0, i2p);
1056                 tr = num2str(je->t_rate, 4, 0, i2p);
1057                 gtk_entry_set_text(GTK_ENTRY(ui.eta);
1058                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1059                 free(tr);
1060                 free(mr);
1061         } else if (je->m_iops || je->t_iops)
1062                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1063
1064         gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
1065         gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
1066         gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
1067         gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
1068 #endif
1069
1070         if (je->eta_sec != INT_MAX && je->nr_running) {
1071                 char *iops_str[2];
1072                 char *rate_str[2];
1073
1074                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1075                         strcpy(output, "-.-% done");
1076                 else {
1077                         eta_good = 1;
1078                         perc *= 100.0;
1079                         sprintf(output, "%3.1f%% done", perc);
1080                 }
1081
1082                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1083                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1084
1085                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1086                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1087
1088                 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
1089                 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
1090                 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
1091                 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
1092
1093                 graph_add_xy_data(ui.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1094                 graph_add_xy_data(ui.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1095                 graph_add_xy_data(ui.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1096                 graph_add_xy_data(ui.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1097
1098                 free(rate_str[0]);
1099                 free(rate_str[1]);
1100                 free(iops_str[0]);
1101                 free(iops_str[1]);
1102         }
1103
1104         if (eta_str[0]) {
1105                 char *dst = output + strlen(output);
1106
1107                 sprintf(dst, " - %s", eta_str);
1108         }
1109                 
1110         gfio_update_thread_status(output, perc);
1111         gdk_threads_leave();
1112 }
1113
1114 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1115 {
1116         struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1117         struct gfio_client *gc = client->client_data;
1118         struct gui *ui = gc->ui;
1119         const char *os, *arch;
1120         char buf[64];
1121
1122         os = fio_get_os_string(probe->os);
1123         if (!os)
1124                 os = "unknown";
1125
1126         arch = fio_get_arch_string(probe->arch);
1127         if (!arch)
1128                 os = "unknown";
1129
1130         if (!client->name)
1131                 client->name = strdup((char *) probe->hostname);
1132
1133         gdk_threads_enter();
1134
1135         gtk_label_set_text(GTK_LABEL(ui->probe.hostname), (char *) probe->hostname);
1136         gtk_label_set_text(GTK_LABEL(ui->probe.os), os);
1137         gtk_label_set_text(GTK_LABEL(ui->probe.arch), arch);
1138         sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1139         gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), buf);
1140
1141         gfio_set_connected(ui, 1);
1142
1143         gdk_threads_leave();
1144 }
1145
1146 static void gfio_update_thread_status(char *status_message, double perc)
1147 {
1148         static char message[100];
1149         const char *m = message;
1150
1151         strncpy(message, status_message, sizeof(message) - 1);
1152         gtk_progress_bar_set_text(
1153                 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
1154         gtk_progress_bar_set_fraction(
1155                 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
1156         gtk_widget_queue_draw(ui.window);
1157 }
1158
1159 static void gfio_quit_op(struct fio_client *client)
1160 {
1161         struct gfio_client *gc = client->client_data;
1162
1163         gdk_threads_enter();
1164         gfio_set_connected(gc->ui, 0);
1165         gdk_threads_leave();
1166 }
1167
1168 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1169 {
1170         struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1171         struct gfio_client *gc = client->client_data;
1172         struct gui *ui = gc->ui;
1173         char tmp[8];
1174         int i;
1175
1176         p->iodepth              = le32_to_cpu(p->iodepth);
1177         p->rw                   = le32_to_cpu(p->rw);
1178
1179         for (i = 0; i < 2; i++) {
1180                 p->min_bs[i]    = le32_to_cpu(p->min_bs[i]);
1181                 p->max_bs[i]    = le32_to_cpu(p->max_bs[i]);
1182         }
1183
1184         p->numjobs              = le32_to_cpu(p->numjobs);
1185         p->group_reporting      = le32_to_cpu(p->group_reporting);
1186
1187         gdk_threads_enter();
1188
1189         gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
1190         gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
1191         gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
1192
1193         sprintf(tmp, "%u", p->iodepth);
1194         gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
1195
1196         gdk_threads_leave();
1197 }
1198
1199 static void gfio_client_timed_out(struct fio_client *client)
1200 {
1201         struct gfio_client *gc = client->client_data;
1202         GtkWidget *dialog, *label, *content;
1203         char buf[256];
1204
1205         gdk_threads_enter();
1206
1207         gfio_set_connected(gc->ui, 0);
1208         clear_ui_info(gc->ui);
1209
1210         sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1211
1212         dialog = gtk_dialog_new_with_buttons("Timed out!",
1213                         GTK_WINDOW(gc->ui->window),
1214                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1215                         GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1216
1217         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1218         label = gtk_label_new((const gchar *) buf);
1219         gtk_container_add(GTK_CONTAINER(content), label);
1220         gtk_widget_show_all(dialog);
1221         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1222
1223         gtk_dialog_run(GTK_DIALOG(dialog));
1224         gtk_widget_destroy(dialog);
1225
1226         gdk_threads_leave();
1227 }
1228
1229 struct client_ops gfio_client_ops = {
1230         .text_op                = gfio_text_op,
1231         .disk_util              = gfio_disk_util_op,
1232         .thread_status          = gfio_thread_status_op,
1233         .group_stats            = gfio_group_stats_op,
1234         .eta                    = gfio_update_eta,
1235         .probe                  = gfio_probe_op,
1236         .quit                   = gfio_quit_op,
1237         .add_job                = gfio_add_job_op,
1238         .timed_out              = gfio_client_timed_out,
1239         .stay_connected         = 1,
1240 };
1241
1242 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1243                 __attribute__((unused)) gpointer data)
1244 {
1245         gtk_main_quit();
1246 }
1247
1248 static void *job_thread(void *arg)
1249 {
1250         fio_handle_clients(&gfio_client_ops);
1251         return NULL;
1252 }
1253
1254 static int send_job_files(struct gui *ui)
1255 {
1256         int i, ret = 0;
1257
1258         for (i = 0; i < ui->nr_job_files; i++) {
1259                 ret = fio_clients_send_ini(ui->job_files[i]);
1260                 if (ret)
1261                         break;
1262
1263                 free(ui->job_files[i]);
1264                 ui->job_files[i] = NULL;
1265         }
1266         while (i < ui->nr_job_files) {
1267                 free(ui->job_files[i]);
1268                 ui->job_files[i] = NULL;
1269                 i++;
1270         }
1271
1272         return ret;
1273 }
1274
1275 static void start_job_thread(struct gui *ui)
1276 {
1277         if (send_job_files(ui)) {
1278                 printf("Yeah, I didn't really like those options too much.\n");
1279                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1280                 return;
1281         }
1282 }
1283
1284 static void *server_thread(void *arg)
1285 {
1286         is_backend = 1;
1287         gfio_server_running = 1;
1288         fio_start_server(NULL);
1289         gfio_server_running = 0;
1290         return NULL;
1291 }
1292
1293 static void gfio_start_server(struct gui *ui)
1294 {
1295         if (!gfio_server_running) {
1296                 gfio_server_running = 1;
1297                 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1298                 pthread_detach(ui->server_t);
1299         }
1300 }
1301
1302 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1303                 gpointer data)
1304 {
1305         struct gui *ui = data;
1306
1307         gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1308         start_job_thread(ui);
1309 }
1310
1311 static void file_open(GtkWidget *w, gpointer data);
1312
1313 static void connect_clicked(GtkWidget *widget, gpointer data)
1314 {
1315         struct gui *ui = data;
1316
1317         if (!ui->connected) {
1318                 if (!ui->nr_job_files)
1319                         file_open(widget, data);
1320                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1321                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1322                 if (!fio_clients_connect()) {
1323                         pthread_create(&ui->t, NULL, job_thread, NULL);
1324                         gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1325                 }
1326         } else {
1327                 fio_clients_terminate();
1328                 gfio_set_connected(ui, 0);
1329                 clear_ui_info(ui);
1330         }
1331 }
1332
1333 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1334                         struct button_spec *buttonspec)
1335 {
1336         ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1337         g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1338         gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1339         gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1340         gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1341 }
1342
1343 static void add_buttons(struct gui *ui,
1344                                 struct button_spec *buttonlist,
1345                                 int nbuttons)
1346 {
1347         int i;
1348
1349         for (i = 0; i < nbuttons; i++)
1350                 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1351 }
1352
1353 static void on_info_bar_response(GtkWidget *widget, gint response,
1354                                  gpointer data)
1355 {
1356         if (response == GTK_RESPONSE_OK) {
1357                 gtk_widget_destroy(widget);
1358                 ui.error_info_bar = NULL;
1359         }
1360 }
1361
1362 void report_error(GError *error)
1363 {
1364         if (ui.error_info_bar == NULL) {
1365                 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1366                                                                GTK_RESPONSE_OK,
1367                                                                NULL);
1368                 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1369                 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1370                                               GTK_MESSAGE_ERROR);
1371                 
1372                 ui.error_label = gtk_label_new(error->message);
1373                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1374                 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1375                 
1376                 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1377                 gtk_widget_show_all(ui.vbox);
1378         } else {
1379                 char buffer[256];
1380                 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1381                 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1382         }
1383 }
1384
1385 struct connection_widgets
1386 {
1387         GtkWidget *hentry;
1388         GtkWidget *combo;
1389         GtkWidget *button;
1390 };
1391
1392 static void hostname_cb(GtkEntry *entry, gpointer data)
1393 {
1394         struct connection_widgets *cw = data;
1395         int uses_net = 0, is_localhost = 0;
1396         const gchar *text;
1397         gchar *ctext;
1398
1399         /*
1400          * Check whether to display the 'auto start backend' box
1401          * or not. Show it if we are a localhost and using network,
1402          * or using a socket.
1403          */
1404         ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1405         if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1406                 uses_net = 1;
1407         g_free(ctext);
1408
1409         if (uses_net) {
1410                 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1411                 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1412                     !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1413                     !strcmp(text, "ip6-loopback"))
1414                         is_localhost = 1;
1415         }
1416
1417         if (!uses_net || is_localhost) {
1418                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1419                 gtk_widget_set_sensitive(cw->button, 1);
1420         } else {
1421                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1422                 gtk_widget_set_sensitive(cw->button, 0);
1423         }
1424 }
1425
1426 static int get_connection_details(char **host, int *port, int *type,
1427                                   int *server_start)
1428 {
1429         GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1430         struct connection_widgets cw;
1431         char *typeentry;
1432
1433         dialog = gtk_dialog_new_with_buttons("Connection details",
1434                         GTK_WINDOW(ui.window),
1435                         GTK_DIALOG_DESTROY_WITH_PARENT,
1436                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1437                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1438
1439         frame = gtk_frame_new("Hostname / socket name");
1440         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1441         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1442
1443         box = gtk_vbox_new(FALSE, 6);
1444         gtk_container_add(GTK_CONTAINER(frame), box);
1445
1446         hbox = gtk_hbox_new(TRUE, 10);
1447         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1448         cw.hentry = gtk_entry_new();
1449         gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1450         gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1451
1452         frame = gtk_frame_new("Port");
1453         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1454         box = gtk_vbox_new(FALSE, 10);
1455         gtk_container_add(GTK_CONTAINER(frame), box);
1456
1457         hbox = gtk_hbox_new(TRUE, 4);
1458         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1459         pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1460
1461         frame = gtk_frame_new("Type");
1462         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1463         box = gtk_vbox_new(FALSE, 10);
1464         gtk_container_add(GTK_CONTAINER(frame), box);
1465
1466         hbox = gtk_hbox_new(TRUE, 4);
1467         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1468
1469         cw.combo = gtk_combo_box_new_text();
1470         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1471         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1472         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1473         gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1474
1475         gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1476
1477         frame = gtk_frame_new("Options");
1478         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1479         box = gtk_vbox_new(FALSE, 10);
1480         gtk_container_add(GTK_CONTAINER(frame), box);
1481
1482         hbox = gtk_hbox_new(TRUE, 4);
1483         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1484
1485         cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1486         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1487         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.");
1488         gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1489
1490         /*
1491          * Connect edit signal, so we can show/not-show the auto start button
1492          */
1493         g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1494         g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1495
1496         gtk_widget_show_all(dialog);
1497
1498         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1499                 gtk_widget_destroy(dialog);
1500                 return 1;
1501         }
1502
1503         *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1504         *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1505
1506         typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1507         if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1508                 *type = Fio_client_ipv4;
1509         else if (!strncmp(typeentry, "IPv6", 4))
1510                 *type = Fio_client_ipv6;
1511         else
1512                 *type = Fio_client_socket;
1513         g_free(typeentry);
1514
1515         *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1516
1517         gtk_widget_destroy(dialog);
1518         return 0;
1519 }
1520
1521 static void gfio_client_added(struct gui *ui, struct fio_client *client)
1522 {
1523         struct gfio_client *gc;
1524
1525         gc = malloc(sizeof(*gc));
1526         memset(gc, 0, sizeof(*gc));
1527         gc->ui = ui;
1528
1529         client->client_data = gc;
1530 }
1531
1532 static void file_open(GtkWidget *w, gpointer data)
1533 {
1534         GtkWidget *dialog;
1535         struct gui *ui = data;
1536         GSList *filenames, *fn_glist;
1537         GtkFileFilter *filter;
1538         char *host;
1539         int port, type, server_start;
1540
1541         dialog = gtk_file_chooser_dialog_new("Open File",
1542                 GTK_WINDOW(ui->window),
1543                 GTK_FILE_CHOOSER_ACTION_OPEN,
1544                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1545                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1546                 NULL);
1547         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1548
1549         filter = gtk_file_filter_new();
1550         gtk_file_filter_add_pattern(filter, "*.fio");
1551         gtk_file_filter_add_pattern(filter, "*.job");
1552         gtk_file_filter_add_pattern(filter, "*.ini");
1553         gtk_file_filter_add_mime_type(filter, "text/fio");
1554         gtk_file_filter_set_name(filter, "Fio job file");
1555         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1556
1557         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1558                 gtk_widget_destroy(dialog);
1559                 return;
1560         }
1561
1562         fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1563
1564         gtk_widget_destroy(dialog);
1565
1566         if (get_connection_details(&host, &port, &type, &server_start))
1567                 goto err;
1568
1569         filenames = fn_glist;
1570         while (filenames != NULL) {
1571                 struct fio_client *client;
1572
1573                 ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1574                 ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1575                 ui->nr_job_files++;
1576
1577                 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1578                 if (!client) {
1579                         GError *error;
1580
1581                         error = g_error_new(g_quark_from_string("fio"), 1,
1582                                         "Failed to add client %s", host);
1583                         report_error(error);
1584                         g_error_free(error);
1585                 }
1586                 gfio_client_added(ui, client);
1587                         
1588                 g_free(filenames->data);
1589                 filenames = g_slist_next(filenames);
1590         }
1591         free(host);
1592
1593         if (server_start)
1594                 gfio_start_server(ui);
1595 err:
1596         g_slist_free(fn_glist);
1597 }
1598
1599 static void file_save(GtkWidget *w, gpointer data)
1600 {
1601         struct gui *ui = data;
1602         GtkWidget *dialog;
1603
1604         dialog = gtk_file_chooser_dialog_new("Save File",
1605                 GTK_WINDOW(ui->window),
1606                 GTK_FILE_CHOOSER_ACTION_SAVE,
1607                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1608                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1609                 NULL);
1610
1611         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1612         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1613
1614         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1615                 char *filename;
1616
1617                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1618                 // save_job_file(filename);
1619                 g_free(filename);
1620         }
1621         gtk_widget_destroy(dialog);
1622 }
1623
1624 static void view_log_destroy(GtkWidget *w, gpointer data)
1625 {
1626         struct gui *ui = (struct gui *) data;
1627
1628         gtk_widget_ref(ui->log_tree);
1629         gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1630         gtk_widget_destroy(w);
1631         ui->log_view = NULL;
1632 }
1633
1634 static void view_log(GtkWidget *w, gpointer data)
1635 {
1636         GtkWidget *win, *scroll, *vbox, *box;
1637         struct gui *ui = (struct gui *) data;
1638
1639         if (ui->log_view)
1640                 return;
1641
1642         ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1643         gtk_window_set_title(GTK_WINDOW(win), "Log");
1644         gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1645
1646         scroll = gtk_scrolled_window_new(NULL, NULL);
1647
1648         gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1649
1650         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1651
1652         box = gtk_hbox_new(TRUE, 0);
1653         gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1654         g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1655         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1656
1657         vbox = gtk_vbox_new(TRUE, 5);
1658         gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1659
1660         gtk_container_add(GTK_CONTAINER(win), vbox);
1661         gtk_widget_show_all(win);
1662 }
1663
1664 static void preferences(GtkWidget *w, gpointer data)
1665 {
1666         GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1667         int i;
1668
1669         dialog = gtk_dialog_new_with_buttons("Preferences",
1670                 GTK_WINDOW(ui.window),
1671                 GTK_DIALOG_DESTROY_WITH_PARENT,
1672                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1673                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1674                 NULL);
1675
1676         frame = gtk_frame_new("Debug logging");
1677         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1678
1679         vbox = gtk_vbox_new(FALSE, 6);
1680         gtk_container_add(GTK_CONTAINER(frame), vbox);
1681
1682         box = gtk_hbox_new(FALSE, 6);
1683         gtk_container_add(GTK_CONTAINER(vbox), box);
1684
1685         buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1686
1687         for (i = 0; i < FD_DEBUG_MAX; i++) {
1688                 if (i == 7) {
1689                         box = gtk_hbox_new(FALSE, 6);
1690                         gtk_container_add(GTK_CONTAINER(vbox), box);
1691                 }
1692
1693
1694                 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1695                 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1696                 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1697         }
1698
1699         frame = gtk_frame_new("Graph font");
1700         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1701         vbox = gtk_vbox_new(FALSE, 6);
1702         gtk_container_add(GTK_CONTAINER(frame), vbox);
1703
1704         font = gtk_font_button_new();
1705         gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
1706
1707         gtk_widget_show_all(dialog);
1708
1709         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1710                 gtk_widget_destroy(dialog);
1711                 return;
1712         }
1713
1714         for (i = 0; i < FD_DEBUG_MAX; i++) {
1715                 int set;
1716
1717                 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1718                 if (set)
1719                         fio_debug |= (1UL << i);
1720         }
1721
1722         gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
1723         printf("got font %s\n", gfio_graph_font);
1724
1725         gtk_widget_destroy(dialog);
1726 }
1727
1728 static void about_dialog(GtkWidget *w, gpointer data)
1729 {
1730         gtk_show_about_dialog(NULL,
1731                 "program-name", "gfio",
1732                 "comments", "Gtk2 UI for fio",
1733                 "license", "GPLv2",
1734                 "version", fio_version_string,
1735                 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1736                 "logo-icon-name", "fio",
1737                 /* Must be last: */
1738                 NULL, NULL,
1739                 NULL);
1740 }
1741
1742 static GtkActionEntry menu_items[] = {
1743         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1744         { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
1745         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1746         { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
1747         { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
1748         { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1749         { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
1750         { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1751         { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
1752 };
1753 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1754
1755 static const gchar *ui_string = " \
1756         <ui> \
1757                 <menubar name=\"MainMenu\"> \
1758                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1759                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1760                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1761                                 <separator name=\"Separator\"/> \
1762                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1763                                 <separator name=\"Separator2\"/> \
1764                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
1765                         </menu> \
1766                         <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1767                                 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1768                         </menu>\
1769                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
1770                                 <menuitem name=\"About\" action=\"About\" /> \
1771                         </menu> \
1772                 </menubar> \
1773         </ui> \
1774 ";
1775
1776 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1777                                    struct gui *ui)
1778 {
1779         GtkActionGroup *action_group = gtk_action_group_new("Menu");
1780         GError *error = 0;
1781
1782         action_group = gtk_action_group_new("Menu");
1783         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
1784
1785         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1786         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1787
1788         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1789         return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1790 }
1791
1792 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1793                    GtkWidget *vbox, GtkUIManager *ui_manager)
1794 {
1795         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1796 }
1797
1798 static void init_ui(int *argc, char **argv[], struct gui *ui)
1799 {
1800         GtkSettings *settings;
1801         GtkUIManager *uimanager;
1802         GtkWidget *menu, *probe, *probe_frame, *probe_box;
1803
1804         memset(ui, 0, sizeof(*ui));
1805
1806         /* Magical g*thread incantation, you just need this thread stuff.
1807          * Without it, the update that happens in gfio_update_thread_status
1808          * doesn't really happen in a timely fashion, you need expose events
1809          */
1810         if (!g_thread_supported())
1811                 g_thread_init(NULL);
1812         gdk_threads_init();
1813
1814         gtk_init(argc, argv);
1815         settings = gtk_settings_get_default();
1816         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1817         g_type_init();
1818         
1819         ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1820         gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1821         gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 700);
1822
1823         g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1824         g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1825
1826         ui->vbox = gtk_vbox_new(FALSE, 0);
1827         gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1828
1829         uimanager = gtk_ui_manager_new();
1830         menu = get_menubar_menu(ui->window, uimanager, ui);
1831         gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1832
1833         /*
1834          * Set up alignments for widgets at the top of ui, 
1835          * align top left, expand horizontally but not vertically
1836          */
1837         ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1838         ui->topvbox = gtk_vbox_new(FALSE, 3);
1839         gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1840         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1841
1842         probe = gtk_frame_new("Job");
1843         gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1844         probe_frame = gtk_vbox_new(FALSE, 3);
1845         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1846
1847         probe_box = gtk_hbox_new(FALSE, 3);
1848         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1849         ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1850         ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1851         ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1852         ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1853
1854         probe_box = gtk_hbox_new(FALSE, 3);
1855         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1856
1857         ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1858         ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1859         ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1860         ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1861         ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1862         ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1863
1864         probe_box = gtk_hbox_new(FALSE, 3);
1865         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1866         ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1867         ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1868         ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1869         ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1870
1871         /*
1872          * Only add this if we have a commit rate
1873          */
1874 #if 0
1875         probe_box = gtk_hbox_new(FALSE, 3);
1876         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1877
1878         ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1879         ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1880
1881         ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1882         ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1883 #endif
1884
1885         /*
1886          * Set up a drawing area and IOPS and bandwidth graphs
1887          */
1888         ui->drawing_area = gtk_drawing_area_new();
1889         gtk_widget_set_size_request(GTK_WIDGET(ui->drawing_area), 
1890                         DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
1891         g_signal_connect(G_OBJECT(ui->drawing_area), "expose_event",
1892                                 G_CALLBACK (on_expose_drawing_area), ui);
1893         ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1894         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1895                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1896         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1897                                         ui->drawing_area);
1898         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1899                         TRUE, TRUE, 0);
1900
1901         setup_iops_graph(ui);
1902         setup_bandwidth_graph(ui);
1903
1904         /*
1905          * Set up alignments for widgets at the bottom of ui, 
1906          * align bottom left, expand horizontally but not vertically
1907          */
1908         ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1909         ui->buttonbox = gtk_hbox_new(FALSE, 0);
1910         gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1911         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1912                                         FALSE, FALSE, 0);
1913
1914         add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1915
1916         /*
1917          * Set up thread status progress bar
1918          */
1919         ui->thread_status_pb = gtk_progress_bar_new();
1920         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1921         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1922         gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1923
1924         gfio_ui_setup_log(ui);
1925
1926         gtk_widget_show_all(ui->window);
1927 }
1928
1929 int main(int argc, char *argv[], char *envp[])
1930 {
1931         if (initialize_fio(envp))
1932                 return 1;
1933         if (fio_init_options())
1934                 return 1;
1935
1936         init_ui(&argc, &argv, &ui);
1937
1938         gdk_threads_enter();
1939         gtk_main();
1940         gdk_threads_leave();
1941         return 0;
1942 }