gfio: correct depths view
[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  *
6  * The license below covers all files distributed with fio unless otherwise
7  * noted in the file itself.
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License version 2 as
11  *  published by the Free Software Foundation.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include <locale.h>
24 #include <malloc.h>
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28
29 #include "fio.h"
30
31 static void gfio_update_thread_status(char *status_message, double perc);
32
33 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
34
35 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
36
37 static void connect_clicked(GtkWidget *widget, gpointer data);
38 static void start_job_clicked(GtkWidget *widget, gpointer data);
39
40 static struct button_spec {
41         const char *buttontext;
42         clickfunction f;
43         const char *tooltiptext;
44         const int start_insensitive;
45 } buttonspeclist[] = {
46 #define CONNECT_BUTTON 0
47 #define START_JOB_BUTTON 1
48         { "Connect", connect_clicked, "Connect to host", 0 },
49         { "Start Job",
50                 start_job_clicked,
51                 "Send current fio job to fio server to be executed", 1 },
52 };
53
54 struct probe_widget {
55         GtkWidget *hostname;
56         GtkWidget *os;
57         GtkWidget *arch;
58         GtkWidget *fio_ver;
59 };
60
61 struct eta_widget {
62         GtkWidget *name;
63         GtkWidget *iotype;
64         GtkWidget *ioengine;
65         GtkWidget *iodepth;
66         GtkWidget *jobs;
67         GtkWidget *files;
68         GtkWidget *read_bw;
69         GtkWidget *read_iops;
70         GtkWidget *cr_bw;
71         GtkWidget *cr_iops;
72         GtkWidget *write_bw;
73         GtkWidget *write_iops;
74         GtkWidget *cw_bw;
75         GtkWidget *cw_iops;
76 };
77
78 struct gui {
79         GtkWidget *window;
80         GtkWidget *vbox;
81         GtkWidget *topvbox;
82         GtkWidget *topalign;
83         GtkWidget *bottomalign;
84         GtkWidget *thread_status_pb;
85         GtkWidget *buttonbox;
86         GtkWidget *button[ARRAYSIZE(buttonspeclist)];
87         GtkWidget *scrolled_window;
88         GtkWidget *textview;
89         GtkWidget *error_info_bar;
90         GtkWidget *error_label;
91         GtkTextBuffer *text;
92         struct probe_widget probe;
93         struct eta_widget eta;
94         int connected;
95         pthread_t t;
96
97         struct fio_client *client;
98         int nr_job_files;
99         char **job_files;
100 } ui;
101
102 static void clear_ui_info(struct gui *ui)
103 {
104         gtk_label_set_text(GTK_LABEL(ui->probe.hostname), "");
105         gtk_label_set_text(GTK_LABEL(ui->probe.os), "");
106         gtk_label_set_text(GTK_LABEL(ui->probe.arch), "");
107         gtk_label_set_text(GTK_LABEL(ui->probe.fio_ver), "");
108         gtk_entry_set_text(GTK_ENTRY(ui->eta.name), "");
109         gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), "");
110         gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), "");
111         gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), "");
112         gtk_entry_set_text(GTK_ENTRY(ui->eta.jobs), "");
113         gtk_entry_set_text(GTK_ENTRY(ui->eta.files), "");
114         gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), "");
115         gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), "");
116         gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), "");
117         gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), "");
118 }
119
120 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
121 {
122         GtkWidget *entry, *frame;
123
124         frame = gtk_frame_new(label);
125         entry = gtk_entry_new();
126         gtk_entry_set_editable(GTK_ENTRY(entry), 0);
127         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
128         gtk_container_add(GTK_CONTAINER(frame), entry);
129
130         return entry;
131 }
132
133 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
134 {
135         GtkWidget *label_widget;
136         GtkWidget *frame;
137
138         frame = gtk_frame_new(label);
139         label_widget = gtk_label_new(NULL);
140         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
141         gtk_container_add(GTK_CONTAINER(frame), label_widget);
142
143         return label_widget;
144 }
145
146 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
147 {
148         GtkWidget *button, *box;
149
150         box = gtk_hbox_new(FALSE, 3);
151         gtk_container_add(GTK_CONTAINER(hbox), box);
152
153         button = gtk_spin_button_new_with_range(min, max, 1.0);
154         gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
155
156         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
157         gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
158
159         return button;
160 }
161
162 static void gfio_set_connected(struct gui *ui, int connected)
163 {
164         if (connected) {
165                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
166                 ui->connected = 1;
167                 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Disconnect");
168         } else {
169                 ui->connected = 0;
170                 gtk_button_set_label(GTK_BUTTON(ui->button[CONNECT_BUTTON]), "Connect");
171                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
172         }
173 }
174
175 static void label_set_int_value(GtkWidget *entry, unsigned int val)
176 {
177         char tmp[80];
178
179         sprintf(tmp, "%u", val);
180         gtk_label_set_text(GTK_LABEL(entry), tmp);
181 }
182
183 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
184 {
185         char tmp[80];
186
187         sprintf(tmp, "%u", val);
188         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
189 }
190
191 #define ALIGN_LEFT 1
192 #define ALIGN_RIGHT 2
193 #define INVISIBLE 4
194 #define UNSORTABLE 8
195
196 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
197 {
198         GtkCellRenderer *renderer;
199         GtkTreeViewColumn *col;
200         double xalign = 0.0; /* left as default */
201         PangoAlignment align;
202         gboolean visible;
203
204         align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
205                 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
206                 PANGO_ALIGN_CENTER;
207         visible = !(flags & INVISIBLE);
208
209         renderer = gtk_cell_renderer_text_new();
210         col = gtk_tree_view_column_new();
211
212         gtk_tree_view_column_set_title(col, title);
213         if (!(flags & UNSORTABLE))
214                 gtk_tree_view_column_set_sort_column_id(col, index);
215         gtk_tree_view_column_set_resizable(col, TRUE);
216         gtk_tree_view_column_pack_start(col, renderer, TRUE);
217         gtk_tree_view_column_add_attribute(col, renderer, "text", index);
218         gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
219         switch (align) {
220         case PANGO_ALIGN_LEFT:
221                 xalign = 0.0;
222                 break;
223         case PANGO_ALIGN_CENTER:
224                 xalign = 0.5;
225                 break;
226         case PANGO_ALIGN_RIGHT:
227                 xalign = 1.0;
228                 break;
229         }
230         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
231         gtk_tree_view_column_set_visible(col, visible);
232         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
233         return col;
234 }
235
236 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
237                                                fio_fp64_t *plist,
238                                                unsigned int len,
239                                                const char *base,
240                                                unsigned int scale)
241 {
242         GType types[FIO_IO_U_LIST_MAX_LEN];
243         GtkWidget *tree_view;
244         GtkTreeSelection *selection;
245         GtkListStore *model;
246         GtkTreeIter iter;
247         int i;
248
249         for (i = 0; i < len; i++)
250                 types[i] = G_TYPE_INT;
251
252         model = gtk_list_store_newv(len, types);
253
254         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
255         gtk_widget_set_can_focus(tree_view, FALSE);
256
257         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
258         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
259
260         for (i = 0; i < len; i++) {
261                 char fbuf[8];
262
263                 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
264                 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
265         }
266
267         gtk_list_store_append(model, &iter);
268
269         for (i = 0; i < len; i++)
270                 gtk_list_store_set(model, &iter, i, ovals[i], -1);
271
272         return tree_view;
273 }
274
275 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
276                                        int ddir)
277 {
278         unsigned int *io_u_plat = ts->io_u_plat[ddir];
279         unsigned long nr = ts->clat_stat[ddir].samples;
280         fio_fp64_t *plist = ts->percentile_list;
281         unsigned int *ovals, len, minv, maxv, scale_down;
282         const char *base;
283         GtkWidget *tree_view, *frame, *hbox;
284         char tmp[64];
285
286         len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
287         if (!len)
288                 goto out;
289
290         /*
291          * We default to usecs, but if the value range is such that we
292          * should scale down to msecs, do that.
293          */
294         if (minv > 2000 && maxv > 99999) {
295                 scale_down = 1;
296                 base = "msec";
297         } else {
298                 scale_down = 0;
299                 base = "usec";
300         }
301
302         tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
303
304         sprintf(tmp, "Completion percentiles (%s)", base);
305         frame = gtk_frame_new(tmp);
306         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
307
308         hbox = gtk_hbox_new(FALSE, 3);
309         gtk_container_add(GTK_CONTAINER(frame), hbox);
310
311         gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
312 out:
313         if (ovals)
314                 free(ovals);
315 }
316
317 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
318                           unsigned long max, double mean, double dev)
319 {
320         const char *base = "(usec)";
321         GtkWidget *hbox, *label, *frame;
322         char *minp, *maxp;
323         char tmp[64];
324
325         if (!usec_to_msec(&min, &max, &mean, &dev))
326                 base = "(msec)";
327
328         minp = num2str(min, 6, 1, 0);
329         maxp = num2str(max, 6, 1, 0);
330
331         sprintf(tmp, "%s %s", name, base);
332         frame = gtk_frame_new(tmp);
333         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
334
335         hbox = gtk_hbox_new(FALSE, 3);
336         gtk_container_add(GTK_CONTAINER(frame), hbox);
337
338         label = new_info_label_in_frame(hbox, "Minimum");
339         gtk_label_set_text(GTK_LABEL(label), minp);
340         label = new_info_label_in_frame(hbox, "Maximum");
341         gtk_label_set_text(GTK_LABEL(label), maxp);
342         label = new_info_label_in_frame(hbox, "Average");
343         sprintf(tmp, "%5.02f", mean);
344         gtk_label_set_text(GTK_LABEL(label), tmp);
345         label = new_info_label_in_frame(hbox, "Standard deviation");
346         sprintf(tmp, "%5.02f", dev);
347         gtk_label_set_text(GTK_LABEL(label), tmp);
348
349         free(minp);
350         free(maxp);
351
352 }
353
354 #define GFIO_CLAT       1
355 #define GFIO_SLAT       2
356 #define GFIO_LAT        4
357
358 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
359                                   struct thread_stat *ts, int ddir)
360 {
361         const char *ddir_label[2] = { "Read", "Write" };
362         GtkWidget *frame, *label, *box, *vbox, *main_vbox;
363         unsigned long min, max, runt;
364         unsigned long long bw, iops;
365         unsigned int flags = 0;
366         double mean, dev;
367         char *io_p, *bw_p, *iops_p;
368         int i2p;
369
370         if (!ts->runtime[ddir])
371                 return;
372
373         i2p = is_power_of_2(rs->kb_base);
374         runt = ts->runtime[ddir];
375
376         bw = (1000 * ts->io_bytes[ddir]) / runt;
377         io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
378         bw_p = num2str(bw, 6, 1, i2p);
379
380         iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
381         iops_p = num2str(iops, 6, 1, 0);
382
383         box = gtk_hbox_new(FALSE, 3);
384         gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
385
386         frame = gtk_frame_new(ddir_label[ddir]);
387         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
388
389         main_vbox = gtk_vbox_new(FALSE, 3);
390         gtk_container_add(GTK_CONTAINER(frame), main_vbox);
391
392         box = gtk_hbox_new(FALSE, 3);
393         gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
394
395         label = new_info_label_in_frame(box, "IO");
396         gtk_label_set_text(GTK_LABEL(label), io_p);
397         label = new_info_label_in_frame(box, "Bandwidth");
398         gtk_label_set_text(GTK_LABEL(label), bw_p);
399         label = new_info_label_in_frame(box, "IOPS");
400         gtk_label_set_text(GTK_LABEL(label), iops_p);
401         label = new_info_label_in_frame(box, "Runtime (msec)");
402         label_set_int_value(label, ts->runtime[ddir]);
403
404         if (calc_lat(&ts->bw_stat[ddir], &min, &max, &mean, &dev)) {
405                 double p_of_agg = 100.0;
406                 const char *bw_str = "KB";
407                 char tmp[32];
408
409                 if (rs->agg[ddir]) {
410                         p_of_agg = mean * 100 / (double) rs->agg[ddir];
411                         if (p_of_agg > 100.0)
412                                 p_of_agg = 100.0;
413                 }
414
415                 if (mean > 999999.9) {
416                         min /= 1000.0;
417                         max /= 1000.0;
418                         mean /= 1000.0;
419                         dev /= 1000.0;
420                         bw_str = "MB";
421                 }
422
423                 sprintf(tmp, "Bandwidth (%s)", bw_str);
424                 frame = gtk_frame_new(tmp);
425                 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
426
427                 box = gtk_hbox_new(FALSE, 3);
428                 gtk_container_add(GTK_CONTAINER(frame), box);
429
430                 label = new_info_label_in_frame(box, "Minimum");
431                 label_set_int_value(label, min);
432                 label = new_info_label_in_frame(box, "Maximum");
433                 label_set_int_value(label, max);
434                 label = new_info_label_in_frame(box, "Percentage of jobs");
435                 sprintf(tmp, "%3.2f%%", p_of_agg);
436                 gtk_label_set_text(GTK_LABEL(label), tmp);
437                 label = new_info_label_in_frame(box, "Average");
438                 sprintf(tmp, "%5.02f", mean);
439                 gtk_label_set_text(GTK_LABEL(label), tmp);
440                 label = new_info_label_in_frame(box, "Standard deviation");
441                 sprintf(tmp, "%5.02f", dev);
442                 gtk_label_set_text(GTK_LABEL(label), tmp);
443         }
444
445         if (calc_lat(&ts->slat_stat[ddir], &min, &max, &mean, &dev))
446                 flags |= GFIO_SLAT;
447         if (calc_lat(&ts->clat_stat[ddir], &min, &max, &mean, &dev))
448                 flags |= GFIO_CLAT;
449         if (calc_lat(&ts->lat_stat[ddir], &min, &max, &mean, &dev))
450                 flags |= GFIO_LAT;
451
452         if (flags) {
453                 frame = gtk_frame_new("Latency");
454                 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
455
456                 vbox = gtk_vbox_new(FALSE, 3);
457                 gtk_container_add(GTK_CONTAINER(frame), vbox);
458
459                 if (flags & GFIO_SLAT)
460                         gfio_show_lat(vbox, "Submission latency", min, max, mean, dev);
461                 if (flags & GFIO_CLAT)
462                         gfio_show_lat(vbox, "Completion latency", min, max, mean, dev);
463                 if (flags & GFIO_LAT)
464                         gfio_show_lat(vbox, "Total latency", min, max, mean, dev);
465         }
466
467         if (ts->clat_percentiles)
468                 gfio_show_clat_percentiles(main_vbox, ts, ddir);
469
470
471         free(io_p);
472         free(bw_p);
473         free(iops_p);
474 }
475
476 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
477                                           const char **labels)
478 {
479         GtkWidget *tree_view;
480         GtkTreeSelection *selection;
481         GtkListStore *model;
482         GtkTreeIter iter;
483         GType *types;
484         int i, skipped;
485
486         /*
487          * Check if all are empty, in which case don't bother
488          */
489         for (i = 0, skipped = 0; i < num; i++)
490                 if (lat[i] <= 0.0)
491                         skipped++;
492
493         if (skipped == num)
494                 return NULL;
495
496         types = malloc(num * sizeof(GType));
497
498         for (i = 0; i < num; i++)
499                 types[i] = G_TYPE_STRING;
500
501         model = gtk_list_store_newv(num, types);
502         free(types);
503         types = NULL;
504
505         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
506         gtk_widget_set_can_focus(tree_view, FALSE);
507
508         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
509         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
510
511         for (i = 0; i < num; i++)
512                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
513
514         gtk_list_store_append(model, &iter);
515
516         for (i = 0; i < num; i++) {
517                 char fbuf[32];
518
519                 if (lat[i] <= 0.0)
520                         sprintf(fbuf, "0.00");
521                 else
522                         sprintf(fbuf, "%3.2f%%", lat[i]);
523
524                 gtk_list_store_set(model, &iter, i, fbuf, -1);
525         }
526
527         return tree_view;
528 }
529
530 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
531 {
532         GtkWidget *box, *frame, *tree_view;
533         double io_u_lat_u[FIO_IO_U_LAT_U_NR];
534         double io_u_lat_m[FIO_IO_U_LAT_M_NR];
535         const char *uranges[] = { "2", "4", "10", "20", "50", "100",
536                                   "250", "500", "750", "1000", };
537         const char *mranges[] = { "2", "4", "10", "20", "50", "100",
538                                   "250", "500", "750", "1000", "2000",
539                                   ">= 2000", };
540
541         stat_calc_lat_u(ts, io_u_lat_u);
542         stat_calc_lat_m(ts, io_u_lat_m);
543
544         tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
545         if (tree_view) {
546                 frame = gtk_frame_new("Latency buckets (usec)");
547                 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
548
549                 box = gtk_hbox_new(FALSE, 3);
550                 gtk_container_add(GTK_CONTAINER(frame), box);
551                 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
552         }
553
554         tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
555         if (tree_view) {
556                 frame = gtk_frame_new("Latency buckets (msec)");
557                 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
558
559                 box = gtk_hbox_new(FALSE, 3);
560                 gtk_container_add(GTK_CONTAINER(frame), box);
561                 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
562         }
563 }
564
565 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
566 {
567         GtkWidget *box, *frame, *entry;
568         double usr_cpu, sys_cpu;
569         unsigned long runtime;
570         char tmp[32];
571
572         runtime = ts->total_run_time;
573         if (runtime) {
574                 double runt = (double) runtime;
575
576                 usr_cpu = (double) ts->usr_time * 100 / runt;
577                 sys_cpu = (double) ts->sys_time * 100 / runt;
578         } else {
579                 usr_cpu = 0;
580                 sys_cpu = 0;
581         }
582
583         frame = gtk_frame_new("OS resources");
584         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
585
586         box = gtk_hbox_new(FALSE, 3);
587         gtk_container_add(GTK_CONTAINER(frame), box);
588
589         entry = new_info_entry_in_frame(box, "User CPU");
590         sprintf(tmp, "%3.2f%%", usr_cpu);
591         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
592         entry = new_info_entry_in_frame(box, "System CPU");
593         sprintf(tmp, "%3.2f%%", sys_cpu);
594         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
595         entry = new_info_entry_in_frame(box, "Context switches");
596         entry_set_int_value(entry, ts->ctx);
597         entry = new_info_entry_in_frame(box, "Major faults");
598         entry_set_int_value(entry, ts->majf);
599         entry = new_info_entry_in_frame(box, "Minor faults");
600         entry_set_int_value(entry, ts->minf);
601 }
602 static void gfio_add_sc_depths_tree(GtkListStore *model,
603                                     struct thread_stat *ts, unsigned int len,
604                                     int submit)
605 {
606         double io_u_dist[FIO_IO_U_MAP_NR];
607         GtkTreeIter iter;
608         /* Bits 0, and 3-8 */
609         const int add_mask = 0x1f9;
610         int i, j;
611
612         if (submit)
613                 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
614         else
615                 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
616
617         gtk_list_store_append(model, &iter);
618
619         gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
620
621         for (i = 1, j = 0; i < len; i++) {
622                 char fbuf[32];
623
624                 if (!(add_mask & (1UL << (i - 1))))
625                         sprintf(fbuf, "0.0%%");
626                 else {
627                         sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
628                         j++;
629                 }
630
631                 gtk_list_store_set(model, &iter, i, fbuf, -1);
632         }
633
634 }
635
636 static void gfio_add_total_depths_tree(GtkListStore *model,
637                                        struct thread_stat *ts, unsigned int len)
638 {
639         double io_u_dist[FIO_IO_U_MAP_NR];
640         GtkTreeIter iter;
641         /* Bits 1-6, and 8 */
642         const int add_mask = 0x17e;
643         int i, j;
644
645         stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
646
647         gtk_list_store_append(model, &iter);
648
649         gtk_list_store_set(model, &iter, 0, "Total", -1);
650
651         for (i = 1, j = 0; i < len; i++) {
652                 char fbuf[32];
653
654                 if (!(add_mask & (1UL << (i - 1))))
655                         sprintf(fbuf, "0.0%%");
656                 else {
657                         sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
658                         j++;
659                 }
660
661                 gtk_list_store_set(model, &iter, i, fbuf, -1);
662         }
663
664 }
665
666 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
667 {
668         GtkWidget *frame, *box, *tree_view;
669         GtkTreeSelection *selection;
670         GtkListStore *model;
671         GType types[FIO_IO_U_MAP_NR + 1];
672         int i;
673 #define NR_LABELS       10
674         const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
675
676         frame = gtk_frame_new("IO depths");
677         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
678
679         box = gtk_hbox_new(FALSE, 3);
680         gtk_container_add(GTK_CONTAINER(frame), box);
681
682         for (i = 0; i < NR_LABELS; i++)
683                 types[i] = G_TYPE_STRING;
684
685         model = gtk_list_store_newv(NR_LABELS, types);
686
687         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
688         gtk_widget_set_can_focus(tree_view, FALSE);
689
690         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
691         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
692
693         for (i = 0; i < NR_LABELS; i++)
694                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
695
696         gfio_add_total_depths_tree(model, ts, NR_LABELS);
697         gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
698         gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
699
700         gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
701 }
702
703 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
704                             struct group_run_stats *rs)
705 {
706         GtkWidget *dialog, *box, *vbox, *entry, *content;
707         struct gui *ui = client->client_data;
708
709         gdk_threads_enter();
710
711         dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
712                         GTK_DIALOG_DESTROY_WITH_PARENT,
713                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
714
715         g_signal_connect_swapped(dialog, "response",
716                              G_CALLBACK(gtk_widget_destroy),
717                              dialog);
718
719         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
720
721         vbox = gtk_vbox_new(FALSE, 3);
722         gtk_container_add(GTK_CONTAINER(content), vbox);
723
724         box = gtk_hbox_new(TRUE, 3);
725         gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
726
727         entry = new_info_entry_in_frame(box, "Name");
728         gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
729         if (strlen(ts->description)) {
730                 entry = new_info_entry_in_frame(box, "Description");
731                 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
732         }
733         entry = new_info_entry_in_frame(box, "Group ID");
734         entry_set_int_value(entry, ts->groupid);
735         entry = new_info_entry_in_frame(box, "Jobs");
736         entry_set_int_value(entry, ts->members);
737         entry = new_info_entry_in_frame(box, "Error");
738         entry_set_int_value(entry, ts->error);
739         entry = new_info_entry_in_frame(box, "PID");
740         entry_set_int_value(entry, ts->pid);
741
742         if (ts->io_bytes[DDIR_READ])
743                 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
744         if (ts->io_bytes[DDIR_WRITE])
745                 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
746
747         gfio_show_latency_buckets(vbox, ts);
748         gfio_show_cpu_usage(vbox, ts);
749         gfio_show_io_depths(vbox, ts);
750
751         gtk_widget_show_all(dialog);
752         gdk_threads_leave();
753 }
754
755 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
756 {
757 #if 0
758         GtkTextBuffer *buffer;
759         GtkTextIter end;
760
761         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
762         gdk_threads_enter();
763         gtk_text_buffer_get_end_iter(buffer, &end);
764         gtk_text_buffer_insert(buffer, &end, buf, -1);
765         gdk_threads_leave();
766         gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
767                                         &end, 0.0, FALSE, 0.0,0.0);
768 #else
769         fio_client_ops.text_op(client, cmd);
770 #endif
771 }
772
773 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
774 {
775         printf("gfio_disk_util_op called\n");
776         fio_client_ops.disk_util(client, cmd);
777 }
778
779 extern int sum_stat_clients;
780 extern struct thread_stat client_ts;
781 extern struct group_run_stats client_gs;
782
783 static int sum_stat_nr;
784
785 static void gfio_thread_status_op(struct fio_client *client,
786                                   struct fio_net_cmd *cmd)
787 {
788         struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
789
790         gfio_display_ts(client, &p->ts, &p->rs);
791
792         if (sum_stat_clients == 1)
793                 return;
794
795         sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
796         sum_group_stats(&client_gs, &p->rs);
797
798         client_ts.members++;
799         client_ts.groupid = p->ts.groupid;
800
801         if (++sum_stat_nr == sum_stat_clients) {
802                 strcpy(client_ts.name, "All clients");
803                 gfio_display_ts(client, &client_ts, &client_gs);
804         }
805 }
806
807 static void gfio_group_stats_op(struct fio_client *client,
808                                 struct fio_net_cmd *cmd)
809 {
810         printf("gfio_group_stats_op called\n");
811         fio_client_ops.group_stats(client, cmd);
812 }
813
814 static void gfio_update_eta(struct jobs_eta *je)
815 {
816         static int eta_good;
817         char eta_str[128];
818         char output[256];
819         char tmp[32];
820         double perc = 0.0;
821         int i2p = 0;
822
823         eta_str[0] = '\0';
824         output[0] = '\0';
825
826         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
827                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
828                 eta_to_str(eta_str, je->eta_sec);
829         }
830
831         sprintf(tmp, "%u", je->nr_running);
832         gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
833         sprintf(tmp, "%u", je->files_open);
834         gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
835
836 #if 0
837         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
838         if (je->m_rate || je->t_rate) {
839                 char *tr, *mr;
840
841                 mr = num2str(je->m_rate, 4, 0, i2p);
842                 tr = num2str(je->t_rate, 4, 0, i2p);
843                 gtk_entry_set_text(GTK_ENTRY(ui.eta);
844                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
845                 free(tr);
846                 free(mr);
847         } else if (je->m_iops || je->t_iops)
848                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
849
850         gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
851         gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
852         gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
853         gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
854 #endif
855
856         if (je->eta_sec != INT_MAX && je->nr_running) {
857                 char *iops_str[2];
858                 char *rate_str[2];
859
860                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
861                         strcpy(output, "-.-% done");
862                 else {
863                         eta_good = 1;
864                         perc *= 100.0;
865                         sprintf(output, "%3.1f%% done", perc);
866                 }
867
868                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
869                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
870
871                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
872                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
873
874                 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
875                 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
876                 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
877                 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
878
879                 free(rate_str[0]);
880                 free(rate_str[1]);
881                 free(iops_str[0]);
882                 free(iops_str[1]);
883         }
884
885         if (eta_str[0]) {
886                 char *dst = output + strlen(output);
887
888                 sprintf(dst, " - %s", eta_str);
889         }
890                 
891         gfio_update_thread_status(output, perc);
892 }
893
894 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
895 {
896         struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
897         const char *os, *arch;
898         char buf[64];
899
900         os = fio_get_os_string(probe->os);
901         if (!os)
902                 os = "unknown";
903
904         arch = fio_get_arch_string(probe->arch);
905         if (!arch)
906                 os = "unknown";
907
908         if (!client->name)
909                 client->name = strdup((char *) probe->hostname);
910
911         gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
912         gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
913         gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
914         sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
915         gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
916 }
917
918 static void gfio_update_thread_status(char *status_message, double perc)
919 {
920         static char message[100];
921         const char *m = message;
922
923         strncpy(message, status_message, sizeof(message) - 1);
924         gtk_progress_bar_set_text(
925                 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
926         gtk_progress_bar_set_fraction(
927                 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
928         gdk_threads_enter();
929         gtk_widget_queue_draw(ui.window);
930         gdk_threads_leave();
931 }
932
933 static void gfio_quit_op(struct fio_client *client)
934 {
935         struct gui *ui = client->client_data;
936
937         gfio_set_connected(ui, 0);
938 }
939
940 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
941 {
942         struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
943         struct gui *ui = client->client_data;
944         char tmp[8];
945         int i;
946
947         p->iodepth              = le32_to_cpu(p->iodepth);
948         p->rw                   = le32_to_cpu(p->rw);
949
950         for (i = 0; i < 2; i++) {
951                 p->min_bs[i]    = le32_to_cpu(p->min_bs[i]);
952                 p->max_bs[i]    = le32_to_cpu(p->max_bs[i]);
953         }
954
955         p->numjobs              = le32_to_cpu(p->numjobs);
956         p->group_reporting      = le32_to_cpu(p->group_reporting);
957
958         gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
959         gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
960         gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
961
962         sprintf(tmp, "%u", p->iodepth);
963         gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
964 }
965
966 static void gfio_client_timed_out(struct fio_client *client)
967 {
968         struct gui *ui = client->client_data;
969         GtkWidget *dialog, *label, *content;
970         char buf[256];
971
972         gdk_threads_enter();
973
974         gfio_set_connected(ui, 0);
975         clear_ui_info(ui);
976
977         sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
978
979         dialog = gtk_dialog_new_with_buttons("Timed out!",
980                         GTK_WINDOW(ui->window),
981                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
982                         GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
983
984         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
985         label = gtk_label_new((const gchar *) buf);
986         gtk_container_add(GTK_CONTAINER(content), label);
987         gtk_widget_show_all(dialog);
988         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
989
990         gtk_dialog_run(GTK_DIALOG(dialog));
991         gtk_widget_destroy(dialog);
992
993         gdk_threads_leave();
994 }
995
996 struct client_ops gfio_client_ops = {
997         .text_op                = gfio_text_op,
998         .disk_util              = gfio_disk_util_op,
999         .thread_status          = gfio_thread_status_op,
1000         .group_stats            = gfio_group_stats_op,
1001         .eta                    = gfio_update_eta,
1002         .probe                  = gfio_probe_op,
1003         .quit                   = gfio_quit_op,
1004         .add_job                = gfio_add_job_op,
1005         .timed_out              = gfio_client_timed_out,
1006         .stay_connected         = 1,
1007 };
1008
1009 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1010                 __attribute__((unused)) gpointer data)
1011 {
1012         gtk_main_quit();
1013 }
1014
1015 static void *job_thread(void *arg)
1016 {
1017         fio_handle_clients(&gfio_client_ops);
1018         return NULL;
1019 }
1020
1021 static int send_job_files(struct gui *ui)
1022 {
1023         int i, ret = 0;
1024
1025         for (i = 0; i < ui->nr_job_files; i++) {
1026                 ret = fio_clients_send_ini(ui->job_files[i]);
1027                 if (ret)
1028                         break;
1029
1030                 free(ui->job_files[i]);
1031                 ui->job_files[i] = NULL;
1032         }
1033         while (i < ui->nr_job_files) {
1034                 free(ui->job_files[i]);
1035                 ui->job_files[i] = NULL;
1036                 i++;
1037         }
1038
1039         return ret;
1040 }
1041
1042 static void start_job_thread(struct gui *ui)
1043 {
1044         if (send_job_files(ui)) {
1045                 printf("Yeah, I didn't really like those options too much.\n");
1046                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1047                 return;
1048         }
1049 }
1050
1051 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1052                 gpointer data)
1053 {
1054         struct gui *ui = data;
1055
1056         gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1057         start_job_thread(ui);
1058 }
1059
1060 static void file_open(GtkWidget *w, gpointer data);
1061
1062 static void connect_clicked(GtkWidget *widget, gpointer data)
1063 {
1064         struct gui *ui = data;
1065
1066         if (!ui->connected) {
1067                 if (!ui->nr_job_files)
1068                         file_open(widget, data);
1069                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1070                 fio_clients_connect();
1071                 pthread_create(&ui->t, NULL, job_thread, NULL);
1072                 gfio_set_connected(ui, 1);
1073         } else {
1074                 fio_clients_terminate();
1075                 gfio_set_connected(ui, 0);
1076                 clear_ui_info(ui);
1077         }
1078 }
1079
1080 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1081                         struct button_spec *buttonspec)
1082 {
1083         ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1084         g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1085         gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1086         gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1087         gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1088 }
1089
1090 static void add_buttons(struct gui *ui,
1091                                 struct button_spec *buttonlist,
1092                                 int nbuttons)
1093 {
1094         int i;
1095
1096         for (i = 0; i < nbuttons; i++)
1097                 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1098 }
1099
1100 static void on_info_bar_response(GtkWidget *widget, gint response,
1101                                  gpointer data)
1102 {
1103         if (response == GTK_RESPONSE_OK) {
1104                 gtk_widget_destroy(widget);
1105                 ui.error_info_bar = NULL;
1106         }
1107 }
1108
1109 void report_error(GError *error)
1110 {
1111         if (ui.error_info_bar == NULL) {
1112                 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1113                                                                GTK_RESPONSE_OK,
1114                                                                NULL);
1115                 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1116                 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1117                                               GTK_MESSAGE_ERROR);
1118                 
1119                 ui.error_label = gtk_label_new(error->message);
1120                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1121                 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1122                 
1123                 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1124                 gtk_widget_show_all(ui.vbox);
1125         } else {
1126                 char buffer[256];
1127                 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1128                 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1129         }
1130 }
1131
1132 static int get_connection_details(char **host, int *port, int *type,
1133                                   int *server_start)
1134 {
1135         GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1136         GtkWidget *button;
1137         char *typeentry;
1138
1139         dialog = gtk_dialog_new_with_buttons("Connection details",
1140                         GTK_WINDOW(ui.window),
1141                         GTK_DIALOG_DESTROY_WITH_PARENT,
1142                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1143                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1144
1145         frame = gtk_frame_new("Hostname / socket name");
1146         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1147         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1148
1149         box = gtk_vbox_new(FALSE, 6);
1150         gtk_container_add(GTK_CONTAINER(frame), box);
1151
1152         hbox = gtk_hbox_new(TRUE, 10);
1153         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1154         hentry = gtk_entry_new();
1155         gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1156         gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1157
1158         frame = gtk_frame_new("Port");
1159         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1160         box = gtk_vbox_new(FALSE, 10);
1161         gtk_container_add(GTK_CONTAINER(frame), box);
1162
1163         hbox = gtk_hbox_new(TRUE, 4);
1164         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1165         pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1166
1167         frame = gtk_frame_new("Type");
1168         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1169         box = gtk_vbox_new(FALSE, 10);
1170         gtk_container_add(GTK_CONTAINER(frame), box);
1171
1172         hbox = gtk_hbox_new(TRUE, 4);
1173         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1174
1175         combo = gtk_combo_box_text_new();
1176         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1177         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1178         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1179         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1180
1181         gtk_container_add(GTK_CONTAINER(hbox), combo);
1182
1183         frame = gtk_frame_new("Options");
1184         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1185         box = gtk_vbox_new(FALSE, 10);
1186         gtk_container_add(GTK_CONTAINER(frame), box);
1187
1188         hbox = gtk_hbox_new(TRUE, 4);
1189         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1190
1191         button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1192         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1193         gtk_widget_set_tooltip_text(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.");
1194         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1195
1196         gtk_widget_show_all(dialog);
1197
1198         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1199                 gtk_widget_destroy(dialog);
1200                 return 1;
1201         }
1202
1203         *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1204         *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1205
1206         typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1207         if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1208                 *type = Fio_client_ipv4;
1209         else if (!strncmp(typeentry, "IPv6", 4))
1210                 *type = Fio_client_ipv6;
1211         else
1212                 *type = Fio_client_socket;
1213         g_free(typeentry);
1214
1215         *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1216
1217         gtk_widget_destroy(dialog);
1218         return 0;
1219 }
1220
1221 static void file_open(GtkWidget *w, gpointer data)
1222 {
1223         GtkWidget *dialog;
1224         GSList *filenames, *fn_glist;
1225         GtkFileFilter *filter;
1226         char *host;
1227         int port, type, server_start;
1228
1229         dialog = gtk_file_chooser_dialog_new("Open File",
1230                 GTK_WINDOW(ui.window),
1231                 GTK_FILE_CHOOSER_ACTION_OPEN,
1232                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1233                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1234                 NULL);
1235         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1236
1237         filter = gtk_file_filter_new();
1238         gtk_file_filter_add_pattern(filter, "*.fio");
1239         gtk_file_filter_add_pattern(filter, "*.job");
1240         gtk_file_filter_add_mime_type(filter, "text/fio");
1241         gtk_file_filter_set_name(filter, "Fio job file");
1242         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1243
1244         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1245                 gtk_widget_destroy(dialog);
1246                 return;
1247         }
1248
1249         fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1250
1251         gtk_widget_destroy(dialog);
1252
1253         if (get_connection_details(&host, &port, &type, &server_start))
1254                 goto err;
1255
1256         filenames = fn_glist;
1257         while (filenames != NULL) {
1258                 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1259                 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1260                 ui.nr_job_files++;
1261
1262                 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1263                 if (!ui.client) {
1264                         GError *error;
1265
1266                         error = g_error_new(g_quark_from_string("fio"), 1,
1267                                         "Failed to add client %s", host);
1268                         report_error(error);
1269                         g_error_free(error);
1270                 }
1271                 ui.client->client_data = &ui;
1272                         
1273                 g_free(filenames->data);
1274                 filenames = g_slist_next(filenames);
1275         }
1276         free(host);
1277 err:
1278         g_slist_free(fn_glist);
1279 }
1280
1281 static void file_save(GtkWidget *w, gpointer data)
1282 {
1283         GtkWidget *dialog;
1284
1285         dialog = gtk_file_chooser_dialog_new("Save File",
1286                 GTK_WINDOW(ui.window),
1287                 GTK_FILE_CHOOSER_ACTION_SAVE,
1288                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1289                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1290                 NULL);
1291
1292         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1293         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1294
1295         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1296                 char *filename;
1297
1298                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1299                 // save_job_file(filename);
1300                 g_free(filename);
1301         }
1302         gtk_widget_destroy(dialog);
1303 }
1304
1305 static void preferences(GtkWidget *w, gpointer data)
1306 {
1307         GtkWidget *dialog, *frame, *box, **buttons;
1308         int i;
1309
1310         dialog = gtk_dialog_new_with_buttons("Preferences",
1311                 GTK_WINDOW(ui.window),
1312                 GTK_DIALOG_DESTROY_WITH_PARENT,
1313                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1314                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1315                 NULL);
1316
1317         frame = gtk_frame_new("Debug logging");
1318         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1319         box = gtk_hbox_new(FALSE, 6);
1320         gtk_container_add(GTK_CONTAINER(frame), box);
1321
1322         buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1323
1324         for (i = 0; i < FD_DEBUG_MAX; i++) {
1325                 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1326                 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1327                 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1328         }
1329
1330         gtk_widget_show_all(dialog);
1331
1332         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1333                 gtk_widget_destroy(dialog);
1334                 return;
1335         }
1336
1337         for (i = 0; i < FD_DEBUG_MAX; i++) {
1338                 int set;
1339
1340                 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1341                 if (set)
1342                         fio_debug |= (1UL << i);
1343         }
1344
1345         gtk_widget_destroy(dialog);
1346 }
1347
1348 static void about_dialog(GtkWidget *w, gpointer data)
1349 {
1350         gtk_show_about_dialog(NULL,
1351                 "program-name", "gfio",
1352                 "comments", "Gtk2 UI for fio",
1353                 "license", "GPLv2",
1354                 "version", fio_version_string,
1355                 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1356                 "logo-icon-name", "fio",
1357                 /* Must be last: */
1358                 NULL, NULL,
1359                 NULL);
1360 }
1361
1362 static GtkActionEntry menu_items[] = {
1363         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1364         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1365         { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
1366         { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
1367         { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1368         { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1369         { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
1370 };
1371 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1372
1373 static const gchar *ui_string = " \
1374         <ui> \
1375                 <menubar name=\"MainMenu\"> \
1376                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1377                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1378                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1379                                 <separator name=\"Separator\"/> \
1380                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1381                                 <separator name=\"Separator2\"/> \
1382                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
1383                         </menu> \
1384                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
1385                                 <menuitem name=\"About\" action=\"About\" /> \
1386                         </menu> \
1387                 </menubar> \
1388         </ui> \
1389 ";
1390
1391 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1392 {
1393         GtkActionGroup *action_group = gtk_action_group_new("Menu");
1394         GError *error = 0;
1395
1396         action_group = gtk_action_group_new("Menu");
1397         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1398
1399         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1400         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1401
1402         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1403         return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1404 }
1405
1406 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1407                    GtkWidget *vbox, GtkUIManager *ui_manager)
1408 {
1409         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1410 }
1411
1412 static void init_ui(int *argc, char **argv[], struct gui *ui)
1413 {
1414         GtkSettings *settings;
1415         GtkUIManager *uimanager;
1416         GtkWidget *menu, *probe, *probe_frame, *probe_box;
1417
1418         memset(ui, 0, sizeof(*ui));
1419
1420         /* Magical g*thread incantation, you just need this thread stuff.
1421          * Without it, the update that happens in gfio_update_thread_status
1422          * doesn't really happen in a timely fashion, you need expose events
1423          */
1424         if (!g_thread_supported())
1425                 g_thread_init(NULL);
1426         gdk_threads_init();
1427
1428         gtk_init(argc, argv);
1429         settings = gtk_settings_get_default();
1430         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1431         g_type_init();
1432         
1433         ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1434         gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1435         gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1436
1437         g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1438         g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1439
1440         ui->vbox = gtk_vbox_new(FALSE, 0);
1441         gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1442
1443         uimanager = gtk_ui_manager_new();
1444         menu = get_menubar_menu(ui->window, uimanager);
1445         gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1446
1447         /*
1448          * Set up alignments for widgets at the top of ui, 
1449          * align top left, expand horizontally but not vertically
1450          */
1451         ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1452         ui->topvbox = gtk_vbox_new(FALSE, 3);
1453         gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1454         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1455
1456         probe = gtk_frame_new("Job");
1457         gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1458         probe_frame = gtk_vbox_new(FALSE, 3);
1459         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1460
1461         probe_box = gtk_hbox_new(FALSE, 3);
1462         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1463         ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1464         ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1465         ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1466         ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1467
1468         probe_box = gtk_hbox_new(FALSE, 3);
1469         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1470
1471         ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1472         ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1473         ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1474         ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1475         ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1476         ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1477
1478         probe_box = gtk_hbox_new(FALSE, 3);
1479         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1480         ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1481         ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1482         ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1483         ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1484
1485         /*
1486          * Only add this if we have a commit rate
1487          */
1488 #if 0
1489         probe_box = gtk_hbox_new(FALSE, 3);
1490         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1491
1492         ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1493         ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1494
1495         ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1496         ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1497 #endif
1498
1499         /*
1500          * Add a text box for text op messages 
1501          */
1502         ui->textview = gtk_text_view_new();
1503         ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1504         gtk_text_buffer_set_text(ui->text, "", -1);
1505         gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1506         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1507         ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1508         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1509                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1510         gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1511         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1512                         TRUE, TRUE, 0);
1513
1514         /*
1515          * Set up alignments for widgets at the bottom of ui, 
1516          * align bottom left, expand horizontally but not vertically
1517          */
1518         ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1519         ui->buttonbox = gtk_hbox_new(FALSE, 0);
1520         gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1521         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1522                                         FALSE, FALSE, 0);
1523
1524         add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1525
1526         /*
1527          * Set up thread status progress bar
1528          */
1529         ui->thread_status_pb = gtk_progress_bar_new();
1530         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1531         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1532         gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1533
1534
1535         gtk_widget_show_all(ui->window);
1536 }
1537
1538 int main(int argc, char *argv[], char *envp[])
1539 {
1540         if (initialize_fio(envp))
1541                 return 1;
1542         if (fio_init_options())
1543                 return 1;
1544
1545         init_ui(&argc, &argv, &ui);
1546
1547         gdk_threads_enter();
1548         gtk_main();
1549         gdk_threads_leave();
1550         return 0;
1551 }