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