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