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