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