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