Only send job options once
[fio.git] / gfio.c
1 /*
2  * gfio - gui front end for fio - the flexible io tester
3  *
4  * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> 
5  * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
6  *
7  * The license below covers all files distributed with fio unless otherwise
8  * noted in the file itself.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2 as
12  *  published by the Free Software Foundation.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 #include <locale.h>
25 #include <malloc.h>
26 #include <string.h>
27
28 #include <glib.h>
29 #include <cairo.h>
30 #include <gtk/gtk.h>
31
32 #include "fio.h"
33 #include "graph.h"
34
35 static int gfio_server_running;
36 static const char *gfio_graph_font;
37 static unsigned int gfio_graph_limit = 100;
38
39 static void view_log(GtkWidget *w, gpointer data);
40
41 #define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
42
43 typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
44
45 static void connect_clicked(GtkWidget *widget, gpointer data);
46 static void start_job_clicked(GtkWidget *widget, gpointer data);
47 static void send_clicked(GtkWidget *widget, gpointer data);
48
49 static struct button_spec {
50         const char *buttontext;
51         clickfunction f;
52         const char *tooltiptext;
53         const int start_insensitive;
54 } buttonspeclist[] = {
55 #define CONNECT_BUTTON 0
56 #define SEND_BUTTON 1
57 #define START_JOB_BUTTON 2
58         { "Connect", connect_clicked, "Connect to host", 0 },
59         { "Send", send_clicked, "Send job description to host", 1 },
60         { "Start Job", start_job_clicked,
61                 "Start the current job on the server", 1 },
62 };
63
64 struct probe_widget {
65         GtkWidget *hostname;
66         GtkWidget *os;
67         GtkWidget *arch;
68         GtkWidget *fio_ver;
69 };
70
71 struct eta_widget {
72         GtkWidget *names;
73         GtkWidget *iotype;
74         GtkWidget *ioengine;
75         GtkWidget *iodepth;
76         GtkWidget *jobs;
77         GtkWidget *files;
78         GtkWidget *read_bw;
79         GtkWidget *read_iops;
80         GtkWidget *cr_bw;
81         GtkWidget *cr_iops;
82         GtkWidget *write_bw;
83         GtkWidget *write_iops;
84         GtkWidget *cw_bw;
85         GtkWidget *cw_iops;
86 };
87
88 struct gfio_graphs {
89 #define DRAWING_AREA_XDIM 1000
90 #define DRAWING_AREA_YDIM 400
91         GtkWidget *drawing_area;
92         struct graph *iops_graph;
93         struct graph *bandwidth_graph;
94 };
95
96 /*
97  * Main window widgets and data
98  */
99 struct gui {
100         GtkWidget *window;
101         GtkWidget *vbox;
102         GtkWidget *topvbox;
103         GtkWidget *topalign;
104         GtkWidget *bottomalign;
105         GtkWidget *thread_status_pb;
106         GtkWidget *buttonbox;
107         GtkWidget *scrolled_window;
108         GtkWidget *notebook;
109         GtkWidget *error_info_bar;
110         GtkWidget *error_label;
111         GtkListStore *log_model;
112         GtkWidget *log_tree;
113         GtkWidget *log_view;
114         struct gfio_graphs graphs;
115         struct probe_widget probe;
116         struct eta_widget eta;
117         pthread_t server_t;
118
119         pthread_t t;
120         int handler_running;
121
122         struct flist_head list;
123 } main_ui;
124
125 /*
126  * Notebook entry
127  */
128 struct gui_entry {
129         struct flist_head list;
130         struct gui *ui;
131
132         GtkWidget *vbox;
133         GtkWidget *topvbox;
134         GtkWidget *topalign;
135         GtkWidget *bottomalign;
136         GtkWidget *thread_status_pb;
137         GtkWidget *buttonbox;
138         GtkWidget *button[ARRAYSIZE(buttonspeclist)];
139         GtkWidget *scrolled_window;
140         GtkWidget *notebook;
141         GtkWidget *error_info_bar;
142         GtkWidget *error_label;
143         GtkWidget *results_notebook;
144         GtkWidget *results_window;
145         GtkListStore *log_model;
146         GtkWidget *log_tree;
147         GtkWidget *log_view;
148         struct gfio_graphs graphs;
149         struct probe_widget probe;
150         struct eta_widget eta;
151         GtkWidget *page_label;
152         gint page_num;
153         int connected;
154
155         struct gfio_client *client;
156         int nr_job_files;
157         char **job_files;
158 };
159
160 struct gfio_client {
161         struct gui_entry *ge;
162         struct fio_client *client;
163         GtkWidget *results_widget;
164         GtkWidget *disk_util_frame;
165         GtkWidget *err_entry;
166         unsigned int job_added;
167         struct thread_options o;
168 };
169
170 static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
171 static void gfio_update_thread_status_all(char *status_message, double perc);
172 void report_error(GError *error);
173
174 static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
175 {
176         switch (power_of_ten) {
177                 case 9: graph_y_title(g, "Billions of IOs / sec");
178                         break;
179                 case 6: graph_y_title(g, "Millions of IOs / sec");
180                         break;
181                 case 3: graph_y_title(g, "Thousands of IOs / sec");
182                         break;
183                 case 0:
184                 default: graph_y_title(g, "IOs / sec");
185                         break;
186         }
187 }
188
189 static struct graph *setup_iops_graph(void)
190 {
191         struct graph *g;
192
193         g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
194         graph_title(g, "IOPS");
195         graph_x_title(g, "Time (secs)");
196         graph_y_title(g, "IOs / sec");
197         graph_add_label(g, "Read IOPS");
198         graph_add_label(g, "Write IOPS");
199         graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
200         graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
201         line_graph_set_data_count_limit(g, gfio_graph_limit);
202         graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
203         graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
204         return g;
205 }
206
207 static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
208 {
209         switch (power_of_ten) {
210                 case 9: graph_y_title(g, "Petabytes / sec");
211                         break;
212                 case 6: graph_y_title(g, "Gigabytes / sec");
213                         break;
214                 case 3: graph_y_title(g, "Megabytes / sec");
215                         break;
216                 case 0:
217                 default: graph_y_title(g, "Kilobytes / sec");
218                         break;
219         }
220 }
221
222 static struct graph *setup_bandwidth_graph(void)
223 {
224         struct graph *g;
225
226         g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
227         graph_title(g, "Bandwidth");
228         graph_x_title(g, "Time (secs)");
229         graph_y_title(g, "Kbytes / sec");
230         graph_add_label(g, "Read Bandwidth");
231         graph_add_label(g, "Write Bandwidth");
232         graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
233         graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
234         line_graph_set_data_count_limit(g, 100);
235         graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
236         graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
237
238         return g;
239 }
240
241 static void setup_graphs(struct gfio_graphs *g)
242 {
243         g->iops_graph = setup_iops_graph();
244         g->bandwidth_graph = setup_bandwidth_graph();
245 }
246
247 static void clear_ge_ui_info(struct gui_entry *ge)
248 {
249         gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
250         gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
251         gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
252         gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
253 #if 0
254         /* should we empty it... */
255         gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
256 #endif
257         gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
258         gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
259         gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
260         gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
261         gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
262         gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
263         gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
264         gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
265         gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
266 }
267
268 static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
269 {
270         GtkWidget *entry, *frame;
271
272         frame = gtk_frame_new(label);
273         entry = gtk_combo_box_new_text();
274         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
275         gtk_container_add(GTK_CONTAINER(frame), entry);
276
277         return entry;
278 }
279
280 static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
281 {
282         GtkWidget *entry, *frame;
283
284         frame = gtk_frame_new(label);
285         entry = gtk_entry_new();
286         gtk_entry_set_editable(GTK_ENTRY(entry), 0);
287         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
288         gtk_container_add(GTK_CONTAINER(frame), entry);
289
290         return entry;
291 }
292
293 static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
294 {
295         GtkWidget *label_widget;
296         GtkWidget *frame;
297
298         frame = gtk_frame_new(label);
299         label_widget = gtk_label_new(NULL);
300         gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
301         gtk_container_add(GTK_CONTAINER(frame), label_widget);
302
303         return label_widget;
304 }
305
306 static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
307 {
308         GtkWidget *button, *box;
309
310         box = gtk_hbox_new(FALSE, 3);
311         gtk_container_add(GTK_CONTAINER(hbox), box);
312
313         button = gtk_spin_button_new_with_range(min, max, 1.0);
314         gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
315
316         gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
317         gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
318
319         return button;
320 }
321
322 static void gfio_set_connected(struct gui_entry *ge, int connected)
323 {
324         if (connected) {
325                 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
326                 ge->connected = 1;
327                 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
328                 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
329         } else {
330                 ge->connected = 0;
331                 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
332                 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
333                 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
334                 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
335         }
336 }
337
338 static void label_set_int_value(GtkWidget *entry, unsigned int val)
339 {
340         char tmp[80];
341
342         sprintf(tmp, "%u", val);
343         gtk_label_set_text(GTK_LABEL(entry), tmp);
344 }
345
346 static void entry_set_int_value(GtkWidget *entry, unsigned int val)
347 {
348         char tmp[80];
349
350         sprintf(tmp, "%u", val);
351         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
352 }
353
354 #define ALIGN_LEFT 1
355 #define ALIGN_RIGHT 2
356 #define INVISIBLE 4
357 #define UNSORTABLE 8
358
359 GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
360 {
361         GtkCellRenderer *renderer;
362         GtkTreeViewColumn *col;
363         double xalign = 0.0; /* left as default */
364         PangoAlignment align;
365         gboolean visible;
366
367         align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
368                 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
369                 PANGO_ALIGN_CENTER;
370         visible = !(flags & INVISIBLE);
371
372         renderer = gtk_cell_renderer_text_new();
373         col = gtk_tree_view_column_new();
374
375         gtk_tree_view_column_set_title(col, title);
376         if (!(flags & UNSORTABLE))
377                 gtk_tree_view_column_set_sort_column_id(col, index);
378         gtk_tree_view_column_set_resizable(col, TRUE);
379         gtk_tree_view_column_pack_start(col, renderer, TRUE);
380         gtk_tree_view_column_add_attribute(col, renderer, "text", index);
381         gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
382         switch (align) {
383         case PANGO_ALIGN_LEFT:
384                 xalign = 0.0;
385                 break;
386         case PANGO_ALIGN_CENTER:
387                 xalign = 0.5;
388                 break;
389         case PANGO_ALIGN_RIGHT:
390                 xalign = 1.0;
391                 break;
392         }
393         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
394         gtk_tree_view_column_set_visible(col, visible);
395         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
396         return col;
397 }
398
399 static void gfio_ui_setup_log(struct gui *ui)
400 {
401         GtkTreeSelection *selection;
402         GtkListStore *model;
403         GtkWidget *tree_view;
404
405         model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
406
407         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
408         gtk_widget_set_can_focus(tree_view, FALSE);
409
410         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
411         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
412         g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
413                 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
414
415         tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
416         tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
417         tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
418         tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
419
420         ui->log_model = model;
421         ui->log_tree = tree_view;
422 }
423
424 static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
425                                                fio_fp64_t *plist,
426                                                unsigned int len,
427                                                const char *base,
428                                                unsigned int scale)
429 {
430         GType types[FIO_IO_U_LIST_MAX_LEN];
431         GtkWidget *tree_view;
432         GtkTreeSelection *selection;
433         GtkListStore *model;
434         GtkTreeIter iter;
435         int i;
436
437         for (i = 0; i < len; i++)
438                 types[i] = G_TYPE_INT;
439
440         model = gtk_list_store_newv(len, types);
441
442         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
443         gtk_widget_set_can_focus(tree_view, FALSE);
444
445         g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
446                 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
447
448         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
449         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
450
451         for (i = 0; i < len; i++) {
452                 char fbuf[8];
453
454                 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
455                 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
456         }
457
458         gtk_list_store_append(model, &iter);
459
460         for (i = 0; i < len; i++) {
461                 if (scale)
462                         ovals[i] = (ovals[i] + 999) / 1000;
463                 gtk_list_store_set(model, &iter, i, ovals[i], -1);
464         }
465
466         return tree_view;
467 }
468
469 static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
470                                        int ddir)
471 {
472         unsigned int *io_u_plat = ts->io_u_plat[ddir];
473         unsigned long nr = ts->clat_stat[ddir].samples;
474         fio_fp64_t *plist = ts->percentile_list;
475         unsigned int *ovals, len, minv, maxv, scale_down;
476         const char *base;
477         GtkWidget *tree_view, *frame, *hbox;
478         char tmp[64];
479
480         len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
481         if (!len)
482                 goto out;
483
484         /*
485          * We default to usecs, but if the value range is such that we
486          * should scale down to msecs, do that.
487          */
488         if (minv > 2000 && maxv > 99999) {
489                 scale_down = 1;
490                 base = "msec";
491         } else {
492                 scale_down = 0;
493                 base = "usec";
494         }
495
496         tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
497
498         sprintf(tmp, "Completion percentiles (%s)", base);
499         frame = gtk_frame_new(tmp);
500         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
501
502         hbox = gtk_hbox_new(FALSE, 3);
503         gtk_container_add(GTK_CONTAINER(frame), hbox);
504
505         gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
506 out:
507         if (ovals)
508                 free(ovals);
509 }
510
511 static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
512                           unsigned long max, double mean, double dev)
513 {
514         const char *base = "(usec)";
515         GtkWidget *hbox, *label, *frame;
516         char *minp, *maxp;
517         char tmp[64];
518
519         if (!usec_to_msec(&min, &max, &mean, &dev))
520                 base = "(msec)";
521
522         minp = num2str(min, 6, 1, 0);
523         maxp = num2str(max, 6, 1, 0);
524
525         sprintf(tmp, "%s %s", name, base);
526         frame = gtk_frame_new(tmp);
527         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
528
529         hbox = gtk_hbox_new(FALSE, 3);
530         gtk_container_add(GTK_CONTAINER(frame), hbox);
531
532         label = new_info_label_in_frame(hbox, "Minimum");
533         gtk_label_set_text(GTK_LABEL(label), minp);
534         label = new_info_label_in_frame(hbox, "Maximum");
535         gtk_label_set_text(GTK_LABEL(label), maxp);
536         label = new_info_label_in_frame(hbox, "Average");
537         sprintf(tmp, "%5.02f", mean);
538         gtk_label_set_text(GTK_LABEL(label), tmp);
539         label = new_info_label_in_frame(hbox, "Standard deviation");
540         sprintf(tmp, "%5.02f", dev);
541         gtk_label_set_text(GTK_LABEL(label), tmp);
542
543         free(minp);
544         free(maxp);
545
546 }
547
548 #define GFIO_CLAT       1
549 #define GFIO_SLAT       2
550 #define GFIO_LAT        4
551
552 static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
553                                   struct thread_stat *ts, int ddir)
554 {
555         const char *ddir_label[2] = { "Read", "Write" };
556         GtkWidget *frame, *label, *box, *vbox, *main_vbox;
557         unsigned long min[3], max[3], runt;
558         unsigned long long bw, iops;
559         unsigned int flags = 0;
560         double mean[3], dev[3];
561         char *io_p, *bw_p, *iops_p;
562         int i2p;
563
564         if (!ts->runtime[ddir])
565                 return;
566
567         i2p = is_power_of_2(rs->kb_base);
568         runt = ts->runtime[ddir];
569
570         bw = (1000 * ts->io_bytes[ddir]) / runt;
571         io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
572         bw_p = num2str(bw, 6, 1, i2p);
573
574         iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
575         iops_p = num2str(iops, 6, 1, 0);
576
577         box = gtk_hbox_new(FALSE, 3);
578         gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
579
580         frame = gtk_frame_new(ddir_label[ddir]);
581         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
582
583         main_vbox = gtk_vbox_new(FALSE, 3);
584         gtk_container_add(GTK_CONTAINER(frame), main_vbox);
585
586         box = gtk_hbox_new(FALSE, 3);
587         gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
588
589         label = new_info_label_in_frame(box, "IO");
590         gtk_label_set_text(GTK_LABEL(label), io_p);
591         label = new_info_label_in_frame(box, "Bandwidth");
592         gtk_label_set_text(GTK_LABEL(label), bw_p);
593         label = new_info_label_in_frame(box, "IOPS");
594         gtk_label_set_text(GTK_LABEL(label), iops_p);
595         label = new_info_label_in_frame(box, "Runtime (msec)");
596         label_set_int_value(label, ts->runtime[ddir]);
597
598         if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
599                 double p_of_agg = 100.0;
600                 const char *bw_str = "KB";
601                 char tmp[32];
602
603                 if (rs->agg[ddir]) {
604                         p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
605                         if (p_of_agg > 100.0)
606                                 p_of_agg = 100.0;
607                 }
608
609                 if (mean[0] > 999999.9) {
610                         min[0] /= 1000.0;
611                         max[0] /= 1000.0;
612                         mean[0] /= 1000.0;
613                         dev[0] /= 1000.0;
614                         bw_str = "MB";
615                 }
616
617                 sprintf(tmp, "Bandwidth (%s)", bw_str);
618                 frame = gtk_frame_new(tmp);
619                 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
620
621                 box = gtk_hbox_new(FALSE, 3);
622                 gtk_container_add(GTK_CONTAINER(frame), box);
623
624                 label = new_info_label_in_frame(box, "Minimum");
625                 label_set_int_value(label, min[0]);
626                 label = new_info_label_in_frame(box, "Maximum");
627                 label_set_int_value(label, max[0]);
628                 label = new_info_label_in_frame(box, "Percentage of jobs");
629                 sprintf(tmp, "%3.2f%%", p_of_agg);
630                 gtk_label_set_text(GTK_LABEL(label), tmp);
631                 label = new_info_label_in_frame(box, "Average");
632                 sprintf(tmp, "%5.02f", mean[0]);
633                 gtk_label_set_text(GTK_LABEL(label), tmp);
634                 label = new_info_label_in_frame(box, "Standard deviation");
635                 sprintf(tmp, "%5.02f", dev[0]);
636                 gtk_label_set_text(GTK_LABEL(label), tmp);
637         }
638
639         if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
640                 flags |= GFIO_SLAT;
641         if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
642                 flags |= GFIO_CLAT;
643         if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
644                 flags |= GFIO_LAT;
645
646         if (flags) {
647                 frame = gtk_frame_new("Latency");
648                 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
649
650                 vbox = gtk_vbox_new(FALSE, 3);
651                 gtk_container_add(GTK_CONTAINER(frame), vbox);
652
653                 if (flags & GFIO_SLAT)
654                         gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
655                 if (flags & GFIO_CLAT)
656                         gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
657                 if (flags & GFIO_LAT)
658                         gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
659         }
660
661         if (ts->clat_percentiles)
662                 gfio_show_clat_percentiles(main_vbox, ts, ddir);
663
664
665         free(io_p);
666         free(bw_p);
667         free(iops_p);
668 }
669
670 static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
671                                           const char **labels)
672 {
673         GtkWidget *tree_view;
674         GtkTreeSelection *selection;
675         GtkListStore *model;
676         GtkTreeIter iter;
677         GType *types;
678         int i, skipped;
679
680         /*
681          * Check if all are empty, in which case don't bother
682          */
683         for (i = 0, skipped = 0; i < num; i++)
684                 if (lat[i] <= 0.0)
685                         skipped++;
686
687         if (skipped == num)
688                 return NULL;
689
690         types = malloc(num * sizeof(GType));
691
692         for (i = 0; i < num; i++)
693                 types[i] = G_TYPE_STRING;
694
695         model = gtk_list_store_newv(num, types);
696         free(types);
697         types = NULL;
698
699         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
700         gtk_widget_set_can_focus(tree_view, FALSE);
701
702         g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
703                 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
704
705         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
706         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
707
708         for (i = 0; i < num; i++)
709                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
710
711         gtk_list_store_append(model, &iter);
712
713         for (i = 0; i < num; i++) {
714                 char fbuf[32];
715
716                 if (lat[i] <= 0.0)
717                         sprintf(fbuf, "0.00");
718                 else
719                         sprintf(fbuf, "%3.2f%%", lat[i]);
720
721                 gtk_list_store_set(model, &iter, i, fbuf, -1);
722         }
723
724         return tree_view;
725 }
726
727 static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
728 {
729         GtkWidget *box, *frame, *tree_view;
730         double io_u_lat_u[FIO_IO_U_LAT_U_NR];
731         double io_u_lat_m[FIO_IO_U_LAT_M_NR];
732         const char *uranges[] = { "2", "4", "10", "20", "50", "100",
733                                   "250", "500", "750", "1000", };
734         const char *mranges[] = { "2", "4", "10", "20", "50", "100",
735                                   "250", "500", "750", "1000", "2000",
736                                   ">= 2000", };
737
738         stat_calc_lat_u(ts, io_u_lat_u);
739         stat_calc_lat_m(ts, io_u_lat_m);
740
741         tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
742         if (tree_view) {
743                 frame = gtk_frame_new("Latency buckets (usec)");
744                 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
745
746                 box = gtk_hbox_new(FALSE, 3);
747                 gtk_container_add(GTK_CONTAINER(frame), box);
748                 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
749         }
750
751         tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
752         if (tree_view) {
753                 frame = gtk_frame_new("Latency buckets (msec)");
754                 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
755
756                 box = gtk_hbox_new(FALSE, 3);
757                 gtk_container_add(GTK_CONTAINER(frame), box);
758                 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
759         }
760 }
761
762 static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
763 {
764         GtkWidget *box, *frame, *entry;
765         double usr_cpu, sys_cpu;
766         unsigned long runtime;
767         char tmp[32];
768
769         runtime = ts->total_run_time;
770         if (runtime) {
771                 double runt = (double) runtime;
772
773                 usr_cpu = (double) ts->usr_time * 100 / runt;
774                 sys_cpu = (double) ts->sys_time * 100 / runt;
775         } else {
776                 usr_cpu = 0;
777                 sys_cpu = 0;
778         }
779
780         frame = gtk_frame_new("OS resources");
781         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
782
783         box = gtk_hbox_new(FALSE, 3);
784         gtk_container_add(GTK_CONTAINER(frame), box);
785
786         entry = new_info_entry_in_frame(box, "User CPU");
787         sprintf(tmp, "%3.2f%%", usr_cpu);
788         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
789         entry = new_info_entry_in_frame(box, "System CPU");
790         sprintf(tmp, "%3.2f%%", sys_cpu);
791         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
792         entry = new_info_entry_in_frame(box, "Context switches");
793         entry_set_int_value(entry, ts->ctx);
794         entry = new_info_entry_in_frame(box, "Major faults");
795         entry_set_int_value(entry, ts->majf);
796         entry = new_info_entry_in_frame(box, "Minor faults");
797         entry_set_int_value(entry, ts->minf);
798 }
799 static void gfio_add_sc_depths_tree(GtkListStore *model,
800                                     struct thread_stat *ts, unsigned int len,
801                                     int submit)
802 {
803         double io_u_dist[FIO_IO_U_MAP_NR];
804         GtkTreeIter iter;
805         /* Bits 0, and 3-8 */
806         const int add_mask = 0x1f9;
807         int i, j;
808
809         if (submit)
810                 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
811         else
812                 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
813
814         gtk_list_store_append(model, &iter);
815
816         gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
817
818         for (i = 1, j = 0; i < len; i++) {
819                 char fbuf[32];
820
821                 if (!(add_mask & (1UL << (i - 1))))
822                         sprintf(fbuf, "0.0%%");
823                 else {
824                         sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
825                         j++;
826                 }
827
828                 gtk_list_store_set(model, &iter, i, fbuf, -1);
829         }
830
831 }
832
833 static void gfio_add_total_depths_tree(GtkListStore *model,
834                                        struct thread_stat *ts, unsigned int len)
835 {
836         double io_u_dist[FIO_IO_U_MAP_NR];
837         GtkTreeIter iter;
838         /* Bits 1-6, and 8 */
839         const int add_mask = 0x17e;
840         int i, j;
841
842         stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
843
844         gtk_list_store_append(model, &iter);
845
846         gtk_list_store_set(model, &iter, 0, "Total", -1);
847
848         for (i = 1, j = 0; i < len; i++) {
849                 char fbuf[32];
850
851                 if (!(add_mask & (1UL << (i - 1))))
852                         sprintf(fbuf, "0.0%%");
853                 else {
854                         sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
855                         j++;
856                 }
857
858                 gtk_list_store_set(model, &iter, i, fbuf, -1);
859         }
860
861 }
862
863 static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
864 {
865         GtkWidget *frame, *box, *tree_view;
866         GtkTreeSelection *selection;
867         GtkListStore *model;
868         GType types[FIO_IO_U_MAP_NR + 1];
869         int i;
870 #define NR_LABELS       10
871         const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
872
873         frame = gtk_frame_new("IO depths");
874         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
875
876         box = gtk_hbox_new(FALSE, 3);
877         gtk_container_add(GTK_CONTAINER(frame), box);
878
879         for (i = 0; i < NR_LABELS; i++)
880                 types[i] = G_TYPE_STRING;
881
882         model = gtk_list_store_newv(NR_LABELS, types);
883
884         tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
885         gtk_widget_set_can_focus(tree_view, FALSE);
886
887         g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
888                 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
889
890         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
891         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
892
893         for (i = 0; i < NR_LABELS; i++)
894                 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
895
896         gfio_add_total_depths_tree(model, ts, NR_LABELS);
897         gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
898         gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
899
900         gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
901 }
902
903 static gboolean results_window_delete(GtkWidget *w, gpointer data)
904 {
905         struct gui_entry *ge = (struct gui_entry *) data;
906
907         gtk_widget_destroy(w);
908         ge->results_window = NULL;
909         ge->results_notebook = NULL;
910         return TRUE;
911 }
912
913 static GtkWidget *get_results_window(struct gui_entry *ge)
914 {
915         GtkWidget *win, *notebook;
916
917         if (ge->results_window)
918                 return ge->results_notebook;
919
920         win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
921         gtk_window_set_title(GTK_WINDOW(win), "Results");
922         gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
923         g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
924         g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
925
926         notebook = gtk_notebook_new();
927         gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
928         gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
929         gtk_container_add(GTK_CONTAINER(win), notebook);
930
931         ge->results_window = win;
932         ge->results_notebook = notebook;
933         return ge->results_notebook;
934 }
935
936 static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
937                             struct group_run_stats *rs)
938 {
939         GtkWidget *res_win, *box, *vbox, *entry, *scroll;
940         struct gfio_client *gc = client->client_data;
941
942         gdk_threads_enter();
943
944         res_win = get_results_window(gc->ge);
945
946         scroll = gtk_scrolled_window_new(NULL, NULL);
947         gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
948         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
949
950         vbox = gtk_vbox_new(FALSE, 3);
951
952         box = gtk_hbox_new(FALSE, 0);
953         gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
954
955         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
956
957         gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
958
959         gc->results_widget = vbox;
960
961         entry = new_info_entry_in_frame(box, "Name");
962         gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
963         if (strlen(ts->description)) {
964                 entry = new_info_entry_in_frame(box, "Description");
965                 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
966         }
967         entry = new_info_entry_in_frame(box, "Group ID");
968         entry_set_int_value(entry, ts->groupid);
969         entry = new_info_entry_in_frame(box, "Jobs");
970         entry_set_int_value(entry, ts->members);
971         gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
972         entry_set_int_value(entry, ts->error);
973         entry = new_info_entry_in_frame(box, "PID");
974         entry_set_int_value(entry, ts->pid);
975
976         if (ts->io_bytes[DDIR_READ])
977                 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
978         if (ts->io_bytes[DDIR_WRITE])
979                 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
980
981         gfio_show_latency_buckets(vbox, ts);
982         gfio_show_cpu_usage(vbox, ts);
983         gfio_show_io_depths(vbox, ts);
984
985         gtk_widget_show_all(gc->ge->results_window);
986         gdk_threads_leave();
987 }
988
989 static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
990 {
991         struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
992         struct gui *ui = &main_ui;
993         GtkTreeIter iter;
994         struct tm *tm;
995         time_t sec;
996         char tmp[64], timebuf[80];
997
998         sec = p->log_sec;
999         tm = localtime(&sec);
1000         strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1001         sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1002
1003         gdk_threads_enter();
1004
1005         gtk_list_store_append(ui->log_model, &iter);
1006         gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1007         gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1008         gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1009         gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1010
1011         if (p->level == FIO_LOG_ERR)
1012                 view_log(NULL, (gpointer) ui);
1013
1014         gdk_threads_leave();
1015 }
1016
1017 static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1018 {
1019         struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1020         struct gfio_client *gc = client->client_data;
1021         GtkWidget *box, *frame, *entry, *vbox;
1022         double util;
1023         char tmp[16];
1024
1025         gdk_threads_enter();
1026
1027         if (!gc->results_widget)
1028                 goto out;
1029
1030         if (!gc->disk_util_frame) {
1031                 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1032                 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1033         }
1034
1035         vbox = gtk_vbox_new(FALSE, 3);
1036         gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1037
1038         frame = gtk_frame_new((char *) p->dus.name);
1039         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1040
1041         box = gtk_vbox_new(FALSE, 3);
1042         gtk_container_add(GTK_CONTAINER(frame), box);
1043
1044         frame = gtk_frame_new("Read");
1045         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1046         vbox = gtk_hbox_new(TRUE, 3);
1047         gtk_container_add(GTK_CONTAINER(frame), vbox);
1048         entry = new_info_entry_in_frame(vbox, "IOs");
1049         entry_set_int_value(entry, p->dus.ios[0]);
1050         entry = new_info_entry_in_frame(vbox, "Merges");
1051         entry_set_int_value(entry, p->dus.merges[0]);
1052         entry = new_info_entry_in_frame(vbox, "Sectors");
1053         entry_set_int_value(entry, p->dus.sectors[0]);
1054         entry = new_info_entry_in_frame(vbox, "Ticks");
1055         entry_set_int_value(entry, p->dus.ticks[0]);
1056
1057         frame = gtk_frame_new("Write");
1058         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1059         vbox = gtk_hbox_new(TRUE, 3);
1060         gtk_container_add(GTK_CONTAINER(frame), vbox);
1061         entry = new_info_entry_in_frame(vbox, "IOs");
1062         entry_set_int_value(entry, p->dus.ios[1]);
1063         entry = new_info_entry_in_frame(vbox, "Merges");
1064         entry_set_int_value(entry, p->dus.merges[1]);
1065         entry = new_info_entry_in_frame(vbox, "Sectors");
1066         entry_set_int_value(entry, p->dus.sectors[1]);
1067         entry = new_info_entry_in_frame(vbox, "Ticks");
1068         entry_set_int_value(entry, p->dus.ticks[1]);
1069
1070         frame = gtk_frame_new("Shared");
1071         gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1072         vbox = gtk_hbox_new(TRUE, 3);
1073         gtk_container_add(GTK_CONTAINER(frame), vbox);
1074         entry = new_info_entry_in_frame(vbox, "IO ticks");
1075         entry_set_int_value(entry, p->dus.io_ticks);
1076         entry = new_info_entry_in_frame(vbox, "Time in queue");
1077         entry_set_int_value(entry, p->dus.time_in_queue);
1078
1079         util = 0.0;
1080         if (p->dus.msec)
1081                 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1082         if (util > 100.0)
1083                 util = 100.0;
1084
1085         sprintf(tmp, "%3.2f%%", util);
1086         entry = new_info_entry_in_frame(vbox, "Disk utilization");
1087         gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1088
1089         gtk_widget_show_all(gc->results_widget);
1090 out:
1091         gdk_threads_leave();
1092 }
1093
1094 extern int sum_stat_clients;
1095 extern struct thread_stat client_ts;
1096 extern struct group_run_stats client_gs;
1097
1098 static int sum_stat_nr;
1099
1100 static void gfio_thread_status_op(struct fio_client *client,
1101                                   struct fio_net_cmd *cmd)
1102 {
1103         struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1104
1105         gfio_display_ts(client, &p->ts, &p->rs);
1106
1107         if (sum_stat_clients == 1)
1108                 return;
1109
1110         sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1111         sum_group_stats(&client_gs, &p->rs);
1112
1113         client_ts.members++;
1114         client_ts.groupid = p->ts.groupid;
1115
1116         if (++sum_stat_nr == sum_stat_clients) {
1117                 strcpy(client_ts.name, "All clients");
1118                 gfio_display_ts(client, &client_ts, &client_gs);
1119         }
1120 }
1121
1122 static void gfio_group_stats_op(struct fio_client *client,
1123                                 struct fio_net_cmd *cmd)
1124 {
1125         /* We're ignoring group stats for now */
1126 }
1127
1128 static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1129                                    gpointer data)
1130 {
1131         struct gfio_graphs *g = data;
1132
1133         graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1134         graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1135         graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1136         graph_set_position(g->bandwidth_graph, 0, 0);
1137         return TRUE;
1138 }
1139
1140 static void draw_graph(struct graph *g, cairo_t *cr)
1141 {
1142         line_graph_draw(g, cr);
1143         cairo_stroke(cr);
1144 }
1145
1146 static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1147 {
1148         struct gfio_graphs *g = p;
1149         cairo_t *cr;
1150
1151         cr = gdk_cairo_create(w->window);
1152         cairo_set_source_rgb(cr, 0, 0, 0);
1153         draw_graph(g->iops_graph, cr);
1154         draw_graph(g->bandwidth_graph, cr);
1155         cairo_destroy(cr);
1156
1157         return FALSE;
1158 }
1159
1160 /*
1161  * Client specific ETA
1162  */
1163 static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1164 {
1165         struct gfio_client *gc = client->client_data;
1166         struct gui_entry *ge = gc->ge;
1167         static int eta_good;
1168         char eta_str[128];
1169         char output[256];
1170         char tmp[32];
1171         double perc = 0.0;
1172         int i2p = 0;
1173
1174         gdk_threads_enter();
1175
1176         eta_str[0] = '\0';
1177         output[0] = '\0';
1178
1179         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1180                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1181                 eta_to_str(eta_str, je->eta_sec);
1182         }
1183
1184         sprintf(tmp, "%u", je->nr_running);
1185         gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1186         sprintf(tmp, "%u", je->files_open);
1187         gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1188
1189 #if 0
1190         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1191         if (je->m_rate || je->t_rate) {
1192                 char *tr, *mr;
1193
1194                 mr = num2str(je->m_rate, 4, 0, i2p);
1195                 tr = num2str(je->t_rate, 4, 0, i2p);
1196                 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1197                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1198                 free(tr);
1199                 free(mr);
1200         } else if (je->m_iops || je->t_iops)
1201                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1202
1203         gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1204         gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1205         gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1206         gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1207 #endif
1208
1209         if (je->eta_sec != INT_MAX && je->nr_running) {
1210                 char *iops_str[2];
1211                 char *rate_str[2];
1212
1213                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1214                         strcpy(output, "-.-% done");
1215                 else {
1216                         eta_good = 1;
1217                         perc *= 100.0;
1218                         sprintf(output, "%3.1f%% done", perc);
1219                 }
1220
1221                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1222                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1223
1224                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1225                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1226
1227                 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1228                 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1229                 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1230                 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1231
1232                 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1233                 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1234                 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1235                 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1236
1237                 free(rate_str[0]);
1238                 free(rate_str[1]);
1239                 free(iops_str[0]);
1240                 free(iops_str[1]);
1241         }
1242
1243         if (eta_str[0]) {
1244                 char *dst = output + strlen(output);
1245
1246                 sprintf(dst, " - %s", eta_str);
1247         }
1248                 
1249         gfio_update_thread_status(ge, output, perc);
1250         gdk_threads_leave();
1251 }
1252
1253 /*
1254  * Update ETA in main window for all clients
1255  */
1256 static void gfio_update_all_eta(struct jobs_eta *je)
1257 {
1258         struct gui *ui = &main_ui;
1259         static int eta_good;
1260         char eta_str[128];
1261         char output[256];
1262         double perc = 0.0;
1263         int i2p = 0;
1264
1265         gdk_threads_enter();
1266
1267         eta_str[0] = '\0';
1268         output[0] = '\0';
1269
1270         if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1271                 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1272                 eta_to_str(eta_str, je->eta_sec);
1273         }
1274
1275 #if 0
1276         if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1277         if (je->m_rate || je->t_rate) {
1278                 char *tr, *mr;
1279
1280                 mr = num2str(je->m_rate, 4, 0, i2p);
1281                 tr = num2str(je->t_rate, 4, 0, i2p);
1282                 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1283                 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1284                 free(tr);
1285                 free(mr);
1286         } else if (je->m_iops || je->t_iops)
1287                 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1288
1289         gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1290         gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1291         gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1292         gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1293 #endif
1294
1295         entry_set_int_value(ui->eta.jobs, je->nr_running);
1296
1297         if (je->eta_sec != INT_MAX && je->nr_running) {
1298                 char *iops_str[2];
1299                 char *rate_str[2];
1300
1301                 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1302                         strcpy(output, "-.-% done");
1303                 else {
1304                         eta_good = 1;
1305                         perc *= 100.0;
1306                         sprintf(output, "%3.1f%% done", perc);
1307                 }
1308
1309                 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1310                 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1311
1312                 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1313                 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1314
1315                 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1316                 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1317                 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1318                 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1319
1320                 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1321                 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1322                 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1323                 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1324
1325                 free(rate_str[0]);
1326                 free(rate_str[1]);
1327                 free(iops_str[0]);
1328                 free(iops_str[1]);
1329         }
1330
1331         if (eta_str[0]) {
1332                 char *dst = output + strlen(output);
1333
1334                 sprintf(dst, " - %s", eta_str);
1335         }
1336                 
1337         gfio_update_thread_status_all(output, perc);
1338         gdk_threads_leave();
1339 }
1340
1341 static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1342 {
1343         struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1344         struct gfio_client *gc = client->client_data;
1345         struct gui_entry *ge = gc->ge;
1346         const char *os, *arch;
1347         char buf[64];
1348
1349         os = fio_get_os_string(probe->os);
1350         if (!os)
1351                 os = "unknown";
1352
1353         arch = fio_get_arch_string(probe->arch);
1354         if (!arch)
1355                 os = "unknown";
1356
1357         if (!client->name)
1358                 client->name = strdup((char *) probe->hostname);
1359
1360         gdk_threads_enter();
1361
1362         gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1363         gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1364         gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1365         sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1366         gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1367
1368         gfio_set_connected(ge, 1);
1369
1370         gdk_threads_leave();
1371 }
1372
1373 static void gfio_update_thread_status(struct gui_entry *ge,
1374                                       char *status_message, double perc)
1375 {
1376         static char message[100];
1377         const char *m = message;
1378
1379         strncpy(message, status_message, sizeof(message) - 1);
1380         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1381         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1382         gtk_widget_queue_draw(main_ui.window);
1383 }
1384
1385 static void gfio_update_thread_status_all(char *status_message, double perc)
1386 {
1387         struct gui *ui = &main_ui;
1388         static char message[100];
1389         const char *m = message;
1390
1391         strncpy(message, status_message, sizeof(message) - 1);
1392         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1393         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1394         gtk_widget_queue_draw(ui->window);
1395 }
1396
1397 static void gfio_quit_op(struct fio_client *client)
1398 {
1399         struct gfio_client *gc = client->client_data;
1400
1401         gdk_threads_enter();
1402         gfio_set_connected(gc->ge, 0);
1403         gdk_threads_leave();
1404 }
1405
1406 static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1407 {
1408         struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1409         struct gfio_client *gc = client->client_data;
1410         struct thread_options *o = &gc->o;
1411         struct gui_entry *ge = gc->ge;
1412         char tmp[8];
1413
1414         convert_thread_options_to_cpu(o, &p->top);
1415
1416         gdk_threads_enter();
1417
1418         gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1419
1420         gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1421         gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1422
1423         gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1424         gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1425
1426         sprintf(tmp, "%u", o->iodepth);
1427         gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1428
1429         gc->job_added++;
1430
1431         gdk_threads_leave();
1432 }
1433
1434 static void gfio_client_timed_out(struct fio_client *client)
1435 {
1436         struct gfio_client *gc = client->client_data;
1437         GtkWidget *dialog, *label, *content;
1438         char buf[256];
1439
1440         gdk_threads_enter();
1441
1442         gfio_set_connected(gc->ge, 0);
1443         clear_ge_ui_info(gc->ge);
1444
1445         sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1446
1447         dialog = gtk_dialog_new_with_buttons("Timed out!",
1448                         GTK_WINDOW(main_ui.window),
1449                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1450                         GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1451
1452         /* gtk_dialog_get_content_area() is 2.14 and newer */
1453         content = GTK_DIALOG(dialog)->vbox;
1454
1455         label = gtk_label_new((const gchar *) buf);
1456         gtk_container_add(GTK_CONTAINER(content), label);
1457         gtk_widget_show_all(dialog);
1458         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1459
1460         gtk_dialog_run(GTK_DIALOG(dialog));
1461         gtk_widget_destroy(dialog);
1462
1463         gdk_threads_leave();
1464 }
1465
1466 static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1467 {
1468         struct gfio_client *gc = client->client_data;
1469
1470         gdk_threads_enter();
1471
1472         gfio_set_connected(gc->ge, 0);
1473
1474         if (gc->err_entry)
1475                 entry_set_int_value(gc->err_entry, client->error);
1476
1477         gdk_threads_leave();
1478 }
1479
1480 struct client_ops gfio_client_ops = {
1481         .text_op                = gfio_text_op,
1482         .disk_util              = gfio_disk_util_op,
1483         .thread_status          = gfio_thread_status_op,
1484         .group_stats            = gfio_group_stats_op,
1485         .jobs_eta               = gfio_update_client_eta,
1486         .eta                    = gfio_update_all_eta,
1487         .probe                  = gfio_probe_op,
1488         .quit                   = gfio_quit_op,
1489         .add_job                = gfio_add_job_op,
1490         .timed_out              = gfio_client_timed_out,
1491         .stop                   = gfio_client_stop,
1492         .eta_msec               = FIO_CLIENT_DEF_ETA_MSEC,
1493         .stay_connected         = 1,
1494 };
1495
1496 static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1497                 __attribute__((unused)) gpointer data)
1498 {
1499         gtk_main_quit();
1500 }
1501
1502 static void *job_thread(void *arg)
1503 {
1504         struct gui *ui = arg;
1505
1506         ui->handler_running = 1;
1507         fio_handle_clients(&gfio_client_ops);
1508         ui->handler_running = 0;
1509         return NULL;
1510 }
1511
1512 static int send_job_files(struct gui_entry *ge)
1513 {
1514         struct gfio_client *gc = ge->client;
1515         int i, ret = 0;
1516
1517         for (i = 0; i < ge->nr_job_files; i++) {
1518                 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1519                 if (ret < 0) {
1520                         GError *error;
1521
1522                         error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1523                         report_error(error);
1524                         g_error_free(error);
1525                         break;
1526                 } else if (ret)
1527                         break;
1528
1529                 free(ge->job_files[i]);
1530                 ge->job_files[i] = NULL;
1531         }
1532         while (i < ge->nr_job_files) {
1533                 free(ge->job_files[i]);
1534                 ge->job_files[i] = NULL;
1535                 i++;
1536         }
1537
1538         return ret;
1539 }
1540
1541 static void *server_thread(void *arg)
1542 {
1543         is_backend = 1;
1544         gfio_server_running = 1;
1545         fio_start_server(NULL);
1546         gfio_server_running = 0;
1547         return NULL;
1548 }
1549
1550 static void gfio_start_server(void)
1551 {
1552         struct gui *ui = &main_ui;
1553
1554         if (!gfio_server_running) {
1555                 gfio_server_running = 1;
1556                 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1557                 pthread_detach(ui->server_t);
1558         }
1559 }
1560
1561 static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1562                 gpointer data)
1563 {
1564         struct gui_entry *ge = data;
1565         struct gfio_client *gc = ge->client;
1566
1567         gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1568         fio_start_client(gc->client);
1569 }
1570
1571 static void file_open(GtkWidget *w, gpointer data);
1572
1573 static void connect_clicked(GtkWidget *widget, gpointer data)
1574 {
1575         struct gui_entry *ge = data;
1576         struct gfio_client *gc = ge->client;
1577
1578         if (!ge->connected) {
1579                 int ret;
1580
1581                 if (!ge->nr_job_files)
1582                         file_open(widget, data);
1583                 if (!ge->nr_job_files)
1584                         return;
1585
1586                 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1587                 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1588                 ret = fio_client_connect(gc->client);
1589                 if (!ret) {
1590                         if (!ge->ui->handler_running)
1591                                 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1592                         gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1593                         gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1594                 } else {
1595                         GError *error;
1596
1597                         error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1598                         report_error(error);
1599                         g_error_free(error);
1600                 }
1601         } else {
1602                 fio_client_terminate(gc->client);
1603                 gfio_set_connected(ge, 0);
1604                 clear_ge_ui_info(ge);
1605         }
1606 }
1607
1608 static void send_clicked(GtkWidget *widget, gpointer data)
1609 {
1610         struct gui_entry *ge = data;
1611
1612         if (send_job_files(ge)) {
1613                 GError *error;
1614
1615                 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1616                 report_error(error);
1617                 g_error_free(error);
1618
1619                 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1620         }
1621
1622         gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1623         gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1624 }
1625
1626 static GtkWidget *add_button(GtkWidget *buttonbox,
1627                              struct button_spec *buttonspec, gpointer data)
1628 {
1629         GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1630
1631         g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1632         gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1633         gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1634         gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1635
1636         return button;
1637 }
1638
1639 static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1640                         int nbuttons)
1641 {
1642         int i;
1643
1644         for (i = 0; i < nbuttons; i++)
1645                 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1646 }
1647
1648 static void on_info_bar_response(GtkWidget *widget, gint response,
1649                                  gpointer data)
1650 {
1651         struct gui *ui = &main_ui;
1652
1653         if (response == GTK_RESPONSE_OK) {
1654                 gtk_widget_destroy(widget);
1655                 ui->error_info_bar = NULL;
1656         }
1657 }
1658
1659 void report_error(GError *error)
1660 {
1661         struct gui *ui = &main_ui;
1662
1663         if (ui->error_info_bar == NULL) {
1664                 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1665                                                                GTK_RESPONSE_OK,
1666                                                                NULL);
1667                 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1668                 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1669                                               GTK_MESSAGE_ERROR);
1670                 
1671                 ui->error_label = gtk_label_new(error->message);
1672                 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1673                 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1674                 
1675                 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1676                 gtk_widget_show_all(ui->vbox);
1677         } else {
1678                 char buffer[256];
1679                 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1680                 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1681         }
1682 }
1683
1684 struct connection_widgets
1685 {
1686         GtkWidget *hentry;
1687         GtkWidget *combo;
1688         GtkWidget *button;
1689 };
1690
1691 static void hostname_cb(GtkEntry *entry, gpointer data)
1692 {
1693         struct connection_widgets *cw = data;
1694         int uses_net = 0, is_localhost = 0;
1695         const gchar *text;
1696         gchar *ctext;
1697
1698         /*
1699          * Check whether to display the 'auto start backend' box
1700          * or not. Show it if we are a localhost and using network,
1701          * or using a socket.
1702          */
1703         ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1704         if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1705                 uses_net = 1;
1706         g_free(ctext);
1707
1708         if (uses_net) {
1709                 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1710                 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1711                     !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1712                     !strcmp(text, "ip6-loopback"))
1713                         is_localhost = 1;
1714         }
1715
1716         if (!uses_net || is_localhost) {
1717                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1718                 gtk_widget_set_sensitive(cw->button, 1);
1719         } else {
1720                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1721                 gtk_widget_set_sensitive(cw->button, 0);
1722         }
1723 }
1724
1725 static int get_connection_details(char **host, int *port, int *type,
1726                                   int *server_start)
1727 {
1728         GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1729         struct connection_widgets cw;
1730         char *typeentry;
1731
1732         dialog = gtk_dialog_new_with_buttons("Connection details",
1733                         GTK_WINDOW(main_ui.window),
1734                         GTK_DIALOG_DESTROY_WITH_PARENT,
1735                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1736                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1737
1738         frame = gtk_frame_new("Hostname / socket name");
1739         /* gtk_dialog_get_content_area() is 2.14 and newer */
1740         vbox = GTK_DIALOG(dialog)->vbox;
1741         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1742
1743         box = gtk_vbox_new(FALSE, 6);
1744         gtk_container_add(GTK_CONTAINER(frame), box);
1745
1746         hbox = gtk_hbox_new(TRUE, 10);
1747         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1748         cw.hentry = gtk_entry_new();
1749         gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1750         gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1751
1752         frame = gtk_frame_new("Port");
1753         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1754         box = gtk_vbox_new(FALSE, 10);
1755         gtk_container_add(GTK_CONTAINER(frame), box);
1756
1757         hbox = gtk_hbox_new(TRUE, 4);
1758         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1759         pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1760
1761         frame = gtk_frame_new("Type");
1762         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1763         box = gtk_vbox_new(FALSE, 10);
1764         gtk_container_add(GTK_CONTAINER(frame), box);
1765
1766         hbox = gtk_hbox_new(TRUE, 4);
1767         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1768
1769         cw.combo = gtk_combo_box_new_text();
1770         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1771         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1772         gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1773         gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1774
1775         gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1776
1777         frame = gtk_frame_new("Options");
1778         gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1779         box = gtk_vbox_new(FALSE, 10);
1780         gtk_container_add(GTK_CONTAINER(frame), box);
1781
1782         hbox = gtk_hbox_new(TRUE, 4);
1783         gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1784
1785         cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1786         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1787         gtk_widget_set_tooltip_text(cw.button, "When running fio locally, it is necessary to have the backend running on the same system. If this is checked, gfio will start the backend automatically for you if it isn't already running.");
1788         gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1789
1790         /*
1791          * Connect edit signal, so we can show/not-show the auto start button
1792          */
1793         g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1794         g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1795
1796         gtk_widget_show_all(dialog);
1797
1798         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1799                 gtk_widget_destroy(dialog);
1800                 return 1;
1801         }
1802
1803         *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1804         *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1805
1806         typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1807         if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1808                 *type = Fio_client_ipv4;
1809         else if (!strncmp(typeentry, "IPv6", 4))
1810                 *type = Fio_client_ipv6;
1811         else
1812                 *type = Fio_client_socket;
1813         g_free(typeentry);
1814
1815         *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1816
1817         gtk_widget_destroy(dialog);
1818         return 0;
1819 }
1820
1821 static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1822 {
1823         struct gfio_client *gc;
1824
1825         gc = malloc(sizeof(*gc));
1826         memset(gc, 0, sizeof(*gc));
1827         gc->ge = ge;
1828         gc->client = fio_get_client(client);
1829
1830         ge->client = gc;
1831
1832         client->client_data = gc;
1833 }
1834
1835 static GtkWidget *new_client_page(struct gui_entry *ge);
1836
1837 static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1838 {
1839         struct gui_entry *ge;
1840
1841         ge = malloc(sizeof(*ge));
1842         memset(ge, 0, sizeof(*ge));
1843         INIT_FLIST_HEAD(&ge->list);
1844         flist_add_tail(&ge->list, &ui->list);
1845         ge->ui = ui;
1846         return ge;
1847 }
1848
1849 /*
1850  * FIXME: need more handling here
1851  */
1852 static void ge_destroy(GtkWidget *w, gpointer data)
1853 {
1854         struct gui_entry *ge = data;
1855         struct gfio_client *gc = ge->client;
1856
1857         if (gc->client)
1858                 fio_put_client(gc->client);
1859
1860         flist_del(&ge->list);
1861         free(ge);
1862 }
1863
1864 static struct gui_entry *get_new_ge_with_tab(const char *name)
1865 {
1866         struct gui_entry *ge;
1867
1868         ge = alloc_new_gui_entry(&main_ui);
1869
1870         ge->vbox = new_client_page(ge);
1871         g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1872
1873         ge->page_label = gtk_label_new(name);
1874         ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1875
1876         gtk_widget_show_all(main_ui.window);
1877         return ge;
1878 }
1879
1880 static void file_new(GtkWidget *w, gpointer data)
1881 {
1882         get_new_ge_with_tab("Untitled");
1883 }
1884
1885 /*
1886  * Return the 'ge' corresponding to the tab. If the active tab is the
1887  * main tab, open a new tab.
1888  */
1889 static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1890 {
1891         struct flist_head *entry;
1892         struct gui_entry *ge;
1893
1894         if (!cur_page)
1895                 return get_new_ge_with_tab("Untitled");
1896
1897         flist_for_each(entry, &main_ui.list) {
1898                 ge = flist_entry(entry, struct gui_entry, list);
1899                 if (ge->page_num == cur_page)
1900                         return ge;
1901         }
1902
1903         return NULL;
1904 }
1905
1906 static void file_open(GtkWidget *w, gpointer data)
1907 {
1908         struct gui *ui = data;
1909         GtkWidget *dialog;
1910         GSList *filenames, *fn_glist;
1911         GtkFileFilter *filter;
1912         char *host;
1913         int port, type, server_start;
1914         struct gui_entry *ge;
1915         gint cur_page;
1916
1917         /*
1918          * Creates new tab if current tab is the main window, or the
1919          * current tab already has a client.
1920          */
1921         cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1922         ge = get_ge_from_page(cur_page);
1923         if (ge->client)
1924                 ge = get_new_ge_with_tab("Untitled");
1925
1926         gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1927
1928         dialog = gtk_file_chooser_dialog_new("Open File",
1929                 GTK_WINDOW(ui->window),
1930                 GTK_FILE_CHOOSER_ACTION_OPEN,
1931                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1932                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1933                 NULL);
1934         gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1935
1936         filter = gtk_file_filter_new();
1937         gtk_file_filter_add_pattern(filter, "*.fio");
1938         gtk_file_filter_add_pattern(filter, "*.job");
1939         gtk_file_filter_add_pattern(filter, "*.ini");
1940         gtk_file_filter_add_mime_type(filter, "text/fio");
1941         gtk_file_filter_set_name(filter, "Fio job file");
1942         gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1943
1944         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1945                 gtk_widget_destroy(dialog);
1946                 return;
1947         }
1948
1949         fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1950
1951         gtk_widget_destroy(dialog);
1952
1953         if (get_connection_details(&host, &port, &type, &server_start))
1954                 goto err;
1955
1956         filenames = fn_glist;
1957         while (filenames != NULL) {
1958                 struct fio_client *client;
1959
1960                 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1961                 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1962                 ge->nr_job_files++;
1963
1964                 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1965                 if (!client) {
1966                         GError *error;
1967
1968                         error = g_error_new(g_quark_from_string("fio"), 1,
1969                                         "Failed to add client %s", host);
1970                         report_error(error);
1971                         g_error_free(error);
1972                 }
1973                 gfio_client_added(ge, client);
1974                         
1975                 g_free(filenames->data);
1976                 filenames = g_slist_next(filenames);
1977         }
1978         free(host);
1979
1980         if (server_start)
1981                 gfio_start_server();
1982 err:
1983         g_slist_free(fn_glist);
1984 }
1985
1986 static void file_save(GtkWidget *w, gpointer data)
1987 {
1988         struct gui *ui = data;
1989         GtkWidget *dialog;
1990
1991         dialog = gtk_file_chooser_dialog_new("Save File",
1992                 GTK_WINDOW(ui->window),
1993                 GTK_FILE_CHOOSER_ACTION_SAVE,
1994                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1995                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1996                 NULL);
1997
1998         gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1999         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2000
2001         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2002                 char *filename;
2003
2004                 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2005                 // save_job_file(filename);
2006                 g_free(filename);
2007         }
2008         gtk_widget_destroy(dialog);
2009 }
2010
2011 static void view_log_destroy(GtkWidget *w, gpointer data)
2012 {
2013         struct gui *ui = (struct gui *) data;
2014
2015         gtk_widget_ref(ui->log_tree);
2016         gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2017         gtk_widget_destroy(w);
2018         ui->log_view = NULL;
2019 }
2020
2021 static void view_log(GtkWidget *w, gpointer data)
2022 {
2023         GtkWidget *win, *scroll, *vbox, *box;
2024         struct gui *ui = (struct gui *) data;
2025
2026         if (ui->log_view)
2027                 return;
2028
2029         ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2030         gtk_window_set_title(GTK_WINDOW(win), "Log");
2031         gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
2032
2033         scroll = gtk_scrolled_window_new(NULL, NULL);
2034
2035         gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2036
2037         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2038
2039         box = gtk_hbox_new(TRUE, 0);
2040         gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2041         g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2042         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2043
2044         vbox = gtk_vbox_new(TRUE, 5);
2045         gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2046
2047         gtk_container_add(GTK_CONTAINER(win), vbox);
2048         gtk_widget_show_all(win);
2049 }
2050
2051 static void __update_graph_limits(struct gfio_graphs *g)
2052 {
2053         line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2054         line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2055 }
2056
2057 static void update_graph_limits(void)
2058 {
2059         struct flist_head *entry;
2060         struct gui_entry *ge;
2061
2062         __update_graph_limits(&main_ui.graphs);
2063
2064         flist_for_each(entry, &main_ui.list) {
2065                 ge = flist_entry(entry, struct gui_entry, list);
2066                 __update_graph_limits(&ge->graphs);
2067         }
2068 }
2069
2070 static void preferences(GtkWidget *w, gpointer data)
2071 {
2072         GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2073         GtkWidget *hbox, *spin, *entry, *spin_int;
2074         int i;
2075
2076         dialog = gtk_dialog_new_with_buttons("Preferences",
2077                 GTK_WINDOW(main_ui.window),
2078                 GTK_DIALOG_DESTROY_WITH_PARENT,
2079                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2080                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2081                 NULL);
2082
2083         frame = gtk_frame_new("Graphing");
2084         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2085         vbox = gtk_vbox_new(FALSE, 6);
2086         gtk_container_add(GTK_CONTAINER(frame), vbox);
2087
2088         hbox = gtk_hbox_new(FALSE, 5);
2089         gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2090         entry = gtk_label_new("Font face to use for graph labels");
2091         gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2092
2093         font = gtk_font_button_new();
2094         gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
2095
2096         box = gtk_vbox_new(FALSE, 6);
2097         gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2098
2099         hbox = gtk_hbox_new(FALSE, 5);
2100         gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2101         entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2102         gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2103
2104         spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
2105
2106         box = gtk_vbox_new(FALSE, 6);
2107         gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2108
2109         hbox = gtk_hbox_new(FALSE, 5);
2110         gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2111         entry = gtk_label_new("Client ETA request interval (msec)");
2112         gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2113
2114         spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
2115         frame = gtk_frame_new("Debug logging");
2116         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2117         vbox = gtk_vbox_new(FALSE, 6);
2118         gtk_container_add(GTK_CONTAINER(frame), vbox);
2119
2120         box = gtk_hbox_new(FALSE, 6);
2121         gtk_container_add(GTK_CONTAINER(vbox), box);
2122
2123         buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2124
2125         for (i = 0; i < FD_DEBUG_MAX; i++) {
2126                 if (i == 7) {
2127                         box = gtk_hbox_new(FALSE, 6);
2128                         gtk_container_add(GTK_CONTAINER(vbox), box);
2129                 }
2130
2131
2132                 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2133                 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2134                 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2135         }
2136
2137         gtk_widget_show_all(dialog);
2138
2139         if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2140                 gtk_widget_destroy(dialog);
2141                 return;
2142         }
2143
2144         for (i = 0; i < FD_DEBUG_MAX; i++) {
2145                 int set;
2146
2147                 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2148                 if (set)
2149                         fio_debug |= (1UL << i);
2150         }
2151
2152         gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2153         gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2154         update_graph_limits();
2155         gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
2156
2157         gtk_widget_destroy(dialog);
2158 }
2159
2160 static void about_dialog(GtkWidget *w, gpointer data)
2161 {
2162         const char *authors[] = {
2163                 "Jens Axboe <axboe@kernel.dk>",
2164                 "Stephen Carmeron <stephenmcameron@gmail.com>",
2165                 NULL
2166         };
2167         const char *license[] = {
2168                 "Fio is free software; you can redistribute it and/or modify "
2169                 "it under the terms of the GNU General Public License as published by "
2170                 "the Free Software Foundation; either version 2 of the License, or "
2171                 "(at your option) any later version.\n",
2172                 "Fio is distributed in the hope that it will be useful, "
2173                 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2174                 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
2175                 "GNU General Public License for more details.\n",
2176                 "You should have received a copy of the GNU General Public License "
2177                 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2178                 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA\n"
2179         };
2180         char *license_trans;
2181
2182         license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2183                                      license[2], "\n", NULL);
2184
2185         gtk_show_about_dialog(NULL,
2186                 "program-name", "gfio",
2187                 "comments", "Gtk2 UI for fio",
2188                 "license", license_trans,
2189                 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2190                 "authors", authors,
2191                 "version", fio_version_string,
2192                 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2193                 "logo-icon-name", "fio",
2194                 /* Must be last: */
2195                 "wrap-license", TRUE,
2196                 NULL);
2197
2198         g_free(license_trans);
2199 }
2200
2201 static GtkActionEntry menu_items[] = {
2202         { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2203         { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2204         { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2205         { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2206         { "OpenFile", GTK_STOCK_OPEN, NULL,   "<Control>O", NULL, G_CALLBACK(file_open) },
2207         { "SaveFile", GTK_STOCK_SAVE, NULL,   "<Control>S", NULL, G_CALLBACK(file_save) },
2208         { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2209         { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2210         { "Quit", GTK_STOCK_QUIT, NULL,   "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2211         { "About", GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
2212 };
2213 static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2214
2215 static const gchar *ui_string = " \
2216         <ui> \
2217                 <menubar name=\"MainMenu\"> \
2218                         <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2219                                 <menuitem name=\"New\" action=\"NewFile\" /> \
2220                                 <separator name=\"Separator1\"/> \
2221                                 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2222                                 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2223                                 <separator name=\"Separator2\"/> \
2224                                 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2225                                 <separator name=\"Separator3\"/> \
2226                                 <menuitem name=\"Quit\" action=\"Quit\" /> \
2227                         </menu> \
2228                         <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2229                                 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2230                         </menu>\
2231                         <menu name=\"Help\" action=\"HelpMenuAction\"> \
2232                                 <menuitem name=\"About\" action=\"About\" /> \
2233                         </menu> \
2234                 </menubar> \
2235         </ui> \
2236 ";
2237
2238 static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2239                                    struct gui *ui)
2240 {
2241         GtkActionGroup *action_group = gtk_action_group_new("Menu");
2242         GError *error = 0;
2243
2244         action_group = gtk_action_group_new("Menu");
2245         gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2246
2247         gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2248         gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2249
2250         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2251         return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2252 }
2253
2254 void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2255                    GtkWidget *vbox, GtkUIManager *ui_manager)
2256 {
2257         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2258 }
2259
2260 static GtkWidget *new_client_page(struct gui_entry *ge)
2261 {
2262         GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2263         GdkColor white;
2264
2265         main_vbox = gtk_vbox_new(FALSE, 3);
2266
2267         ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2268         ge->topvbox = gtk_vbox_new(FALSE, 3);
2269         gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2270         gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2271
2272         probe = gtk_frame_new("Job");
2273         gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2274         probe_frame = gtk_vbox_new(FALSE, 3);
2275         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2276
2277         probe_box = gtk_hbox_new(FALSE, 3);
2278         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2279         ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2280         ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2281         ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2282         ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2283
2284         probe_box = gtk_hbox_new(FALSE, 3);
2285         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2286
2287         ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2288         ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2289         ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2290         ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2291         ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2292         ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2293
2294         probe_box = gtk_hbox_new(FALSE, 3);
2295         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2296         ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2297         ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2298         ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2299         ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2300
2301         /*
2302          * Only add this if we have a commit rate
2303          */
2304 #if 0
2305         probe_box = gtk_hbox_new(FALSE, 3);
2306         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2307
2308         ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2309         ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2310
2311         ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2312         ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2313 #endif
2314
2315         /*
2316          * Set up a drawing area and IOPS and bandwidth graphs
2317          */
2318         gdk_color_parse("white", &white);
2319         ge->graphs.drawing_area = gtk_drawing_area_new();
2320         gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2321                 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2322         gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2323         g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2324                                 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2325         g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2326                                 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2327         ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2328         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2329                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2330         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2331                                         ge->graphs.drawing_area);
2332         gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2333                         TRUE, TRUE, 0);
2334
2335         setup_graphs(&ge->graphs);
2336
2337         /*
2338          * Set up alignments for widgets at the bottom of ui, 
2339          * align bottom left, expand horizontally but not vertically
2340          */
2341         ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2342         ge->buttonbox = gtk_hbox_new(FALSE, 0);
2343         gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2344         gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2345                                         FALSE, FALSE, 0);
2346
2347         add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2348
2349         /*
2350          * Set up thread status progress bar
2351          */
2352         ge->thread_status_pb = gtk_progress_bar_new();
2353         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2354         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2355         gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2356
2357
2358         return main_vbox;
2359 }
2360
2361 static GtkWidget *new_main_page(struct gui *ui)
2362 {
2363         GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2364         GdkColor white;
2365
2366         main_vbox = gtk_vbox_new(FALSE, 3);
2367
2368         /*
2369          * Set up alignments for widgets at the top of ui,
2370          * align top left, expand horizontally but not vertically
2371          */
2372         ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2373         ui->topvbox = gtk_vbox_new(FALSE, 0);
2374         gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2375         gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2376
2377         probe = gtk_frame_new("Run statistics");
2378         gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2379         probe_frame = gtk_vbox_new(FALSE, 3);
2380         gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2381
2382         probe_box = gtk_hbox_new(FALSE, 3);
2383         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2384         ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
2385         ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2386         ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2387         ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2388         ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2389
2390         /*
2391          * Only add this if we have a commit rate
2392          */
2393 #if 0
2394         probe_box = gtk_hbox_new(FALSE, 3);
2395         gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2396
2397         ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2398         ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2399
2400         ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2401         ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2402 #endif
2403
2404         /*
2405          * Set up a drawing area and IOPS and bandwidth graphs
2406          */
2407         gdk_color_parse("white", &white);
2408         ui->graphs.drawing_area = gtk_drawing_area_new();
2409         gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2410                 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
2411         gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2412         g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2413                         G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2414         g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2415                         G_CALLBACK(on_config_drawing_area), &ui->graphs);
2416         ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2417         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2418                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2419         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2420                                         ui->graphs.drawing_area);
2421         gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2422                         TRUE, TRUE, 0);
2423
2424         setup_graphs(&ui->graphs);
2425
2426         /*
2427          * Set up alignments for widgets at the bottom of ui, 
2428          * align bottom left, expand horizontally but not vertically
2429          */
2430         ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2431         ui->buttonbox = gtk_hbox_new(FALSE, 0);
2432         gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2433         gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2434                                         FALSE, FALSE, 0);
2435
2436         /*
2437          * Set up thread status progress bar
2438          */
2439         ui->thread_status_pb = gtk_progress_bar_new();
2440         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2441         gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2442         gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2443
2444         return main_vbox;
2445 }
2446
2447 static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2448                                      guint page, gpointer data)
2449
2450 {
2451         return TRUE;
2452 }
2453
2454 static void init_ui(int *argc, char **argv[], struct gui *ui)
2455 {
2456         GtkSettings *settings;
2457         GtkUIManager *uimanager;
2458         GtkWidget *menu, *vbox;
2459
2460         /* Magical g*thread incantation, you just need this thread stuff.
2461          * Without it, the update that happens in gfio_update_thread_status
2462          * doesn't really happen in a timely fashion, you need expose events
2463          */
2464         if (!g_thread_supported())
2465                 g_thread_init(NULL);
2466         gdk_threads_init();
2467
2468         gtk_init(argc, argv);
2469         settings = gtk_settings_get_default();
2470         gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2471         g_type_init();
2472         
2473         ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2474         gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2475         gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2476
2477         g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2478         g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2479
2480         ui->vbox = gtk_vbox_new(FALSE, 0);
2481         gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2482
2483         uimanager = gtk_ui_manager_new();
2484         menu = get_menubar_menu(ui->window, uimanager, ui);
2485         gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2486
2487         ui->notebook = gtk_notebook_new();
2488         g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2489         gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2490         gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2491         gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2492
2493         vbox = new_main_page(ui);
2494
2495         gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2496
2497         gfio_ui_setup_log(ui);
2498
2499         gtk_widget_show_all(ui->window);
2500 }
2501
2502 int main(int argc, char *argv[], char *envp[])
2503 {
2504         if (initialize_fio(envp))
2505                 return 1;
2506         if (fio_init_options())
2507                 return 1;
2508
2509         memset(&main_ui, 0, sizeof(main_ui));
2510         INIT_FLIST_HEAD(&main_ui.list);
2511
2512         init_ui(&argc, &argv, &main_ui);
2513
2514         gdk_threads_enter();
2515         gtk_main();
2516         gdk_threads_leave();
2517         return 0;
2518 }