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