gfio: move bandwidth stats
[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
603 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
604 {
605         double io_u_dist[FIO_IO_U_MAP_NR];
606         double io_u_dist_s[FIO_IO_U_MAP_NR];
607         double io_u_dist_c[FIO_IO_U_MAP_NR];
608         GtkWidget *frame, *box, *tree_view;
609         GtkTreeSelection *selection;
610         GtkListStore *model;
611         GtkTreeIter iter;
612         GType types[FIO_IO_U_MAP_NR + 1];
613         int i;
614         const char *labels[] = { "Type", "0", "4", "8", "16", "32", "64", ">= 64" };
615
616         stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
617         stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist_s);
618         stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist_c);
619
620         frame = gtk_frame_new("IO depths");
621         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
622
623         box = gtk_hbox_new(FALSE, 3);
624         gtk_container_add(GTK_CONTAINER(frame), box);
625
626         for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++)
627                 types[i] = G_TYPE_STRING;
628
629         model = gtk_list_store_newv(FIO_IO_U_MAP_NR + 1, types);
630
631         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
632         gtk_widget_set_can_focus(tree_view, FALSE);
633
634         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
635         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
636
637         for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++)
638                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
639
640         gtk_list_store_append(model, &iter);
641
642         for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++) {
643                 char fbuf[32];
644
645                 if (i == 0) {
646                         gtk_list_store_set(model, &iter, i, "Total", -1);
647                         continue;
648                 }
649
650                 sprintf(fbuf, "%3.1f%%", io_u_dist[i - 1]);
651                 gtk_list_store_set(model, &iter, i, fbuf, -1);
652         }
653
654         gtk_list_store_append(model, &iter);
655
656         for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++) {
657                 char fbuf[32];
658
659                 if (i == 0) {
660                         gtk_list_store_set(model, &iter, i, "Submit", -1);
661                         continue;
662                 }
663
664                 sprintf(fbuf, "%3.1f%%", io_u_dist_s[i - 1]);
665                 gtk_list_store_set(model, &iter, i, fbuf, -1);
666         }
667
668         gtk_list_store_append(model, &iter);
669
670         for (i = 0; i < FIO_IO_U_MAP_NR + 1; i++) {
671                 char fbuf[32];
672
673                 if (i == 0) {
674                         gtk_list_store_set(model, &iter, i, "Complete", -1);
675                         continue;
676                 }
677
678                 sprintf(fbuf, "%3.1f%%", io_u_dist_c[i - 1]);
679                 gtk_list_store_set(model, &iter, i, fbuf, -1);
680         }
681
682         gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
683 }
684
685 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
686                             struct group_run_stats *rs)
687 {
688         GtkWidget *dialog, *box, *vbox, *entry, *content;
689         struct gui *ui = client->client_data;
690
691         gdk_threads_enter();
692
693         dialog = gtk_dialog_new_with_buttons("Results", GTK_WINDOW(ui->window),
694                         GTK_DIALOG_DESTROY_WITH_PARENT,
695                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
696
697         g_signal_connect_swapped(dialog, "response",
698                              G_CALLBACK(gtk_widget_destroy),
699                              dialog);
700
701         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
702
703         vbox = gtk_vbox_new(FALSE, 3);
704         gtk_container_add(GTK_CONTAINER(content), vbox);
705
706         box = gtk_hbox_new(TRUE, 3);
707         gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
708
709         entry = new_info_entry_in_frame(box, "Name");
710         gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
711         if (strlen(ts->description)) {
712                 entry = new_info_entry_in_frame(box, "Description");
713                 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
714         }
715         entry = new_info_entry_in_frame(box, "Group ID");
716         entry_set_int_value(entry, ts->groupid);
717         entry = new_info_entry_in_frame(box, "Jobs");
718         entry_set_int_value(entry, ts->members);
719         entry = new_info_entry_in_frame(box, "Error");
720         entry_set_int_value(entry, ts->error);
721         entry = new_info_entry_in_frame(box, "PID");
722         entry_set_int_value(entry, ts->pid);
723
724         if (ts->io_bytes[DDIR_READ])
725                 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
726         if (ts->io_bytes[DDIR_WRITE])
727                 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
728
729         gfio_show_latency_buckets(vbox, ts);
730         gfio_show_cpu_usage(vbox, ts);
731         gfio_show_io_depths(vbox, ts);
732
733         gtk_widget_show_all(dialog);
734         gdk_threads_leave();
735 }
736
737 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
738 {
739 #if 0
740         GtkTextBuffer *buffer;
741         GtkTextIter end;
742
743         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.textview));
744         gdk_threads_enter();
745         gtk_text_buffer_get_end_iter(buffer, &end);
746         gtk_text_buffer_insert(buffer, &end, buf, -1);
747         gdk_threads_leave();
748         gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
749                                         &end, 0.0, FALSE, 0.0,0.0);
750 #else
751         fio_client_ops.text_op(client, cmd);
752 #endif
753 }
754
755 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
756 {
757         printf("gfio_disk_util_op called\n");
758         fio_client_ops.disk_util(client, cmd);
759 }
760
761 extern int sum_stat_clients;
762 extern struct thread_stat client_ts;
763 extern struct group_run_stats client_gs;
764
765 static int sum_stat_nr;
766
767 static void gfio_thread_status_op(struct fio_client *client,
768                                   struct fio_net_cmd *cmd)
769 {
770         struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
771
772         gfio_display_ts(client, &p->ts, &p->rs);
773
774         if (sum_stat_clients == 1)
775                 return;
776
777         sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
778         sum_group_stats(&client_gs, &p->rs);
779
780         client_ts.members++;
781         client_ts.groupid = p->ts.groupid;
782
783         if (++sum_stat_nr == sum_stat_clients) {
784                 strcpy(client_ts.name, "All clients");
785                 gfio_display_ts(client, &client_ts, &client_gs);
786         }
787 }
788
789 static void gfio_group_stats_op(struct fio_client *client,
790                                 struct fio_net_cmd *cmd)
791 {
792         printf("gfio_group_stats_op called\n");
793         fio_client_ops.group_stats(client, cmd);
794 }
795
796 static void gfio_update_eta(struct jobs_eta *je)
797 {
798         static int eta_good;
799         char eta_str[128];
800         char output[256];
801         char tmp[32];
802         double perc = 0.0;
803         int i2p = 0;
804
805         eta_str[0] = '\0';
806         output[0] = '\0';
807
808         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
809                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
810                 eta_to_str(eta_str, je->eta_sec);
811         }
812
813         sprintf(tmp, "%u", je->nr_running);
814         gtk_entry_set_text(GTK_ENTRY(ui.eta.jobs), tmp);
815         sprintf(tmp, "%u", je->files_open);
816         gtk_entry_set_text(GTK_ENTRY(ui.eta.files), tmp);
817
818 #if 0
819         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
820         if (je->m_rate || je->t_rate) {
821                 char *tr, *mr;
822
823                 mr = num2str(je->m_rate, 4, 0, i2p);
824                 tr = num2str(je->t_rate, 4, 0, i2p);
825                 gtk_entry_set_text(GTK_ENTRY(ui.eta);
826                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
827                 free(tr);
828                 free(mr);
829         } else if (je->m_iops || je->t_iops)
830                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
831
832         gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_bw), "---");
833         gtk_entry_set_text(GTK_ENTRY(ui.eta.cr_iops), "---");
834         gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_bw), "---");
835         gtk_entry_set_text(GTK_ENTRY(ui.eta.cw_iops), "---");
836 #endif
837
838         if (je->eta_sec != INT_MAX && je->nr_running) {
839                 char *iops_str[2];
840                 char *rate_str[2];
841
842                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
843                         strcpy(output, "-.-% done");
844                 else {
845                         eta_good = 1;
846                         perc *= 100.0;
847                         sprintf(output, "%3.1f%% done", perc);
848                 }
849
850                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
851                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
852
853                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
854                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
855
856                 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_bw), rate_str[0]);
857                 gtk_entry_set_text(GTK_ENTRY(ui.eta.read_iops), iops_str[0]);
858                 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_bw), rate_str[1]);
859                 gtk_entry_set_text(GTK_ENTRY(ui.eta.write_iops), iops_str[1]);
860
861                 free(rate_str[0]);
862                 free(rate_str[1]);
863                 free(iops_str[0]);
864                 free(iops_str[1]);
865         }
866
867         if (eta_str[0]) {
868                 char *dst = output + strlen(output);
869
870                 sprintf(dst, " - %s", eta_str);
871         }
872                 
873         gfio_update_thread_status(output, perc);
874 }
875
876 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
877 {
878         struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
879         const char *os, *arch;
880         char buf[64];
881
882         os = fio_get_os_string(probe->os);
883         if (!os)
884                 os = "unknown";
885
886         arch = fio_get_arch_string(probe->arch);
887         if (!arch)
888                 os = "unknown";
889
890         if (!client->name)
891                 client->name = strdup((char *) probe->hostname);
892
893         gtk_label_set_text(GTK_LABEL(ui.probe.hostname), (char *) probe->hostname);
894         gtk_label_set_text(GTK_LABEL(ui.probe.os), os);
895         gtk_label_set_text(GTK_LABEL(ui.probe.arch), arch);
896         sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
897         gtk_label_set_text(GTK_LABEL(ui.probe.fio_ver), buf);
898 }
899
900 static void gfio_update_thread_status(char *status_message, double perc)
901 {
902         static char message[100];
903         const char *m = message;
904
905         strncpy(message, status_message, sizeof(message) - 1);
906         gtk_progress_bar_set_text(
907                 GTK_PROGRESS_BAR(ui.thread_status_pb), m);
908         gtk_progress_bar_set_fraction(
909                 GTK_PROGRESS_BAR(ui.thread_status_pb), perc / 100.0);
910         gdk_threads_enter();
911         gtk_widget_queue_draw(ui.window);
912         gdk_threads_leave();
913 }
914
915 static void gfio_quit_op(struct fio_client *client)
916 {
917         struct gui *ui = client->client_data;
918
919         gfio_set_connected(ui, 0);
920 }
921
922 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
923 {
924         struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
925         struct gui *ui = client->client_data;
926         char tmp[8];
927         int i;
928
929         p->iodepth              = le32_to_cpu(p->iodepth);
930         p->rw                   = le32_to_cpu(p->rw);
931
932         for (i = 0; i < 2; i++) {
933                 p->min_bs[i]    = le32_to_cpu(p->min_bs[i]);
934                 p->max_bs[i]    = le32_to_cpu(p->max_bs[i]);
935         }
936
937         p->numjobs              = le32_to_cpu(p->numjobs);
938         p->group_reporting      = le32_to_cpu(p->group_reporting);
939
940         gtk_entry_set_text(GTK_ENTRY(ui->eta.name), (gchar *) p->jobname);
941         gtk_entry_set_text(GTK_ENTRY(ui->eta.iotype), ddir_str(p->rw));
942         gtk_entry_set_text(GTK_ENTRY(ui->eta.ioengine), (gchar *) p->ioengine);
943
944         sprintf(tmp, "%u", p->iodepth);
945         gtk_entry_set_text(GTK_ENTRY(ui->eta.iodepth), tmp);
946 }
947
948 static void gfio_client_timed_out(struct fio_client *client)
949 {
950         struct gui *ui = client->client_data;
951         GtkWidget *dialog, *label, *content;
952         char buf[256];
953
954         gdk_threads_enter();
955
956         gfio_set_connected(ui, 0);
957         clear_ui_info(ui);
958
959         sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
960
961         dialog = gtk_dialog_new_with_buttons("Timed out!",
962                         GTK_WINDOW(ui->window),
963                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
964                         GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
965
966         content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
967         label = gtk_label_new((const gchar *) buf);
968         gtk_container_add(GTK_CONTAINER(content), label);
969         gtk_widget_show_all(dialog);
970         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
971
972         gtk_dialog_run(GTK_DIALOG(dialog));
973         gtk_widget_destroy(dialog);
974
975         gdk_threads_leave();
976 }
977
978 struct client_ops gfio_client_ops = {
979         .text_op                = gfio_text_op,
980         .disk_util              = gfio_disk_util_op,
981         .thread_status          = gfio_thread_status_op,
982         .group_stats            = gfio_group_stats_op,
983         .eta                    = gfio_update_eta,
984         .probe                  = gfio_probe_op,
985         .quit                   = gfio_quit_op,
986         .add_job                = gfio_add_job_op,
987         .timed_out              = gfio_client_timed_out,
988         .stay_connected         = 1,
989 };
990
991 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
992                 __attribute__((unused)) gpointer data)
993 {
994         gtk_main_quit();
995 }
996
997 static void *job_thread(void *arg)
998 {
999         fio_handle_clients(&gfio_client_ops);
1000         return NULL;
1001 }
1002
1003 static int send_job_files(struct gui *ui)
1004 {
1005         int i, ret = 0;
1006
1007         for (i = 0; i < ui->nr_job_files; i++) {
1008                 ret = fio_clients_send_ini(ui->job_files[i]);
1009                 if (ret)
1010                         break;
1011
1012                 free(ui->job_files[i]);
1013                 ui->job_files[i] = NULL;
1014         }
1015         while (i < ui->nr_job_files) {
1016                 free(ui->job_files[i]);
1017                 ui->job_files[i] = NULL;
1018                 i++;
1019         }
1020
1021         return ret;
1022 }
1023
1024 static void start_job_thread(struct gui *ui)
1025 {
1026         if (send_job_files(ui)) {
1027                 printf("Yeah, I didn't really like those options too much.\n");
1028                 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 1);
1029                 return;
1030         }
1031 }
1032
1033 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1034                 gpointer data)
1035 {
1036         struct gui *ui = data;
1037
1038         gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
1039         start_job_thread(ui);
1040 }
1041
1042 static void file_open(GtkWidget *w, gpointer data);
1043
1044 static void connect_clicked(GtkWidget *widget, gpointer data)
1045 {
1046         struct gui *ui = data;
1047
1048         if (!ui->connected) {
1049                 if (!ui->nr_job_files)
1050                         file_open(widget, data);
1051                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
1052                 fio_clients_connect();
1053                 pthread_create(&ui->t, NULL, job_thread, NULL);
1054                 gfio_set_connected(ui, 1);
1055         } else {
1056                 fio_clients_terminate();
1057                 gfio_set_connected(ui, 0);
1058                 clear_ui_info(ui);
1059         }
1060 }
1061
1062 static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1063                         struct button_spec *buttonspec)
1064 {
1065         ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1066         g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
1067         gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
1068         gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
1069         gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
1070 }
1071
1072 static void add_buttons(struct gui *ui,
1073                                 struct button_spec *buttonlist,
1074                                 int nbuttons)
1075 {
1076         int i;
1077
1078         for (i = 0; i < nbuttons; i++)
1079                 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1080 }
1081
1082 static void on_info_bar_response(GtkWidget *widget, gint response,
1083                                  gpointer data)
1084 {
1085         if (response == GTK_RESPONSE_OK) {
1086                 gtk_widget_destroy(widget);
1087                 ui.error_info_bar = NULL;
1088         }
1089 }
1090
1091 void report_error(GError *error)
1092 {
1093         if (ui.error_info_bar == NULL) {
1094                 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1095                                                                GTK_RESPONSE_OK,
1096                                                                NULL);
1097                 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1098                 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1099                                               GTK_MESSAGE_ERROR);
1100                 
1101                 ui.error_label = gtk_label_new(error->message);
1102                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1103                 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1104                 
1105                 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1106                 gtk_widget_show_all(ui.vbox);
1107         } else {
1108                 char buffer[256];
1109                 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1110                 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1111         }
1112 }
1113
1114 static int get_connection_details(char **host, int *port, int *type,
1115                                   int *server_start)
1116 {
1117         GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
1118         GtkWidget *button;
1119         char *typeentry;
1120
1121         dialog = gtk_dialog_new_with_buttons("Connection details",
1122                         GTK_WINDOW(ui.window),
1123                         GTK_DIALOG_DESTROY_WITH_PARENT,
1124                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1125                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1126
1127         frame = gtk_frame_new("Hostname / socket name");
1128         vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1129         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1130
1131         box = gtk_vbox_new(FALSE, 6);
1132         gtk_container_add(GTK_CONTAINER(frame), box);
1133
1134         hbox = gtk_hbox_new(TRUE, 10);
1135         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1136         hentry = gtk_entry_new();
1137         gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1138         gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1139
1140         frame = gtk_frame_new("Port");
1141         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1142         box = gtk_vbox_new(FALSE, 10);
1143         gtk_container_add(GTK_CONTAINER(frame), box);
1144
1145         hbox = gtk_hbox_new(TRUE, 4);
1146         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1147         pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1148
1149         frame = gtk_frame_new("Type");
1150         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1151         box = gtk_vbox_new(FALSE, 10);
1152         gtk_container_add(GTK_CONTAINER(frame), box);
1153
1154         hbox = gtk_hbox_new(TRUE, 4);
1155         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1156
1157         combo = gtk_combo_box_text_new();
1158         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv4");
1159         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "IPv6");
1160         gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), "local socket");
1161         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1162
1163         gtk_container_add(GTK_CONTAINER(hbox), combo);
1164
1165         frame = gtk_frame_new("Options");
1166         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1167         box = gtk_vbox_new(FALSE, 10);
1168         gtk_container_add(GTK_CONTAINER(frame), box);
1169
1170         hbox = gtk_hbox_new(TRUE, 4);
1171         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1172
1173         button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1174         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1175         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.");
1176         gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1177
1178         gtk_widget_show_all(dialog);
1179
1180         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1181                 gtk_widget_destroy(dialog);
1182                 return 1;
1183         }
1184
1185         *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1186         *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1187
1188         typeentry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
1189         if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1190                 *type = Fio_client_ipv4;
1191         else if (!strncmp(typeentry, "IPv6", 4))
1192                 *type = Fio_client_ipv6;
1193         else
1194                 *type = Fio_client_socket;
1195         g_free(typeentry);
1196
1197         *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1198
1199         gtk_widget_destroy(dialog);
1200         return 0;
1201 }
1202
1203 static void file_open(GtkWidget *w, gpointer data)
1204 {
1205         GtkWidget *dialog;
1206         GSList *filenames, *fn_glist;
1207         GtkFileFilter *filter;
1208         char *host;
1209         int port, type, server_start;
1210
1211         dialog = gtk_file_chooser_dialog_new("Open File",
1212                 GTK_WINDOW(ui.window),
1213                 GTK_FILE_CHOOSER_ACTION_OPEN,
1214                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1215                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1216                 NULL);
1217         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1218
1219         filter = gtk_file_filter_new();
1220         gtk_file_filter_add_pattern(filter, "*.fio");
1221         gtk_file_filter_add_pattern(filter, "*.job");
1222         gtk_file_filter_add_mime_type(filter, "text/fio");
1223         gtk_file_filter_set_name(filter, "Fio job file");
1224         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1225
1226         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1227                 gtk_widget_destroy(dialog);
1228                 return;
1229         }
1230
1231         fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1232
1233         gtk_widget_destroy(dialog);
1234
1235         if (get_connection_details(&host, &port, &type, &server_start))
1236                 goto err;
1237
1238         filenames = fn_glist;
1239         while (filenames != NULL) {
1240                 ui.job_files = realloc(ui.job_files, (ui.nr_job_files + 1) * sizeof(char *));
1241                 ui.job_files[ui.nr_job_files] = strdup(filenames->data);
1242                 ui.nr_job_files++;
1243
1244                 ui.client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1245                 if (!ui.client) {
1246                         GError *error;
1247
1248                         error = g_error_new(g_quark_from_string("fio"), 1,
1249                                         "Failed to add client %s", host);
1250                         report_error(error);
1251                         g_error_free(error);
1252                 }
1253                 ui.client->client_data = &ui;
1254                         
1255                 g_free(filenames->data);
1256                 filenames = g_slist_next(filenames);
1257         }
1258         free(host);
1259 err:
1260         g_slist_free(fn_glist);
1261 }
1262
1263 static void file_save(GtkWidget *w, gpointer data)
1264 {
1265         GtkWidget *dialog;
1266
1267         dialog = gtk_file_chooser_dialog_new("Save File",
1268                 GTK_WINDOW(ui.window),
1269                 GTK_FILE_CHOOSER_ACTION_SAVE,
1270                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1271                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1272                 NULL);
1273
1274         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1275         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1276
1277         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1278                 char *filename;
1279
1280                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1281                 // save_job_file(filename);
1282                 g_free(filename);
1283         }
1284         gtk_widget_destroy(dialog);
1285 }
1286
1287 static void preferences(GtkWidget *w, gpointer data)
1288 {
1289         GtkWidget *dialog, *frame, *box, **buttons;
1290         int i;
1291
1292         dialog = gtk_dialog_new_with_buttons("Preferences",
1293                 GTK_WINDOW(ui.window),
1294                 GTK_DIALOG_DESTROY_WITH_PARENT,
1295                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1296                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1297                 NULL);
1298
1299         frame = gtk_frame_new("Debug logging");
1300         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1301         box = gtk_hbox_new(FALSE, 6);
1302         gtk_container_add(GTK_CONTAINER(frame), box);
1303
1304         buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1305
1306         for (i = 0; i < FD_DEBUG_MAX; i++) {
1307                 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
1308                 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
1309                 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1310         }
1311
1312         gtk_widget_show_all(dialog);
1313
1314         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1315                 gtk_widget_destroy(dialog);
1316                 return;
1317         }
1318
1319         for (i = 0; i < FD_DEBUG_MAX; i++) {
1320                 int set;
1321
1322                 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1323                 if (set)
1324                         fio_debug |= (1UL << i);
1325         }
1326
1327         gtk_widget_destroy(dialog);
1328 }
1329
1330 static void about_dialog(GtkWidget *w, gpointer data)
1331 {
1332         gtk_show_about_dialog(NULL,
1333                 "program-name", "gfio",
1334                 "comments", "Gtk2 UI for fio",
1335                 "license", "GPLv2",
1336                 "version", fio_version_string,
1337                 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1338                 "logo-icon-name", "fio",
1339                 /* Must be last: */
1340                 NULL, NULL,
1341                 NULL);
1342 }
1343
1344 static GtkActionEntry menu_items[] = {
1345         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1346         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1347         { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
1348         { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
1349         { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
1350         { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1351         { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
1352 };
1353 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
1354
1355 static const gchar *ui_string = " \
1356         <ui> \
1357                 <menubar name=\"MainMenu\"> \
1358                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1359                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1360                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1361                                 <separator name=\"Separator\"/> \
1362                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1363                                 <separator name=\"Separator2\"/> \
1364                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
1365                         </menu> \
1366                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
1367                                 <menuitem name=\"About\" action=\"About\" /> \
1368                         </menu> \
1369                 </menubar> \
1370         </ui> \
1371 ";
1372
1373 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
1374 {
1375         GtkActionGroup *action_group = gtk_action_group_new("Menu");
1376         GError *error = 0;
1377
1378         action_group = gtk_action_group_new("Menu");
1379         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
1380
1381         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1382         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1383
1384         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1385         return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1386 }
1387
1388 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1389                    GtkWidget *vbox, GtkUIManager *ui_manager)
1390 {
1391         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1392 }
1393
1394 static void init_ui(int *argc, char **argv[], struct gui *ui)
1395 {
1396         GtkSettings *settings;
1397         GtkUIManager *uimanager;
1398         GtkWidget *menu, *probe, *probe_frame, *probe_box;
1399
1400         memset(ui, 0, sizeof(*ui));
1401
1402         /* Magical g*thread incantation, you just need this thread stuff.
1403          * Without it, the update that happens in gfio_update_thread_status
1404          * doesn't really happen in a timely fashion, you need expose events
1405          */
1406         if (!g_thread_supported())
1407                 g_thread_init(NULL);
1408         gdk_threads_init();
1409
1410         gtk_init(argc, argv);
1411         settings = gtk_settings_get_default();
1412         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1413         g_type_init();
1414         
1415         ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1416         gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1417         gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1418
1419         g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1420         g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
1421
1422         ui->vbox = gtk_vbox_new(FALSE, 0);
1423         gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
1424
1425         uimanager = gtk_ui_manager_new();
1426         menu = get_menubar_menu(ui->window, uimanager);
1427         gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1428
1429         /*
1430          * Set up alignments for widgets at the top of ui, 
1431          * align top left, expand horizontally but not vertically
1432          */
1433         ui->topalign = gtk_alignment_new(0, 0, 1, 0);
1434         ui->topvbox = gtk_vbox_new(FALSE, 3);
1435         gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
1436         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
1437
1438         probe = gtk_frame_new("Job");
1439         gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1440         probe_frame = gtk_vbox_new(FALSE, 3);
1441         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1442
1443         probe_box = gtk_hbox_new(FALSE, 3);
1444         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1445         ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1446         ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1447         ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1448         ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1449
1450         probe_box = gtk_hbox_new(FALSE, 3);
1451         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1452
1453         ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1454         ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1455         ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1456         ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1457         ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1458         ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
1459
1460         probe_box = gtk_hbox_new(FALSE, 3);
1461         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1462         ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1463         ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1464         ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1465         ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
1466
1467         /*
1468          * Only add this if we have a commit rate
1469          */
1470 #if 0
1471         probe_box = gtk_hbox_new(FALSE, 3);
1472         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
1473
1474         ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1475         ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1476
1477         ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1478         ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1479 #endif
1480
1481         /*
1482          * Add a text box for text op messages 
1483          */
1484         ui->textview = gtk_text_view_new();
1485         ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1486         gtk_text_buffer_set_text(ui->text, "", -1);
1487         gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1488         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1489         ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1490         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1491                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1492         gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
1493         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1494                         TRUE, TRUE, 0);
1495
1496         /*
1497          * Set up alignments for widgets at the bottom of ui, 
1498          * align bottom left, expand horizontally but not vertically
1499          */
1500         ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1501         ui->buttonbox = gtk_hbox_new(FALSE, 0);
1502         gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
1503         gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1504                                         FALSE, FALSE, 0);
1505
1506         add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
1507
1508         /*
1509          * Set up thread status progress bar
1510          */
1511         ui->thread_status_pb = gtk_progress_bar_new();
1512         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
1513         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
1514         gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1515
1516
1517         gtk_widget_show_all(ui->window);
1518 }
1519
1520 int main(int argc, char *argv[], char *envp[])
1521 {
1522         if (initialize_fio(envp))
1523                 return 1;
1524         if (fio_init_options())
1525                 return 1;
1526
1527         init_ui(&argc, &argv, &ui);
1528
1529         gdk_threads_enter();
1530         gtk_main();
1531         gdk_threads_leave();
1532         return 0;
1533 }