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