gfio: make results notebook scrollable too
[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#include <string.h>
27
28#include <glib.h>
29#include <cairo.h>
30#include <gtk/gtk.h>
31
32#include "fio.h"
33#include "graph.h"
34
35static int gfio_server_running;
36static const char *gfio_graph_font;
37
38static void view_log(GtkWidget *w, gpointer data);
39
40#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
41
42typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
43
44static void connect_clicked(GtkWidget *widget, gpointer data);
45static void start_job_clicked(GtkWidget *widget, gpointer data);
46static void send_clicked(GtkWidget *widget, gpointer data);
47
48static struct button_spec {
49 const char *buttontext;
50 clickfunction f;
51 const char *tooltiptext;
52 const int start_insensitive;
53} buttonspeclist[] = {
54#define CONNECT_BUTTON 0
55#define SEND_BUTTON 1
56#define START_JOB_BUTTON 2
57 { "Connect", connect_clicked, "Connect to host", 0 },
58 { "Send", send_clicked, "Send job description to host", 1 },
59 { "Start Job", start_job_clicked,
60 "Start the current job on the server", 1 },
61};
62
63struct probe_widget {
64 GtkWidget *hostname;
65 GtkWidget *os;
66 GtkWidget *arch;
67 GtkWidget *fio_ver;
68};
69
70struct eta_widget {
71 GtkWidget *name;
72 GtkWidget *iotype;
73 GtkWidget *ioengine;
74 GtkWidget *iodepth;
75 GtkWidget *jobs;
76 GtkWidget *files;
77 GtkWidget *read_bw;
78 GtkWidget *read_iops;
79 GtkWidget *cr_bw;
80 GtkWidget *cr_iops;
81 GtkWidget *write_bw;
82 GtkWidget *write_iops;
83 GtkWidget *cw_bw;
84 GtkWidget *cw_iops;
85};
86
87struct gfio_graphs {
88#define DRAWING_AREA_XDIM 1000
89#define DRAWING_AREA_YDIM 400
90 GtkWidget *drawing_area;
91 int drawing_area_xdim;
92 int drawing_area_ydim;
93
94 struct graph *iops_graph;
95 struct graph *bandwidth_graph;
96};
97
98/*
99 * Main window widgets and data
100 */
101struct gui {
102 GtkWidget *window;
103 GtkWidget *vbox;
104 GtkWidget *topvbox;
105 GtkWidget *topalign;
106 GtkWidget *bottomalign;
107 GtkWidget *thread_status_pb;
108 GtkWidget *buttonbox;
109 GtkWidget *scrolled_window;
110 GtkWidget *notebook;
111 GtkWidget *error_info_bar;
112 GtkWidget *error_label;
113 GtkListStore *log_model;
114 GtkWidget *log_tree;
115 GtkWidget *log_view;
116 struct gfio_graphs graphs;
117 struct probe_widget probe;
118 struct eta_widget eta;
119 pthread_t server_t;
120
121 pthread_t t;
122 int handler_running;
123
124 struct flist_head list;
125} main_ui;
126
127/*
128 * Notebook entry
129 */
130struct gui_entry {
131 struct flist_head list;
132 struct gui *ui;
133
134 GtkWidget *vbox;
135 GtkWidget *topvbox;
136 GtkWidget *topalign;
137 GtkWidget *bottomalign;
138 GtkWidget *thread_status_pb;
139 GtkWidget *buttonbox;
140 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
141 GtkWidget *scrolled_window;
142 GtkWidget *notebook;
143 GtkWidget *error_info_bar;
144 GtkWidget *error_label;
145 GtkWidget *results_notebook;
146 GtkWidget *results_window;
147 GtkListStore *log_model;
148 GtkWidget *log_tree;
149 GtkWidget *log_view;
150 struct gfio_graphs graphs;
151 struct probe_widget probe;
152 struct eta_widget eta;
153 GtkWidget *page_label;
154 gint page_num;
155 int connected;
156
157 struct gfio_client *client;
158 int nr_job_files;
159 char **job_files;
160};
161
162struct gfio_client {
163 struct gui_entry *ge;
164 struct fio_client *client;
165 GtkWidget *results_widget;
166 GtkWidget *disk_util_frame;
167 GtkWidget *err_entry;
168 unsigned int job_added;
169 struct thread_options o;
170};
171
172static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
173static void gfio_update_thread_status_all(char *status_message, double perc);
174void report_error(GError *error);
175
176static struct graph *setup_iops_graph(void)
177{
178 struct graph *g;
179
180 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
181 graph_title(g, "IOPS");
182 graph_x_title(g, "Time (secs)");
183 graph_y_title(g, "IOs / sec");
184 graph_add_label(g, "Read IOPS");
185 graph_add_label(g, "Write IOPS");
186 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
187 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
188 line_graph_set_data_count_limit(g, 100);
189 return g;
190}
191
192static struct graph *setup_bandwidth_graph(void)
193{
194 struct graph *g;
195
196 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
197 graph_title(g, "Bandwidth");
198 graph_x_title(g, "Time (secs)");
199 graph_y_title(g, "Kbytes / sec");
200 graph_add_label(g, "Read Bandwidth");
201 graph_add_label(g, "Write Bandwidth");
202 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
203 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
204 line_graph_set_data_count_limit(g, 100);
205 return g;
206}
207
208static void setup_graphs(struct gfio_graphs *g)
209{
210 g->iops_graph = setup_iops_graph();
211 g->bandwidth_graph = setup_bandwidth_graph();
212}
213
214static void clear_ge_ui_info(struct gui_entry *ge)
215{
216 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
217 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
218 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
219 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
220 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
221 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
225 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
226 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
227 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
228 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
229 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
230}
231
232static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
233{
234 GtkWidget *entry, *frame;
235
236 frame = gtk_frame_new(label);
237 entry = gtk_entry_new();
238 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
239 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
240 gtk_container_add(GTK_CONTAINER(frame), entry);
241
242 return entry;
243}
244
245static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
246{
247 GtkWidget *label_widget;
248 GtkWidget *frame;
249
250 frame = gtk_frame_new(label);
251 label_widget = gtk_label_new(NULL);
252 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
253 gtk_container_add(GTK_CONTAINER(frame), label_widget);
254
255 return label_widget;
256}
257
258static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
259{
260 GtkWidget *button, *box;
261
262 box = gtk_hbox_new(FALSE, 3);
263 gtk_container_add(GTK_CONTAINER(hbox), box);
264
265 button = gtk_spin_button_new_with_range(min, max, 1.0);
266 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
267
268 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
269 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
270
271 return button;
272}
273
274static void gfio_set_connected(struct gui_entry *ge, int connected)
275{
276 if (connected) {
277 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
278 ge->connected = 1;
279 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
280 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
281 } else {
282 ge->connected = 0;
283 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
284 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
285 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
286 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
287 }
288}
289
290static void label_set_int_value(GtkWidget *entry, unsigned int val)
291{
292 char tmp[80];
293
294 sprintf(tmp, "%u", val);
295 gtk_label_set_text(GTK_LABEL(entry), tmp);
296}
297
298static void entry_set_int_value(GtkWidget *entry, unsigned int val)
299{
300 char tmp[80];
301
302 sprintf(tmp, "%u", val);
303 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
304}
305
306#define ALIGN_LEFT 1
307#define ALIGN_RIGHT 2
308#define INVISIBLE 4
309#define UNSORTABLE 8
310
311GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
312{
313 GtkCellRenderer *renderer;
314 GtkTreeViewColumn *col;
315 double xalign = 0.0; /* left as default */
316 PangoAlignment align;
317 gboolean visible;
318
319 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
320 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
321 PANGO_ALIGN_CENTER;
322 visible = !(flags & INVISIBLE);
323
324 renderer = gtk_cell_renderer_text_new();
325 col = gtk_tree_view_column_new();
326
327 gtk_tree_view_column_set_title(col, title);
328 if (!(flags & UNSORTABLE))
329 gtk_tree_view_column_set_sort_column_id(col, index);
330 gtk_tree_view_column_set_resizable(col, TRUE);
331 gtk_tree_view_column_pack_start(col, renderer, TRUE);
332 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
333 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
334 switch (align) {
335 case PANGO_ALIGN_LEFT:
336 xalign = 0.0;
337 break;
338 case PANGO_ALIGN_CENTER:
339 xalign = 0.5;
340 break;
341 case PANGO_ALIGN_RIGHT:
342 xalign = 1.0;
343 break;
344 }
345 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
346 gtk_tree_view_column_set_visible(col, visible);
347 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
348 return col;
349}
350
351static void gfio_ui_setup_log(struct gui *ui)
352{
353 GtkTreeSelection *selection;
354 GtkListStore *model;
355 GtkWidget *tree_view;
356
357 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
358
359 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
360 gtk_widget_set_can_focus(tree_view, FALSE);
361
362 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
363 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
364 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
365 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
366
367 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
368 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
369 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
370 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
371
372 ui->log_model = model;
373 ui->log_tree = tree_view;
374}
375
376static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
377 fio_fp64_t *plist,
378 unsigned int len,
379 const char *base,
380 unsigned int scale)
381{
382 GType types[FIO_IO_U_LIST_MAX_LEN];
383 GtkWidget *tree_view;
384 GtkTreeSelection *selection;
385 GtkListStore *model;
386 GtkTreeIter iter;
387 int i;
388
389 for (i = 0; i < len; i++)
390 types[i] = G_TYPE_INT;
391
392 model = gtk_list_store_newv(len, types);
393
394 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
395 gtk_widget_set_can_focus(tree_view, FALSE);
396
397 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
398 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
399
400 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
401 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
402
403 for (i = 0; i < len; i++) {
404 char fbuf[8];
405
406 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
407 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
408 }
409
410 gtk_list_store_append(model, &iter);
411
412 for (i = 0; i < len; i++) {
413 if (scale)
414 ovals[i] = (ovals[i] + 999) / 1000;
415 gtk_list_store_set(model, &iter, i, ovals[i], -1);
416 }
417
418 return tree_view;
419}
420
421static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
422 int ddir)
423{
424 unsigned int *io_u_plat = ts->io_u_plat[ddir];
425 unsigned long nr = ts->clat_stat[ddir].samples;
426 fio_fp64_t *plist = ts->percentile_list;
427 unsigned int *ovals, len, minv, maxv, scale_down;
428 const char *base;
429 GtkWidget *tree_view, *frame, *hbox;
430 char tmp[64];
431
432 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
433 if (!len)
434 goto out;
435
436 /*
437 * We default to usecs, but if the value range is such that we
438 * should scale down to msecs, do that.
439 */
440 if (minv > 2000 && maxv > 99999) {
441 scale_down = 1;
442 base = "msec";
443 } else {
444 scale_down = 0;
445 base = "usec";
446 }
447
448 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
449
450 sprintf(tmp, "Completion percentiles (%s)", base);
451 frame = gtk_frame_new(tmp);
452 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
453
454 hbox = gtk_hbox_new(FALSE, 3);
455 gtk_container_add(GTK_CONTAINER(frame), hbox);
456
457 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
458out:
459 if (ovals)
460 free(ovals);
461}
462
463static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
464 unsigned long max, double mean, double dev)
465{
466 const char *base = "(usec)";
467 GtkWidget *hbox, *label, *frame;
468 char *minp, *maxp;
469 char tmp[64];
470
471 if (!usec_to_msec(&min, &max, &mean, &dev))
472 base = "(msec)";
473
474 minp = num2str(min, 6, 1, 0);
475 maxp = num2str(max, 6, 1, 0);
476
477 sprintf(tmp, "%s %s", name, base);
478 frame = gtk_frame_new(tmp);
479 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
480
481 hbox = gtk_hbox_new(FALSE, 3);
482 gtk_container_add(GTK_CONTAINER(frame), hbox);
483
484 label = new_info_label_in_frame(hbox, "Minimum");
485 gtk_label_set_text(GTK_LABEL(label), minp);
486 label = new_info_label_in_frame(hbox, "Maximum");
487 gtk_label_set_text(GTK_LABEL(label), maxp);
488 label = new_info_label_in_frame(hbox, "Average");
489 sprintf(tmp, "%5.02f", mean);
490 gtk_label_set_text(GTK_LABEL(label), tmp);
491 label = new_info_label_in_frame(hbox, "Standard deviation");
492 sprintf(tmp, "%5.02f", dev);
493 gtk_label_set_text(GTK_LABEL(label), tmp);
494
495 free(minp);
496 free(maxp);
497
498}
499
500#define GFIO_CLAT 1
501#define GFIO_SLAT 2
502#define GFIO_LAT 4
503
504static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
505 struct thread_stat *ts, int ddir)
506{
507 const char *ddir_label[2] = { "Read", "Write" };
508 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
509 unsigned long min[3], max[3], runt;
510 unsigned long long bw, iops;
511 unsigned int flags = 0;
512 double mean[3], dev[3];
513 char *io_p, *bw_p, *iops_p;
514 int i2p;
515
516 if (!ts->runtime[ddir])
517 return;
518
519 i2p = is_power_of_2(rs->kb_base);
520 runt = ts->runtime[ddir];
521
522 bw = (1000 * ts->io_bytes[ddir]) / runt;
523 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
524 bw_p = num2str(bw, 6, 1, i2p);
525
526 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
527 iops_p = num2str(iops, 6, 1, 0);
528
529 box = gtk_hbox_new(FALSE, 3);
530 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
531
532 frame = gtk_frame_new(ddir_label[ddir]);
533 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
534
535 main_vbox = gtk_vbox_new(FALSE, 3);
536 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
537
538 box = gtk_hbox_new(FALSE, 3);
539 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
540
541 label = new_info_label_in_frame(box, "IO");
542 gtk_label_set_text(GTK_LABEL(label), io_p);
543 label = new_info_label_in_frame(box, "Bandwidth");
544 gtk_label_set_text(GTK_LABEL(label), bw_p);
545 label = new_info_label_in_frame(box, "IOPS");
546 gtk_label_set_text(GTK_LABEL(label), iops_p);
547 label = new_info_label_in_frame(box, "Runtime (msec)");
548 label_set_int_value(label, ts->runtime[ddir]);
549
550 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
551 double p_of_agg = 100.0;
552 const char *bw_str = "KB";
553 char tmp[32];
554
555 if (rs->agg[ddir]) {
556 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
557 if (p_of_agg > 100.0)
558 p_of_agg = 100.0;
559 }
560
561 if (mean[0] > 999999.9) {
562 min[0] /= 1000.0;
563 max[0] /= 1000.0;
564 mean[0] /= 1000.0;
565 dev[0] /= 1000.0;
566 bw_str = "MB";
567 }
568
569 sprintf(tmp, "Bandwidth (%s)", bw_str);
570 frame = gtk_frame_new(tmp);
571 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
572
573 box = gtk_hbox_new(FALSE, 3);
574 gtk_container_add(GTK_CONTAINER(frame), box);
575
576 label = new_info_label_in_frame(box, "Minimum");
577 label_set_int_value(label, min[0]);
578 label = new_info_label_in_frame(box, "Maximum");
579 label_set_int_value(label, max[0]);
580 label = new_info_label_in_frame(box, "Percentage of jobs");
581 sprintf(tmp, "%3.2f%%", p_of_agg);
582 gtk_label_set_text(GTK_LABEL(label), tmp);
583 label = new_info_label_in_frame(box, "Average");
584 sprintf(tmp, "%5.02f", mean[0]);
585 gtk_label_set_text(GTK_LABEL(label), tmp);
586 label = new_info_label_in_frame(box, "Standard deviation");
587 sprintf(tmp, "%5.02f", dev[0]);
588 gtk_label_set_text(GTK_LABEL(label), tmp);
589 }
590
591 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
592 flags |= GFIO_SLAT;
593 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
594 flags |= GFIO_CLAT;
595 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
596 flags |= GFIO_LAT;
597
598 if (flags) {
599 frame = gtk_frame_new("Latency");
600 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
601
602 vbox = gtk_vbox_new(FALSE, 3);
603 gtk_container_add(GTK_CONTAINER(frame), vbox);
604
605 if (flags & GFIO_SLAT)
606 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
607 if (flags & GFIO_CLAT)
608 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
609 if (flags & GFIO_LAT)
610 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
611 }
612
613 if (ts->clat_percentiles)
614 gfio_show_clat_percentiles(main_vbox, ts, ddir);
615
616
617 free(io_p);
618 free(bw_p);
619 free(iops_p);
620}
621
622static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
623 const char **labels)
624{
625 GtkWidget *tree_view;
626 GtkTreeSelection *selection;
627 GtkListStore *model;
628 GtkTreeIter iter;
629 GType *types;
630 int i, skipped;
631
632 /*
633 * Check if all are empty, in which case don't bother
634 */
635 for (i = 0, skipped = 0; i < num; i++)
636 if (lat[i] <= 0.0)
637 skipped++;
638
639 if (skipped == num)
640 return NULL;
641
642 types = malloc(num * sizeof(GType));
643
644 for (i = 0; i < num; i++)
645 types[i] = G_TYPE_STRING;
646
647 model = gtk_list_store_newv(num, types);
648 free(types);
649 types = NULL;
650
651 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
652 gtk_widget_set_can_focus(tree_view, FALSE);
653
654 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
655 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
656
657 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
658 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
659
660 for (i = 0; i < num; i++)
661 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
662
663 gtk_list_store_append(model, &iter);
664
665 for (i = 0; i < num; i++) {
666 char fbuf[32];
667
668 if (lat[i] <= 0.0)
669 sprintf(fbuf, "0.00");
670 else
671 sprintf(fbuf, "%3.2f%%", lat[i]);
672
673 gtk_list_store_set(model, &iter, i, fbuf, -1);
674 }
675
676 return tree_view;
677}
678
679static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
680{
681 GtkWidget *box, *frame, *tree_view;
682 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
683 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
684 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
685 "250", "500", "750", "1000", };
686 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
687 "250", "500", "750", "1000", "2000",
688 ">= 2000", };
689
690 stat_calc_lat_u(ts, io_u_lat_u);
691 stat_calc_lat_m(ts, io_u_lat_m);
692
693 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
694 if (tree_view) {
695 frame = gtk_frame_new("Latency buckets (usec)");
696 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
697
698 box = gtk_hbox_new(FALSE, 3);
699 gtk_container_add(GTK_CONTAINER(frame), box);
700 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
701 }
702
703 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
704 if (tree_view) {
705 frame = gtk_frame_new("Latency buckets (msec)");
706 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
707
708 box = gtk_hbox_new(FALSE, 3);
709 gtk_container_add(GTK_CONTAINER(frame), box);
710 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
711 }
712}
713
714static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
715{
716 GtkWidget *box, *frame, *entry;
717 double usr_cpu, sys_cpu;
718 unsigned long runtime;
719 char tmp[32];
720
721 runtime = ts->total_run_time;
722 if (runtime) {
723 double runt = (double) runtime;
724
725 usr_cpu = (double) ts->usr_time * 100 / runt;
726 sys_cpu = (double) ts->sys_time * 100 / runt;
727 } else {
728 usr_cpu = 0;
729 sys_cpu = 0;
730 }
731
732 frame = gtk_frame_new("OS resources");
733 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
734
735 box = gtk_hbox_new(FALSE, 3);
736 gtk_container_add(GTK_CONTAINER(frame), box);
737
738 entry = new_info_entry_in_frame(box, "User CPU");
739 sprintf(tmp, "%3.2f%%", usr_cpu);
740 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
741 entry = new_info_entry_in_frame(box, "System CPU");
742 sprintf(tmp, "%3.2f%%", sys_cpu);
743 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
744 entry = new_info_entry_in_frame(box, "Context switches");
745 entry_set_int_value(entry, ts->ctx);
746 entry = new_info_entry_in_frame(box, "Major faults");
747 entry_set_int_value(entry, ts->majf);
748 entry = new_info_entry_in_frame(box, "Minor faults");
749 entry_set_int_value(entry, ts->minf);
750}
751static void gfio_add_sc_depths_tree(GtkListStore *model,
752 struct thread_stat *ts, unsigned int len,
753 int submit)
754{
755 double io_u_dist[FIO_IO_U_MAP_NR];
756 GtkTreeIter iter;
757 /* Bits 0, and 3-8 */
758 const int add_mask = 0x1f9;
759 int i, j;
760
761 if (submit)
762 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
763 else
764 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
765
766 gtk_list_store_append(model, &iter);
767
768 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
769
770 for (i = 1, j = 0; i < len; i++) {
771 char fbuf[32];
772
773 if (!(add_mask & (1UL << (i - 1))))
774 sprintf(fbuf, "0.0%%");
775 else {
776 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
777 j++;
778 }
779
780 gtk_list_store_set(model, &iter, i, fbuf, -1);
781 }
782
783}
784
785static void gfio_add_total_depths_tree(GtkListStore *model,
786 struct thread_stat *ts, unsigned int len)
787{
788 double io_u_dist[FIO_IO_U_MAP_NR];
789 GtkTreeIter iter;
790 /* Bits 1-6, and 8 */
791 const int add_mask = 0x17e;
792 int i, j;
793
794 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
795
796 gtk_list_store_append(model, &iter);
797
798 gtk_list_store_set(model, &iter, 0, "Total", -1);
799
800 for (i = 1, j = 0; i < len; i++) {
801 char fbuf[32];
802
803 if (!(add_mask & (1UL << (i - 1))))
804 sprintf(fbuf, "0.0%%");
805 else {
806 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
807 j++;
808 }
809
810 gtk_list_store_set(model, &iter, i, fbuf, -1);
811 }
812
813}
814
815static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
816{
817 GtkWidget *frame, *box, *tree_view;
818 GtkTreeSelection *selection;
819 GtkListStore *model;
820 GType types[FIO_IO_U_MAP_NR + 1];
821 int i;
822#define NR_LABELS 10
823 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
824
825 frame = gtk_frame_new("IO depths");
826 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
827
828 box = gtk_hbox_new(FALSE, 3);
829 gtk_container_add(GTK_CONTAINER(frame), box);
830
831 for (i = 0; i < NR_LABELS; i++)
832 types[i] = G_TYPE_STRING;
833
834 model = gtk_list_store_newv(NR_LABELS, types);
835
836 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
837 gtk_widget_set_can_focus(tree_view, FALSE);
838
839 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
840 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
841
842 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
843 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
844
845 for (i = 0; i < NR_LABELS; i++)
846 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
847
848 gfio_add_total_depths_tree(model, ts, NR_LABELS);
849 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
850 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
851
852 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
853}
854
855static gboolean results_window_delete(GtkWidget *w, gpointer data)
856{
857 struct gui_entry *ge = (struct gui_entry *) data;
858
859 gtk_widget_destroy(w);
860 ge->results_window = NULL;
861 ge->results_notebook = NULL;
862 return TRUE;
863}
864
865static GtkWidget *get_results_window(struct gui_entry *ge)
866{
867 GtkWidget *win, *notebook;
868
869 if (ge->results_window)
870 return ge->results_notebook;
871
872 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
873 gtk_window_set_title(GTK_WINDOW(win), "Results");
874 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
875 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
876 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
877
878 notebook = gtk_notebook_new();
879 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
880 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
881 gtk_container_add(GTK_CONTAINER(win), notebook);
882
883 ge->results_window = win;
884 ge->results_notebook = notebook;
885 return ge->results_notebook;
886}
887
888static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
889 struct group_run_stats *rs)
890{
891 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
892 struct gfio_client *gc = client->client_data;
893
894 gdk_threads_enter();
895
896 res_win = get_results_window(gc->ge);
897
898 scroll = gtk_scrolled_window_new(NULL, NULL);
899 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
900 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
901
902 vbox = gtk_vbox_new(FALSE, 3);
903
904 box = gtk_hbox_new(FALSE, 0);
905 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
906
907 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
908
909 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
910
911 gc->results_widget = vbox;
912
913 entry = new_info_entry_in_frame(box, "Name");
914 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
915 if (strlen(ts->description)) {
916 entry = new_info_entry_in_frame(box, "Description");
917 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
918 }
919 entry = new_info_entry_in_frame(box, "Group ID");
920 entry_set_int_value(entry, ts->groupid);
921 entry = new_info_entry_in_frame(box, "Jobs");
922 entry_set_int_value(entry, ts->members);
923 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
924 entry_set_int_value(entry, ts->error);
925 entry = new_info_entry_in_frame(box, "PID");
926 entry_set_int_value(entry, ts->pid);
927
928 if (ts->io_bytes[DDIR_READ])
929 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
930 if (ts->io_bytes[DDIR_WRITE])
931 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
932
933 gfio_show_latency_buckets(vbox, ts);
934 gfio_show_cpu_usage(vbox, ts);
935 gfio_show_io_depths(vbox, ts);
936
937 gtk_widget_show_all(gc->ge->results_window);
938 gdk_threads_leave();
939}
940
941static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
942{
943 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
944 struct gui *ui = &main_ui;
945 GtkTreeIter iter;
946 struct tm *tm;
947 time_t sec;
948 char tmp[64], timebuf[80];
949
950 sec = p->log_sec;
951 tm = localtime(&sec);
952 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
953 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
954
955 gdk_threads_enter();
956
957 gtk_list_store_append(ui->log_model, &iter);
958 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
959 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
960 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
961 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
962
963 if (p->level == FIO_LOG_ERR)
964 view_log(NULL, (gpointer) ui);
965
966 gdk_threads_leave();
967}
968
969static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
970{
971 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
972 struct gfio_client *gc = client->client_data;
973 GtkWidget *box, *frame, *entry, *vbox;
974 double util;
975 char tmp[16];
976
977 gdk_threads_enter();
978
979 if (!gc->results_widget)
980 goto out;
981
982 if (!gc->disk_util_frame) {
983 gc->disk_util_frame = gtk_frame_new("Disk utilization");
984 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
985 }
986
987 vbox = gtk_vbox_new(FALSE, 3);
988 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
989
990 frame = gtk_frame_new((char *) p->dus.name);
991 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
992
993 box = gtk_vbox_new(FALSE, 3);
994 gtk_container_add(GTK_CONTAINER(frame), box);
995
996 frame = gtk_frame_new("Read");
997 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
998 vbox = gtk_hbox_new(TRUE, 3);
999 gtk_container_add(GTK_CONTAINER(frame), vbox);
1000 entry = new_info_entry_in_frame(vbox, "IOs");
1001 entry_set_int_value(entry, p->dus.ios[0]);
1002 entry = new_info_entry_in_frame(vbox, "Merges");
1003 entry_set_int_value(entry, p->dus.merges[0]);
1004 entry = new_info_entry_in_frame(vbox, "Sectors");
1005 entry_set_int_value(entry, p->dus.sectors[0]);
1006 entry = new_info_entry_in_frame(vbox, "Ticks");
1007 entry_set_int_value(entry, p->dus.ticks[0]);
1008
1009 frame = gtk_frame_new("Write");
1010 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1011 vbox = gtk_hbox_new(TRUE, 3);
1012 gtk_container_add(GTK_CONTAINER(frame), vbox);
1013 entry = new_info_entry_in_frame(vbox, "IOs");
1014 entry_set_int_value(entry, p->dus.ios[1]);
1015 entry = new_info_entry_in_frame(vbox, "Merges");
1016 entry_set_int_value(entry, p->dus.merges[1]);
1017 entry = new_info_entry_in_frame(vbox, "Sectors");
1018 entry_set_int_value(entry, p->dus.sectors[1]);
1019 entry = new_info_entry_in_frame(vbox, "Ticks");
1020 entry_set_int_value(entry, p->dus.ticks[1]);
1021
1022 frame = gtk_frame_new("Shared");
1023 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1024 vbox = gtk_hbox_new(TRUE, 3);
1025 gtk_container_add(GTK_CONTAINER(frame), vbox);
1026 entry = new_info_entry_in_frame(vbox, "IO ticks");
1027 entry_set_int_value(entry, p->dus.io_ticks);
1028 entry = new_info_entry_in_frame(vbox, "Time in queue");
1029 entry_set_int_value(entry, p->dus.time_in_queue);
1030
1031 util = 0.0;
1032 if (p->dus.msec)
1033 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1034 if (util > 100.0)
1035 util = 100.0;
1036
1037 sprintf(tmp, "%3.2f%%", util);
1038 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1039 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1040
1041 gtk_widget_show_all(gc->results_widget);
1042out:
1043 gdk_threads_leave();
1044}
1045
1046extern int sum_stat_clients;
1047extern struct thread_stat client_ts;
1048extern struct group_run_stats client_gs;
1049
1050static int sum_stat_nr;
1051
1052static void gfio_thread_status_op(struct fio_client *client,
1053 struct fio_net_cmd *cmd)
1054{
1055 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1056
1057 gfio_display_ts(client, &p->ts, &p->rs);
1058
1059 if (sum_stat_clients == 1)
1060 return;
1061
1062 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1063 sum_group_stats(&client_gs, &p->rs);
1064
1065 client_ts.members++;
1066 client_ts.groupid = p->ts.groupid;
1067
1068 if (++sum_stat_nr == sum_stat_clients) {
1069 strcpy(client_ts.name, "All clients");
1070 gfio_display_ts(client, &client_ts, &client_gs);
1071 }
1072}
1073
1074static void gfio_group_stats_op(struct fio_client *client,
1075 struct fio_net_cmd *cmd)
1076{
1077 /* We're ignoring group stats for now */
1078}
1079
1080static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1081 gpointer data)
1082{
1083 struct gfio_graphs *g = data;
1084
1085 g->drawing_area_xdim = w->allocation.width;
1086 g->drawing_area_ydim = w->allocation.height;
1087 return TRUE;
1088}
1089
1090static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1091{
1092 struct gfio_graphs *g = p;
1093 cairo_t *cr;
1094
1095 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1096 g->drawing_area_ydim);
1097 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1098 g->drawing_area_ydim);
1099 cr = gdk_cairo_create(w->window);
1100
1101 cairo_set_source_rgb(cr, 0, 0, 0);
1102
1103 cairo_save(cr);
1104 cairo_translate(cr, 0, 0);
1105 line_graph_draw(g->bandwidth_graph, cr);
1106 cairo_stroke(cr);
1107 cairo_restore(cr);
1108
1109 cairo_save(cr);
1110 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1111 line_graph_draw(g->iops_graph, cr);
1112 cairo_stroke(cr);
1113 cairo_restore(cr);
1114 cairo_destroy(cr);
1115
1116 return FALSE;
1117}
1118
1119/*
1120 * Client specific ETA
1121 */
1122static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1123{
1124 struct gfio_client *gc = client->client_data;
1125 struct gui_entry *ge = gc->ge;
1126 static int eta_good;
1127 char eta_str[128];
1128 char output[256];
1129 char tmp[32];
1130 double perc = 0.0;
1131 int i2p = 0;
1132
1133 gdk_threads_enter();
1134
1135 eta_str[0] = '\0';
1136 output[0] = '\0';
1137
1138 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1139 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1140 eta_to_str(eta_str, je->eta_sec);
1141 }
1142
1143 sprintf(tmp, "%u", je->nr_running);
1144 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1145 sprintf(tmp, "%u", je->files_open);
1146 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1147
1148#if 0
1149 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1150 if (je->m_rate || je->t_rate) {
1151 char *tr, *mr;
1152
1153 mr = num2str(je->m_rate, 4, 0, i2p);
1154 tr = num2str(je->t_rate, 4, 0, i2p);
1155 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1156 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1157 free(tr);
1158 free(mr);
1159 } else if (je->m_iops || je->t_iops)
1160 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1161
1162 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1163 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1164 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1165 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1166#endif
1167
1168 if (je->eta_sec != INT_MAX && je->nr_running) {
1169 char *iops_str[2];
1170 char *rate_str[2];
1171
1172 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1173 strcpy(output, "-.-% done");
1174 else {
1175 eta_good = 1;
1176 perc *= 100.0;
1177 sprintf(output, "%3.1f%% done", perc);
1178 }
1179
1180 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1181 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1182
1183 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1184 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1185
1186 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1187 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1188 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1189 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1190
1191 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1192 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1193 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1194 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1195
1196 free(rate_str[0]);
1197 free(rate_str[1]);
1198 free(iops_str[0]);
1199 free(iops_str[1]);
1200 }
1201
1202 if (eta_str[0]) {
1203 char *dst = output + strlen(output);
1204
1205 sprintf(dst, " - %s", eta_str);
1206 }
1207
1208 gfio_update_thread_status(ge, output, perc);
1209 gdk_threads_leave();
1210}
1211
1212/*
1213 * Update ETA in main window for all clients
1214 */
1215static void gfio_update_all_eta(struct jobs_eta *je)
1216{
1217 struct gui *ui = &main_ui;
1218 static int eta_good;
1219 char eta_str[128];
1220 char output[256];
1221 double perc = 0.0;
1222 int i2p = 0;
1223
1224 gdk_threads_enter();
1225
1226 eta_str[0] = '\0';
1227 output[0] = '\0';
1228
1229 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1230 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1231 eta_to_str(eta_str, je->eta_sec);
1232 }
1233
1234#if 0
1235 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1236 if (je->m_rate || je->t_rate) {
1237 char *tr, *mr;
1238
1239 mr = num2str(je->m_rate, 4, 0, i2p);
1240 tr = num2str(je->t_rate, 4, 0, i2p);
1241 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1242 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1243 free(tr);
1244 free(mr);
1245 } else if (je->m_iops || je->t_iops)
1246 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1247
1248 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1249 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1250 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1251 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1252#endif
1253
1254 if (je->eta_sec != INT_MAX && je->nr_running) {
1255 char *iops_str[2];
1256 char *rate_str[2];
1257
1258 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1259 strcpy(output, "-.-% done");
1260 else {
1261 eta_good = 1;
1262 perc *= 100.0;
1263 sprintf(output, "%3.1f%% done", perc);
1264 }
1265
1266 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1267 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1268
1269 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1270 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1271
1272 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1273 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1274 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1275 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1276
1277 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1278 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1279 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1280 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1281
1282 free(rate_str[0]);
1283 free(rate_str[1]);
1284 free(iops_str[0]);
1285 free(iops_str[1]);
1286 }
1287
1288 if (eta_str[0]) {
1289 char *dst = output + strlen(output);
1290
1291 sprintf(dst, " - %s", eta_str);
1292 }
1293
1294 gfio_update_thread_status_all(output, perc);
1295 gdk_threads_leave();
1296}
1297
1298static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1299{
1300 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1301 struct gfio_client *gc = client->client_data;
1302 struct gui_entry *ge = gc->ge;
1303 const char *os, *arch;
1304 char buf[64];
1305
1306 os = fio_get_os_string(probe->os);
1307 if (!os)
1308 os = "unknown";
1309
1310 arch = fio_get_arch_string(probe->arch);
1311 if (!arch)
1312 os = "unknown";
1313
1314 if (!client->name)
1315 client->name = strdup((char *) probe->hostname);
1316
1317 gdk_threads_enter();
1318
1319 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1320 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1321 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1322 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1323 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1324
1325 gfio_set_connected(ge, 1);
1326
1327 gdk_threads_leave();
1328}
1329
1330static void gfio_update_thread_status(struct gui_entry *ge,
1331 char *status_message, double perc)
1332{
1333 static char message[100];
1334 const char *m = message;
1335
1336 strncpy(message, status_message, sizeof(message) - 1);
1337 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1338 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1339 gtk_widget_queue_draw(main_ui.window);
1340}
1341
1342static void gfio_update_thread_status_all(char *status_message, double perc)
1343{
1344 struct gui *ui = &main_ui;
1345 static char message[100];
1346 const char *m = message;
1347
1348 strncpy(message, status_message, sizeof(message) - 1);
1349 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1350 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1351 gtk_widget_queue_draw(ui->window);
1352}
1353
1354static void gfio_quit_op(struct fio_client *client)
1355{
1356 struct gfio_client *gc = client->client_data;
1357
1358 gdk_threads_enter();
1359 gfio_set_connected(gc->ge, 0);
1360 gdk_threads_leave();
1361}
1362
1363static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1364{
1365 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1366 struct gfio_client *gc = client->client_data;
1367 struct thread_options *o = &gc->o;
1368 struct gui_entry *ge = gc->ge;
1369 char tmp[8];
1370
1371 convert_thread_options_to_cpu(o, &p->top);
1372
1373 gdk_threads_enter();
1374
1375 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1376
1377 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1378 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1379 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1380
1381 sprintf(tmp, "%u", o->iodepth);
1382 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1383
1384 gc->job_added++;
1385
1386 gdk_threads_leave();
1387}
1388
1389static void gfio_client_timed_out(struct fio_client *client)
1390{
1391 struct gfio_client *gc = client->client_data;
1392 GtkWidget *dialog, *label, *content;
1393 char buf[256];
1394
1395 gdk_threads_enter();
1396
1397 gfio_set_connected(gc->ge, 0);
1398 clear_ge_ui_info(gc->ge);
1399
1400 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1401
1402 dialog = gtk_dialog_new_with_buttons("Timed out!",
1403 GTK_WINDOW(main_ui.window),
1404 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1405 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1406
1407 /* gtk_dialog_get_content_area() is 2.14 and newer */
1408 content = GTK_DIALOG(dialog)->vbox;
1409
1410 label = gtk_label_new((const gchar *) buf);
1411 gtk_container_add(GTK_CONTAINER(content), label);
1412 gtk_widget_show_all(dialog);
1413 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1414
1415 gtk_dialog_run(GTK_DIALOG(dialog));
1416 gtk_widget_destroy(dialog);
1417
1418 gdk_threads_leave();
1419}
1420
1421static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1422{
1423 struct gfio_client *gc = client->client_data;
1424
1425 gdk_threads_enter();
1426
1427 gfio_set_connected(gc->ge, 0);
1428
1429 if (gc->err_entry)
1430 entry_set_int_value(gc->err_entry, client->error);
1431
1432 gdk_threads_leave();
1433}
1434
1435struct client_ops gfio_client_ops = {
1436 .text_op = gfio_text_op,
1437 .disk_util = gfio_disk_util_op,
1438 .thread_status = gfio_thread_status_op,
1439 .group_stats = gfio_group_stats_op,
1440 .jobs_eta = gfio_update_client_eta,
1441 .eta = gfio_update_all_eta,
1442 .probe = gfio_probe_op,
1443 .quit = gfio_quit_op,
1444 .add_job = gfio_add_job_op,
1445 .timed_out = gfio_client_timed_out,
1446 .stop = gfio_client_stop,
1447 .stay_connected = 1,
1448};
1449
1450static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1451 __attribute__((unused)) gpointer data)
1452{
1453 gtk_main_quit();
1454}
1455
1456static void *job_thread(void *arg)
1457{
1458 struct gui *ui = arg;
1459
1460 ui->handler_running = 1;
1461 fio_handle_clients(&gfio_client_ops);
1462 ui->handler_running = 0;
1463 return NULL;
1464}
1465
1466static int send_job_files(struct gui_entry *ge)
1467{
1468 struct gfio_client *gc = ge->client;
1469 int i, ret = 0;
1470
1471 for (i = 0; i < ge->nr_job_files; i++) {
1472 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1473 if (ret < 0) {
1474 GError *error;
1475
1476 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1477 report_error(error);
1478 g_error_free(error);
1479 break;
1480 } else if (ret)
1481 break;
1482
1483 free(ge->job_files[i]);
1484 ge->job_files[i] = NULL;
1485 }
1486 while (i < ge->nr_job_files) {
1487 free(ge->job_files[i]);
1488 ge->job_files[i] = NULL;
1489 i++;
1490 }
1491
1492 return ret;
1493}
1494
1495static void *server_thread(void *arg)
1496{
1497 is_backend = 1;
1498 gfio_server_running = 1;
1499 fio_start_server(NULL);
1500 gfio_server_running = 0;
1501 return NULL;
1502}
1503
1504static void gfio_start_server(void)
1505{
1506 struct gui *ui = &main_ui;
1507
1508 if (!gfio_server_running) {
1509 gfio_server_running = 1;
1510 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1511 pthread_detach(ui->server_t);
1512 }
1513}
1514
1515static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1516 gpointer data)
1517{
1518 struct gui_entry *ge = data;
1519 struct gfio_client *gc = ge->client;
1520
1521 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1522 fio_start_client(gc->client);
1523}
1524
1525static void file_open(GtkWidget *w, gpointer data);
1526
1527static void connect_clicked(GtkWidget *widget, gpointer data)
1528{
1529 struct gui_entry *ge = data;
1530 struct gfio_client *gc = ge->client;
1531
1532 if (!ge->connected) {
1533 int ret;
1534
1535 if (!ge->nr_job_files)
1536 file_open(widget, data);
1537 if (!ge->nr_job_files)
1538 return;
1539
1540 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1541 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1542 ret = fio_client_connect(gc->client);
1543 if (!ret) {
1544 if (!ge->ui->handler_running)
1545 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1546 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1547 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1548 } else {
1549 GError *error;
1550
1551 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1552 report_error(error);
1553 g_error_free(error);
1554 }
1555 } else {
1556 fio_client_terminate(gc->client);
1557 gfio_set_connected(ge, 0);
1558 clear_ge_ui_info(ge);
1559 }
1560}
1561
1562static void send_clicked(GtkWidget *widget, gpointer data)
1563{
1564 struct gui_entry *ge = data;
1565
1566 if (send_job_files(ge)) {
1567 GError *error;
1568
1569 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1570 report_error(error);
1571 g_error_free(error);
1572
1573 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1574 }
1575
1576 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1577 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1578}
1579
1580static GtkWidget *add_button(GtkWidget *buttonbox,
1581 struct button_spec *buttonspec, gpointer data)
1582{
1583 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1584
1585 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1586 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1587 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1588 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1589
1590 return button;
1591}
1592
1593static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1594 int nbuttons)
1595{
1596 int i;
1597
1598 for (i = 0; i < nbuttons; i++)
1599 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1600}
1601
1602static void on_info_bar_response(GtkWidget *widget, gint response,
1603 gpointer data)
1604{
1605 struct gui *ui = &main_ui;
1606
1607 if (response == GTK_RESPONSE_OK) {
1608 gtk_widget_destroy(widget);
1609 ui->error_info_bar = NULL;
1610 }
1611}
1612
1613void report_error(GError *error)
1614{
1615 struct gui *ui = &main_ui;
1616
1617 if (ui->error_info_bar == NULL) {
1618 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1619 GTK_RESPONSE_OK,
1620 NULL);
1621 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1622 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1623 GTK_MESSAGE_ERROR);
1624
1625 ui->error_label = gtk_label_new(error->message);
1626 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1627 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1628
1629 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1630 gtk_widget_show_all(ui->vbox);
1631 } else {
1632 char buffer[256];
1633 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1634 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1635 }
1636}
1637
1638struct connection_widgets
1639{
1640 GtkWidget *hentry;
1641 GtkWidget *combo;
1642 GtkWidget *button;
1643};
1644
1645static void hostname_cb(GtkEntry *entry, gpointer data)
1646{
1647 struct connection_widgets *cw = data;
1648 int uses_net = 0, is_localhost = 0;
1649 const gchar *text;
1650 gchar *ctext;
1651
1652 /*
1653 * Check whether to display the 'auto start backend' box
1654 * or not. Show it if we are a localhost and using network,
1655 * or using a socket.
1656 */
1657 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1658 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1659 uses_net = 1;
1660 g_free(ctext);
1661
1662 if (uses_net) {
1663 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1664 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1665 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1666 !strcmp(text, "ip6-loopback"))
1667 is_localhost = 1;
1668 }
1669
1670 if (!uses_net || is_localhost) {
1671 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1672 gtk_widget_set_sensitive(cw->button, 1);
1673 } else {
1674 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1675 gtk_widget_set_sensitive(cw->button, 0);
1676 }
1677}
1678
1679static int get_connection_details(char **host, int *port, int *type,
1680 int *server_start)
1681{
1682 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1683 struct connection_widgets cw;
1684 char *typeentry;
1685
1686 dialog = gtk_dialog_new_with_buttons("Connection details",
1687 GTK_WINDOW(main_ui.window),
1688 GTK_DIALOG_DESTROY_WITH_PARENT,
1689 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1690 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1691
1692 frame = gtk_frame_new("Hostname / socket name");
1693 /* gtk_dialog_get_content_area() is 2.14 and newer */
1694 vbox = GTK_DIALOG(dialog)->vbox;
1695 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1696
1697 box = gtk_vbox_new(FALSE, 6);
1698 gtk_container_add(GTK_CONTAINER(frame), box);
1699
1700 hbox = gtk_hbox_new(TRUE, 10);
1701 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1702 cw.hentry = gtk_entry_new();
1703 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1704 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1705
1706 frame = gtk_frame_new("Port");
1707 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1708 box = gtk_vbox_new(FALSE, 10);
1709 gtk_container_add(GTK_CONTAINER(frame), box);
1710
1711 hbox = gtk_hbox_new(TRUE, 4);
1712 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1713 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1714
1715 frame = gtk_frame_new("Type");
1716 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1717 box = gtk_vbox_new(FALSE, 10);
1718 gtk_container_add(GTK_CONTAINER(frame), box);
1719
1720 hbox = gtk_hbox_new(TRUE, 4);
1721 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1722
1723 cw.combo = gtk_combo_box_new_text();
1724 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1725 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1726 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1727 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1728
1729 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1730
1731 frame = gtk_frame_new("Options");
1732 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1733 box = gtk_vbox_new(FALSE, 10);
1734 gtk_container_add(GTK_CONTAINER(frame), box);
1735
1736 hbox = gtk_hbox_new(TRUE, 4);
1737 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1738
1739 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1740 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1741 gtk_widget_set_tooltip_text(cw.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.");
1742 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1743
1744 /*
1745 * Connect edit signal, so we can show/not-show the auto start button
1746 */
1747 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1748 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1749
1750 gtk_widget_show_all(dialog);
1751
1752 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1753 gtk_widget_destroy(dialog);
1754 return 1;
1755 }
1756
1757 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1758 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1759
1760 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1761 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1762 *type = Fio_client_ipv4;
1763 else if (!strncmp(typeentry, "IPv6", 4))
1764 *type = Fio_client_ipv6;
1765 else
1766 *type = Fio_client_socket;
1767 g_free(typeentry);
1768
1769 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1770
1771 gtk_widget_destroy(dialog);
1772 return 0;
1773}
1774
1775static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1776{
1777 struct gfio_client *gc;
1778
1779 gc = malloc(sizeof(*gc));
1780 memset(gc, 0, sizeof(*gc));
1781 gc->ge = ge;
1782 gc->client = fio_get_client(client);
1783
1784 ge->client = gc;
1785
1786 client->client_data = gc;
1787}
1788
1789static GtkWidget *new_client_page(struct gui_entry *ge);
1790
1791static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1792{
1793 struct gui_entry *ge;
1794
1795 ge = malloc(sizeof(*ge));
1796 memset(ge, 0, sizeof(*ge));
1797 INIT_FLIST_HEAD(&ge->list);
1798 flist_add_tail(&ge->list, &ui->list);
1799 ge->ui = ui;
1800 return ge;
1801}
1802
1803/*
1804 * FIXME: need more handling here
1805 */
1806static void ge_destroy(GtkWidget *w, gpointer data)
1807{
1808 struct gui_entry *ge = data;
1809 struct gfio_client *gc = ge->client;
1810
1811 if (gc->client)
1812 fio_put_client(gc->client);
1813
1814 flist_del(&ge->list);
1815 free(ge);
1816}
1817
1818static struct gui_entry *get_new_ge_with_tab(const char *name)
1819{
1820 struct gui_entry *ge;
1821
1822 ge = alloc_new_gui_entry(&main_ui);
1823
1824 ge->vbox = new_client_page(ge);
1825 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1826
1827 ge->page_label = gtk_label_new(name);
1828 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1829
1830 gtk_widget_show_all(main_ui.window);
1831 return ge;
1832}
1833
1834static void file_new(GtkWidget *w, gpointer data)
1835{
1836 get_new_ge_with_tab("Untitled");
1837}
1838
1839/*
1840 * Return the 'ge' corresponding to the tab. If the active tab is the
1841 * main tab, open a new tab.
1842 */
1843static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1844{
1845 struct flist_head *entry;
1846 struct gui_entry *ge;
1847
1848 if (!cur_page)
1849 return get_new_ge_with_tab("Untitled");
1850
1851 flist_for_each(entry, &main_ui.list) {
1852 ge = flist_entry(entry, struct gui_entry, list);
1853 if (ge->page_num == cur_page)
1854 return ge;
1855 }
1856
1857 return NULL;
1858}
1859
1860static void file_open(GtkWidget *w, gpointer data)
1861{
1862 struct gui *ui = data;
1863 GtkWidget *dialog;
1864 GSList *filenames, *fn_glist;
1865 GtkFileFilter *filter;
1866 char *host;
1867 int port, type, server_start;
1868 struct gui_entry *ge;
1869 gint cur_page;
1870
1871 /*
1872 * Creates new tab if current tab is the main window, or the
1873 * current tab already has a client.
1874 */
1875 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1876 ge = get_ge_from_page(cur_page);
1877 if (ge->client)
1878 ge = get_new_ge_with_tab("Untitled");
1879
1880 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1881
1882 dialog = gtk_file_chooser_dialog_new("Open File",
1883 GTK_WINDOW(ui->window),
1884 GTK_FILE_CHOOSER_ACTION_OPEN,
1885 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1886 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1887 NULL);
1888 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1889
1890 filter = gtk_file_filter_new();
1891 gtk_file_filter_add_pattern(filter, "*.fio");
1892 gtk_file_filter_add_pattern(filter, "*.job");
1893 gtk_file_filter_add_pattern(filter, "*.ini");
1894 gtk_file_filter_add_mime_type(filter, "text/fio");
1895 gtk_file_filter_set_name(filter, "Fio job file");
1896 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1897
1898 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1899 gtk_widget_destroy(dialog);
1900 return;
1901 }
1902
1903 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1904
1905 gtk_widget_destroy(dialog);
1906
1907 if (get_connection_details(&host, &port, &type, &server_start))
1908 goto err;
1909
1910 filenames = fn_glist;
1911 while (filenames != NULL) {
1912 struct fio_client *client;
1913
1914 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1915 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1916 ge->nr_job_files++;
1917
1918 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1919 if (!client) {
1920 GError *error;
1921
1922 error = g_error_new(g_quark_from_string("fio"), 1,
1923 "Failed to add client %s", host);
1924 report_error(error);
1925 g_error_free(error);
1926 }
1927 gfio_client_added(ge, client);
1928
1929 g_free(filenames->data);
1930 filenames = g_slist_next(filenames);
1931 }
1932 free(host);
1933
1934 if (server_start)
1935 gfio_start_server();
1936err:
1937 g_slist_free(fn_glist);
1938}
1939
1940static void file_save(GtkWidget *w, gpointer data)
1941{
1942 struct gui *ui = data;
1943 GtkWidget *dialog;
1944
1945 dialog = gtk_file_chooser_dialog_new("Save File",
1946 GTK_WINDOW(ui->window),
1947 GTK_FILE_CHOOSER_ACTION_SAVE,
1948 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1949 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1950 NULL);
1951
1952 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1953 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1954
1955 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1956 char *filename;
1957
1958 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1959 // save_job_file(filename);
1960 g_free(filename);
1961 }
1962 gtk_widget_destroy(dialog);
1963}
1964
1965static void view_log_destroy(GtkWidget *w, gpointer data)
1966{
1967 struct gui *ui = (struct gui *) data;
1968
1969 gtk_widget_ref(ui->log_tree);
1970 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1971 gtk_widget_destroy(w);
1972 ui->log_view = NULL;
1973}
1974
1975static void view_log(GtkWidget *w, gpointer data)
1976{
1977 GtkWidget *win, *scroll, *vbox, *box;
1978 struct gui *ui = (struct gui *) data;
1979
1980 if (ui->log_view)
1981 return;
1982
1983 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1984 gtk_window_set_title(GTK_WINDOW(win), "Log");
1985 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1986
1987 scroll = gtk_scrolled_window_new(NULL, NULL);
1988
1989 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1990
1991 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1992
1993 box = gtk_hbox_new(TRUE, 0);
1994 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1995 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1996 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1997
1998 vbox = gtk_vbox_new(TRUE, 5);
1999 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2000
2001 gtk_container_add(GTK_CONTAINER(win), vbox);
2002 gtk_widget_show_all(win);
2003}
2004
2005static void preferences(GtkWidget *w, gpointer data)
2006{
2007 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2008 int i;
2009
2010 dialog = gtk_dialog_new_with_buttons("Preferences",
2011 GTK_WINDOW(main_ui.window),
2012 GTK_DIALOG_DESTROY_WITH_PARENT,
2013 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2014 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2015 NULL);
2016
2017 frame = gtk_frame_new("Debug logging");
2018 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2019
2020 vbox = gtk_vbox_new(FALSE, 6);
2021 gtk_container_add(GTK_CONTAINER(frame), vbox);
2022
2023 box = gtk_hbox_new(FALSE, 6);
2024 gtk_container_add(GTK_CONTAINER(vbox), box);
2025
2026 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2027
2028 for (i = 0; i < FD_DEBUG_MAX; i++) {
2029 if (i == 7) {
2030 box = gtk_hbox_new(FALSE, 6);
2031 gtk_container_add(GTK_CONTAINER(vbox), box);
2032 }
2033
2034
2035 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2036 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2037 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2038 }
2039
2040 frame = gtk_frame_new("Graph font");
2041 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2042 vbox = gtk_vbox_new(FALSE, 6);
2043 gtk_container_add(GTK_CONTAINER(frame), vbox);
2044
2045 font = gtk_font_button_new();
2046 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2047
2048 gtk_widget_show_all(dialog);
2049
2050 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2051 gtk_widget_destroy(dialog);
2052 return;
2053 }
2054
2055 for (i = 0; i < FD_DEBUG_MAX; i++) {
2056 int set;
2057
2058 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2059 if (set)
2060 fio_debug |= (1UL << i);
2061 }
2062
2063 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2064 gtk_widget_destroy(dialog);
2065}
2066
2067static void about_dialog(GtkWidget *w, gpointer data)
2068{
2069 const char *authors[] = {
2070 "Jens Axboe <axboe@kernel.dk>",
2071 "Stephen Carmeron <stephenmcameron@gmail.com>",
2072 NULL
2073 };
2074 const char *license[] = {
2075 "Fio is free software; you can redistribute it and/or modify "
2076 "it under the terms of the GNU General Public License as published by "
2077 "the Free Software Foundation; either version 2 of the License, or "
2078 "(at your option) any later version.\n",
2079 "Fio is distributed in the hope that it will be useful, "
2080 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2081 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2082 "GNU General Public License for more details.\n",
2083 "You should have received a copy of the GNU General Public License "
2084 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2085 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2086 };
2087 char *license_trans;
2088
2089 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2090 license[2], "\n", NULL);
2091
2092 gtk_show_about_dialog(NULL,
2093 "program-name", "gfio",
2094 "comments", "Gtk2 UI for fio",
2095 "license", license_trans,
2096 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2097 "authors", authors,
2098 "version", fio_version_string,
2099 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2100 "logo-icon-name", "fio",
2101 /* Must be last: */
2102 "wrap-license", TRUE,
2103 NULL);
2104
2105 g_free(license_trans);
2106}
2107
2108static GtkActionEntry menu_items[] = {
2109 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2110 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2111 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2112 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2113 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2114 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2115 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2116 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2117 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2118 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2119};
2120static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2121
2122static const gchar *ui_string = " \
2123 <ui> \
2124 <menubar name=\"MainMenu\"> \
2125 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2126 <menuitem name=\"New\" action=\"NewFile\" /> \
2127 <separator name=\"Separator1\"/> \
2128 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2129 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2130 <separator name=\"Separator2\"/> \
2131 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2132 <separator name=\"Separator3\"/> \
2133 <menuitem name=\"Quit\" action=\"Quit\" /> \
2134 </menu> \
2135 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2136 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2137 </menu>\
2138 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2139 <menuitem name=\"About\" action=\"About\" /> \
2140 </menu> \
2141 </menubar> \
2142 </ui> \
2143";
2144
2145static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2146 struct gui *ui)
2147{
2148 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2149 GError *error = 0;
2150
2151 action_group = gtk_action_group_new("Menu");
2152 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2153
2154 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2155 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2156
2157 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2158 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2159}
2160
2161void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2162 GtkWidget *vbox, GtkUIManager *ui_manager)
2163{
2164 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2165}
2166
2167static GtkWidget *new_client_page(struct gui_entry *ge)
2168{
2169 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2170 GdkColor white;
2171
2172 main_vbox = gtk_vbox_new(FALSE, 3);
2173
2174 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2175 ge->topvbox = gtk_vbox_new(FALSE, 3);
2176 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2177 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2178
2179 probe = gtk_frame_new("Job");
2180 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2181 probe_frame = gtk_vbox_new(FALSE, 3);
2182 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2183
2184 probe_box = gtk_hbox_new(FALSE, 3);
2185 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2186 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2187 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2188 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2189 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2190
2191 probe_box = gtk_hbox_new(FALSE, 3);
2192 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2193
2194 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2195 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2196 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2197 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2198 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2199 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2200
2201 probe_box = gtk_hbox_new(FALSE, 3);
2202 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2203 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2204 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2205 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2206 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2207
2208 /*
2209 * Only add this if we have a commit rate
2210 */
2211#if 0
2212 probe_box = gtk_hbox_new(FALSE, 3);
2213 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2214
2215 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2216 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2217
2218 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2219 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2220#endif
2221
2222 /*
2223 * Set up a drawing area and IOPS and bandwidth graphs
2224 */
2225 gdk_color_parse("white", &white);
2226 ge->graphs.drawing_area = gtk_drawing_area_new();
2227 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2228 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2229 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2230 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2231 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2232 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2233 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2234 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2235 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2236 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2237 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2238 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2239 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2240 ge->graphs.drawing_area);
2241 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2242 TRUE, TRUE, 0);
2243
2244 setup_graphs(&ge->graphs);
2245
2246 /*
2247 * Set up alignments for widgets at the bottom of ui,
2248 * align bottom left, expand horizontally but not vertically
2249 */
2250 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2251 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2252 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2253 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2254 FALSE, FALSE, 0);
2255
2256 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2257
2258 /*
2259 * Set up thread status progress bar
2260 */
2261 ge->thread_status_pb = gtk_progress_bar_new();
2262 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2263 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2264 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2265
2266
2267 return main_vbox;
2268}
2269
2270static GtkWidget *new_main_page(struct gui *ui)
2271{
2272 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2273 GdkColor white;
2274
2275 main_vbox = gtk_vbox_new(FALSE, 3);
2276
2277 /*
2278 * Set up alignments for widgets at the top of ui,
2279 * align top left, expand horizontally but not vertically
2280 */
2281 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2282 ui->topvbox = gtk_vbox_new(FALSE, 0);
2283 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2284 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2285
2286 probe = gtk_frame_new("Run statistics");
2287 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2288 probe_frame = gtk_vbox_new(FALSE, 3);
2289 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2290
2291 probe_box = gtk_hbox_new(FALSE, 3);
2292 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2293 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2294 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2295 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2296 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2297
2298 /*
2299 * Only add this if we have a commit rate
2300 */
2301#if 0
2302 probe_box = gtk_hbox_new(FALSE, 3);
2303 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2304
2305 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2306 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2307
2308 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2309 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2310#endif
2311
2312 /*
2313 * Set up a drawing area and IOPS and bandwidth graphs
2314 */
2315 gdk_color_parse("white", &white);
2316 ui->graphs.drawing_area = gtk_drawing_area_new();
2317 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2318 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2319 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2320 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2321 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2322 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2323 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2324 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2325 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2326 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2327 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2328 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2329 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2330 ui->graphs.drawing_area);
2331 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2332 TRUE, TRUE, 0);
2333
2334 setup_graphs(&ui->graphs);
2335
2336 /*
2337 * Set up alignments for widgets at the bottom of ui,
2338 * align bottom left, expand horizontally but not vertically
2339 */
2340 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2341 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2342 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2343 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2344 FALSE, FALSE, 0);
2345
2346 /*
2347 * Set up thread status progress bar
2348 */
2349 ui->thread_status_pb = gtk_progress_bar_new();
2350 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2351 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2352 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2353
2354 return main_vbox;
2355}
2356
2357static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2358 guint page, gpointer data)
2359
2360{
2361 return TRUE;
2362}
2363
2364static void init_ui(int *argc, char **argv[], struct gui *ui)
2365{
2366 GtkSettings *settings;
2367 GtkUIManager *uimanager;
2368 GtkWidget *menu, *vbox;
2369
2370 /* Magical g*thread incantation, you just need this thread stuff.
2371 * Without it, the update that happens in gfio_update_thread_status
2372 * doesn't really happen in a timely fashion, you need expose events
2373 */
2374 if (!g_thread_supported())
2375 g_thread_init(NULL);
2376 gdk_threads_init();
2377
2378 gtk_init(argc, argv);
2379 settings = gtk_settings_get_default();
2380 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2381 g_type_init();
2382
2383 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2384 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2385 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2386
2387 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2388 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2389
2390 ui->vbox = gtk_vbox_new(FALSE, 0);
2391 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2392
2393 uimanager = gtk_ui_manager_new();
2394 menu = get_menubar_menu(ui->window, uimanager, ui);
2395 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2396
2397 ui->notebook = gtk_notebook_new();
2398 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2399 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2400 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2401 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2402
2403 vbox = new_main_page(ui);
2404
2405 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2406
2407 gfio_ui_setup_log(ui);
2408
2409 gtk_widget_show_all(ui->window);
2410}
2411
2412int main(int argc, char *argv[], char *envp[])
2413{
2414 if (initialize_fio(envp))
2415 return 1;
2416 if (fio_init_options())
2417 return 1;
2418
2419 memset(&main_ui, 0, sizeof(main_ui));
2420 INIT_FLIST_HEAD(&main_ui.list);
2421
2422 init_ui(&argc, &argv, &main_ui);
2423
2424 gdk_threads_enter();
2425 gtk_main();
2426 gdk_threads_leave();
2427 return 0;
2428}