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