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