gfio: make notebooks scrollable if there are too many
[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_container_add(GTK_CONTAINER(win), notebook);
880
881 ge->results_window = win;
882 ge->results_notebook = notebook;
883 return ge->results_notebook;
884}
885
886static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
887 struct group_run_stats *rs)
888{
889 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
890 struct gfio_client *gc = client->client_data;
891
892 gdk_threads_enter();
893
894 res_win = get_results_window(gc->ge);
895
896 scroll = gtk_scrolled_window_new(NULL, NULL);
897 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
898 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
899
900 vbox = gtk_vbox_new(FALSE, 3);
901
902 box = gtk_hbox_new(FALSE, 0);
903 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
904
905 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
906
907 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
908
909 gc->results_widget = vbox;
910
911 entry = new_info_entry_in_frame(box, "Name");
912 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
913 if (strlen(ts->description)) {
914 entry = new_info_entry_in_frame(box, "Description");
915 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
916 }
917 entry = new_info_entry_in_frame(box, "Group ID");
918 entry_set_int_value(entry, ts->groupid);
919 entry = new_info_entry_in_frame(box, "Jobs");
920 entry_set_int_value(entry, ts->members);
921 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
922 entry_set_int_value(entry, ts->error);
923 entry = new_info_entry_in_frame(box, "PID");
924 entry_set_int_value(entry, ts->pid);
925
926 if (ts->io_bytes[DDIR_READ])
927 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
928 if (ts->io_bytes[DDIR_WRITE])
929 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
930
931 gfio_show_latency_buckets(vbox, ts);
932 gfio_show_cpu_usage(vbox, ts);
933 gfio_show_io_depths(vbox, ts);
934
935 gtk_widget_show_all(gc->ge->results_window);
936 gdk_threads_leave();
937}
938
939static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
940{
941 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
942 struct gui *ui = &main_ui;
943 GtkTreeIter iter;
944 struct tm *tm;
945 time_t sec;
946 char tmp[64], timebuf[80];
947
948 sec = p->log_sec;
949 tm = localtime(&sec);
950 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
951 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
952
953 gdk_threads_enter();
954
955 gtk_list_store_append(ui->log_model, &iter);
956 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
957 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
958 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
959 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
960
961 if (p->level == FIO_LOG_ERR)
962 view_log(NULL, (gpointer) ui);
963
964 gdk_threads_leave();
965}
966
967static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
968{
969 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
970 struct gfio_client *gc = client->client_data;
971 GtkWidget *box, *frame, *entry, *vbox;
972 double util;
973 char tmp[16];
974
975 gdk_threads_enter();
976
977 if (!gc->results_widget)
978 goto out;
979
980 if (!gc->disk_util_frame) {
981 gc->disk_util_frame = gtk_frame_new("Disk utilization");
982 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
983 }
984
985 vbox = gtk_vbox_new(FALSE, 3);
986 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
987
988 frame = gtk_frame_new((char *) p->dus.name);
989 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
990
991 box = gtk_vbox_new(FALSE, 3);
992 gtk_container_add(GTK_CONTAINER(frame), box);
993
994 frame = gtk_frame_new("Read");
995 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
996 vbox = gtk_hbox_new(TRUE, 3);
997 gtk_container_add(GTK_CONTAINER(frame), vbox);
998 entry = new_info_entry_in_frame(vbox, "IOs");
999 entry_set_int_value(entry, p->dus.ios[0]);
1000 entry = new_info_entry_in_frame(vbox, "Merges");
1001 entry_set_int_value(entry, p->dus.merges[0]);
1002 entry = new_info_entry_in_frame(vbox, "Sectors");
1003 entry_set_int_value(entry, p->dus.sectors[0]);
1004 entry = new_info_entry_in_frame(vbox, "Ticks");
1005 entry_set_int_value(entry, p->dus.ticks[0]);
1006
1007 frame = gtk_frame_new("Write");
1008 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1009 vbox = gtk_hbox_new(TRUE, 3);
1010 gtk_container_add(GTK_CONTAINER(frame), vbox);
1011 entry = new_info_entry_in_frame(vbox, "IOs");
1012 entry_set_int_value(entry, p->dus.ios[1]);
1013 entry = new_info_entry_in_frame(vbox, "Merges");
1014 entry_set_int_value(entry, p->dus.merges[1]);
1015 entry = new_info_entry_in_frame(vbox, "Sectors");
1016 entry_set_int_value(entry, p->dus.sectors[1]);
1017 entry = new_info_entry_in_frame(vbox, "Ticks");
1018 entry_set_int_value(entry, p->dus.ticks[1]);
1019
1020 frame = gtk_frame_new("Shared");
1021 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1022 vbox = gtk_hbox_new(TRUE, 3);
1023 gtk_container_add(GTK_CONTAINER(frame), vbox);
1024 entry = new_info_entry_in_frame(vbox, "IO ticks");
1025 entry_set_int_value(entry, p->dus.io_ticks);
1026 entry = new_info_entry_in_frame(vbox, "Time in queue");
1027 entry_set_int_value(entry, p->dus.time_in_queue);
1028
1029 util = 0.0;
1030 if (p->dus.msec)
1031 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1032 if (util > 100.0)
1033 util = 100.0;
1034
1035 sprintf(tmp, "%3.2f%%", util);
1036 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1037 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1038
1039 gtk_widget_show_all(gc->results_widget);
1040out:
1041 gdk_threads_leave();
1042}
1043
1044extern int sum_stat_clients;
1045extern struct thread_stat client_ts;
1046extern struct group_run_stats client_gs;
1047
1048static int sum_stat_nr;
1049
1050static void gfio_thread_status_op(struct fio_client *client,
1051 struct fio_net_cmd *cmd)
1052{
1053 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1054
1055 gfio_display_ts(client, &p->ts, &p->rs);
1056
1057 if (sum_stat_clients == 1)
1058 return;
1059
1060 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1061 sum_group_stats(&client_gs, &p->rs);
1062
1063 client_ts.members++;
1064 client_ts.groupid = p->ts.groupid;
1065
1066 if (++sum_stat_nr == sum_stat_clients) {
1067 strcpy(client_ts.name, "All clients");
1068 gfio_display_ts(client, &client_ts, &client_gs);
1069 }
1070}
1071
1072static void gfio_group_stats_op(struct fio_client *client,
1073 struct fio_net_cmd *cmd)
1074{
1075 /* We're ignoring group stats for now */
1076}
1077
1078static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1079 gpointer data)
1080{
1081 struct gfio_graphs *g = data;
1082
1083 g->drawing_area_xdim = w->allocation.width;
1084 g->drawing_area_ydim = w->allocation.height;
1085 return TRUE;
1086}
1087
1088static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1089{
1090 struct gfio_graphs *g = p;
1091 cairo_t *cr;
1092
1093 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1094 g->drawing_area_ydim);
1095 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1096 g->drawing_area_ydim);
1097 cr = gdk_cairo_create(w->window);
1098
1099 cairo_set_source_rgb(cr, 0, 0, 0);
1100
1101 cairo_save(cr);
1102 cairo_translate(cr, 0, 0);
1103 line_graph_draw(g->bandwidth_graph, cr);
1104 cairo_stroke(cr);
1105 cairo_restore(cr);
1106
1107 cairo_save(cr);
1108 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1109 line_graph_draw(g->iops_graph, cr);
1110 cairo_stroke(cr);
1111 cairo_restore(cr);
1112 cairo_destroy(cr);
1113
1114 return FALSE;
1115}
1116
1117/*
1118 * Client specific ETA
1119 */
1120static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
1121{
1122 struct gfio_client *gc = client->client_data;
1123 struct gui_entry *ge = gc->ge;
1124 static int eta_good;
1125 char eta_str[128];
1126 char output[256];
1127 char tmp[32];
1128 double perc = 0.0;
1129 int i2p = 0;
1130
1131 gdk_threads_enter();
1132
1133 eta_str[0] = '\0';
1134 output[0] = '\0';
1135
1136 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1137 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1138 eta_to_str(eta_str, je->eta_sec);
1139 }
1140
1141 sprintf(tmp, "%u", je->nr_running);
1142 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
1143 sprintf(tmp, "%u", je->files_open);
1144 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
1145
1146#if 0
1147 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1148 if (je->m_rate || je->t_rate) {
1149 char *tr, *mr;
1150
1151 mr = num2str(je->m_rate, 4, 0, i2p);
1152 tr = num2str(je->t_rate, 4, 0, i2p);
1153 gtk_entry_set_text(GTK_ENTRY(ge->eta);
1154 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1155 free(tr);
1156 free(mr);
1157 } else if (je->m_iops || je->t_iops)
1158 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1159
1160 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1161 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1162 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1163 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
1164#endif
1165
1166 if (je->eta_sec != INT_MAX && je->nr_running) {
1167 char *iops_str[2];
1168 char *rate_str[2];
1169
1170 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1171 strcpy(output, "-.-% done");
1172 else {
1173 eta_good = 1;
1174 perc *= 100.0;
1175 sprintf(output, "%3.1f%% done", perc);
1176 }
1177
1178 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1179 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1180
1181 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1182 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1183
1184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1185 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1187 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
1188
1189 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1190 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1191 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1192 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1193
1194 free(rate_str[0]);
1195 free(rate_str[1]);
1196 free(iops_str[0]);
1197 free(iops_str[1]);
1198 }
1199
1200 if (eta_str[0]) {
1201 char *dst = output + strlen(output);
1202
1203 sprintf(dst, " - %s", eta_str);
1204 }
1205
1206 gfio_update_thread_status(ge, output, perc);
1207 gdk_threads_leave();
1208}
1209
1210/*
1211 * Update ETA in main window for all clients
1212 */
1213static void gfio_update_all_eta(struct jobs_eta *je)
1214{
1215 struct gui *ui = &main_ui;
1216 static int eta_good;
1217 char eta_str[128];
1218 char output[256];
1219 double perc = 0.0;
1220 int i2p = 0;
1221
1222 gdk_threads_enter();
1223
1224 eta_str[0] = '\0';
1225 output[0] = '\0';
1226
1227 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1228 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1229 eta_to_str(eta_str, je->eta_sec);
1230 }
1231
1232#if 0
1233 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1234 if (je->m_rate || je->t_rate) {
1235 char *tr, *mr;
1236
1237 mr = num2str(je->m_rate, 4, 0, i2p);
1238 tr = num2str(je->t_rate, 4, 0, i2p);
1239 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1240 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1241 free(tr);
1242 free(mr);
1243 } else if (je->m_iops || je->t_iops)
1244 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1245
1246 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1247 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1248 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1249 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1250#endif
1251
1252 if (je->eta_sec != INT_MAX && je->nr_running) {
1253 char *iops_str[2];
1254 char *rate_str[2];
1255
1256 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1257 strcpy(output, "-.-% done");
1258 else {
1259 eta_good = 1;
1260 perc *= 100.0;
1261 sprintf(output, "%3.1f%% done", perc);
1262 }
1263
1264 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1265 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1266
1267 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1268 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1269
1270 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1271 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1272 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1273 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1274
1275 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1276 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1277 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1278 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1279
1280 free(rate_str[0]);
1281 free(rate_str[1]);
1282 free(iops_str[0]);
1283 free(iops_str[1]);
1284 }
1285
1286 if (eta_str[0]) {
1287 char *dst = output + strlen(output);
1288
1289 sprintf(dst, " - %s", eta_str);
1290 }
1291
1292 gfio_update_thread_status_all(output, perc);
1293 gdk_threads_leave();
1294}
1295
1296static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1297{
1298 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
1299 struct gfio_client *gc = client->client_data;
1300 struct gui_entry *ge = gc->ge;
1301 const char *os, *arch;
1302 char buf[64];
1303
1304 os = fio_get_os_string(probe->os);
1305 if (!os)
1306 os = "unknown";
1307
1308 arch = fio_get_arch_string(probe->arch);
1309 if (!arch)
1310 os = "unknown";
1311
1312 if (!client->name)
1313 client->name = strdup((char *) probe->hostname);
1314
1315 gdk_threads_enter();
1316
1317 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1318 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1319 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
1320 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
1321 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
1322
1323 gfio_set_connected(ge, 1);
1324
1325 gdk_threads_leave();
1326}
1327
1328static void gfio_update_thread_status(struct gui_entry *ge,
1329 char *status_message, double perc)
1330{
1331 static char message[100];
1332 const char *m = message;
1333
1334 strncpy(message, status_message, sizeof(message) - 1);
1335 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1336 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1337 gtk_widget_queue_draw(main_ui.window);
1338}
1339
1340static void gfio_update_thread_status_all(char *status_message, double perc)
1341{
1342 struct gui *ui = &main_ui;
1343 static char message[100];
1344 const char *m = message;
1345
1346 strncpy(message, status_message, sizeof(message) - 1);
1347 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1348 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1349 gtk_widget_queue_draw(ui->window);
1350}
1351
1352static void gfio_quit_op(struct fio_client *client)
1353{
1354 struct gfio_client *gc = client->client_data;
1355
1356 gdk_threads_enter();
1357 gfio_set_connected(gc->ge, 0);
1358 gdk_threads_leave();
1359}
1360
1361static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1362{
1363 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
1364 struct gfio_client *gc = client->client_data;
1365 struct thread_options *o = &gc->o;
1366 struct gui_entry *ge = gc->ge;
1367 char tmp[8];
1368
1369 convert_thread_options_to_cpu(o, &p->top);
1370
1371 gdk_threads_enter();
1372
1373 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1374
1375 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1376 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1377 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
1378
1379 sprintf(tmp, "%u", o->iodepth);
1380 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
1381
1382 gc->job_added++;
1383
1384 gdk_threads_leave();
1385}
1386
1387static void gfio_client_timed_out(struct fio_client *client)
1388{
1389 struct gfio_client *gc = client->client_data;
1390 GtkWidget *dialog, *label, *content;
1391 char buf[256];
1392
1393 gdk_threads_enter();
1394
1395 gfio_set_connected(gc->ge, 0);
1396 clear_ge_ui_info(gc->ge);
1397
1398 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1399
1400 dialog = gtk_dialog_new_with_buttons("Timed out!",
1401 GTK_WINDOW(main_ui.window),
1402 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1403 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1404
1405 /* gtk_dialog_get_content_area() is 2.14 and newer */
1406 content = GTK_DIALOG(dialog)->vbox;
1407
1408 label = gtk_label_new((const gchar *) buf);
1409 gtk_container_add(GTK_CONTAINER(content), label);
1410 gtk_widget_show_all(dialog);
1411 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1412
1413 gtk_dialog_run(GTK_DIALOG(dialog));
1414 gtk_widget_destroy(dialog);
1415
1416 gdk_threads_leave();
1417}
1418
1419static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1420{
1421 struct gfio_client *gc = client->client_data;
1422
1423 gdk_threads_enter();
1424
1425 gfio_set_connected(gc->ge, 0);
1426
1427 if (gc->err_entry)
1428 entry_set_int_value(gc->err_entry, client->error);
1429
1430 gdk_threads_leave();
1431}
1432
1433struct client_ops gfio_client_ops = {
1434 .text_op = gfio_text_op,
1435 .disk_util = gfio_disk_util_op,
1436 .thread_status = gfio_thread_status_op,
1437 .group_stats = gfio_group_stats_op,
1438 .jobs_eta = gfio_update_client_eta,
1439 .eta = gfio_update_all_eta,
1440 .probe = gfio_probe_op,
1441 .quit = gfio_quit_op,
1442 .add_job = gfio_add_job_op,
1443 .timed_out = gfio_client_timed_out,
1444 .stop = gfio_client_stop,
1445 .stay_connected = 1,
1446};
1447
1448static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1449 __attribute__((unused)) gpointer data)
1450{
1451 gtk_main_quit();
1452}
1453
1454static void *job_thread(void *arg)
1455{
1456 struct gui *ui = arg;
1457
1458 ui->handler_running = 1;
1459 fio_handle_clients(&gfio_client_ops);
1460 ui->handler_running = 0;
1461 return NULL;
1462}
1463
1464static int send_job_files(struct gui_entry *ge)
1465{
1466 struct gfio_client *gc = ge->client;
1467 int i, ret = 0;
1468
1469 for (i = 0; i < ge->nr_job_files; i++) {
1470 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
1471 if (ret < 0) {
1472 GError *error;
1473
1474 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1475 report_error(error);
1476 g_error_free(error);
1477 break;
1478 } else if (ret)
1479 break;
1480
1481 free(ge->job_files[i]);
1482 ge->job_files[i] = NULL;
1483 }
1484 while (i < ge->nr_job_files) {
1485 free(ge->job_files[i]);
1486 ge->job_files[i] = NULL;
1487 i++;
1488 }
1489
1490 return ret;
1491}
1492
1493static void *server_thread(void *arg)
1494{
1495 is_backend = 1;
1496 gfio_server_running = 1;
1497 fio_start_server(NULL);
1498 gfio_server_running = 0;
1499 return NULL;
1500}
1501
1502static void gfio_start_server(void)
1503{
1504 struct gui *ui = &main_ui;
1505
1506 if (!gfio_server_running) {
1507 gfio_server_running = 1;
1508 pthread_create(&ui->server_t, NULL, server_thread, NULL);
1509 pthread_detach(ui->server_t);
1510 }
1511}
1512
1513static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1514 gpointer data)
1515{
1516 struct gui_entry *ge = data;
1517 struct gfio_client *gc = ge->client;
1518
1519 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1520 fio_start_client(gc->client);
1521}
1522
1523static void file_open(GtkWidget *w, gpointer data);
1524
1525static void connect_clicked(GtkWidget *widget, gpointer data)
1526{
1527 struct gui_entry *ge = data;
1528 struct gfio_client *gc = ge->client;
1529
1530 if (!ge->connected) {
1531 int ret;
1532
1533 if (!ge->nr_job_files)
1534 file_open(widget, data);
1535 if (!ge->nr_job_files)
1536 return;
1537
1538 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1539 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1540 ret = fio_client_connect(gc->client);
1541 if (!ret) {
1542 if (!ge->ui->handler_running)
1543 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
1544 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1545 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
1546 } else {
1547 GError *error;
1548
1549 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1550 report_error(error);
1551 g_error_free(error);
1552 }
1553 } else {
1554 fio_client_terminate(gc->client);
1555 gfio_set_connected(ge, 0);
1556 clear_ge_ui_info(ge);
1557 }
1558}
1559
1560static void send_clicked(GtkWidget *widget, gpointer data)
1561{
1562 struct gui_entry *ge = data;
1563
1564 if (send_job_files(ge)) {
1565 GError *error;
1566
1567 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);
1568 report_error(error);
1569 g_error_free(error);
1570
1571 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1572 }
1573
1574 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1575 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
1576}
1577
1578static GtkWidget *add_button(GtkWidget *buttonbox,
1579 struct button_spec *buttonspec, gpointer data)
1580{
1581 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1582
1583 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1584 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1585 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1586 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1587
1588 return button;
1589}
1590
1591static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1592 int nbuttons)
1593{
1594 int i;
1595
1596 for (i = 0; i < nbuttons; i++)
1597 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
1598}
1599
1600static void on_info_bar_response(GtkWidget *widget, gint response,
1601 gpointer data)
1602{
1603 struct gui *ui = &main_ui;
1604
1605 if (response == GTK_RESPONSE_OK) {
1606 gtk_widget_destroy(widget);
1607 ui->error_info_bar = NULL;
1608 }
1609}
1610
1611void report_error(GError *error)
1612{
1613 struct gui *ui = &main_ui;
1614
1615 if (ui->error_info_bar == NULL) {
1616 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1617 GTK_RESPONSE_OK,
1618 NULL);
1619 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1620 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
1621 GTK_MESSAGE_ERROR);
1622
1623 ui->error_label = gtk_label_new(error->message);
1624 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1625 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
1626
1627 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1628 gtk_widget_show_all(ui->vbox);
1629 } else {
1630 char buffer[256];
1631 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1632 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
1633 }
1634}
1635
1636struct connection_widgets
1637{
1638 GtkWidget *hentry;
1639 GtkWidget *combo;
1640 GtkWidget *button;
1641};
1642
1643static void hostname_cb(GtkEntry *entry, gpointer data)
1644{
1645 struct connection_widgets *cw = data;
1646 int uses_net = 0, is_localhost = 0;
1647 const gchar *text;
1648 gchar *ctext;
1649
1650 /*
1651 * Check whether to display the 'auto start backend' box
1652 * or not. Show it if we are a localhost and using network,
1653 * or using a socket.
1654 */
1655 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1656 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1657 uses_net = 1;
1658 g_free(ctext);
1659
1660 if (uses_net) {
1661 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1662 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1663 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1664 !strcmp(text, "ip6-loopback"))
1665 is_localhost = 1;
1666 }
1667
1668 if (!uses_net || is_localhost) {
1669 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1670 gtk_widget_set_sensitive(cw->button, 1);
1671 } else {
1672 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1673 gtk_widget_set_sensitive(cw->button, 0);
1674 }
1675}
1676
1677static int get_connection_details(char **host, int *port, int *type,
1678 int *server_start)
1679{
1680 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1681 struct connection_widgets cw;
1682 char *typeentry;
1683
1684 dialog = gtk_dialog_new_with_buttons("Connection details",
1685 GTK_WINDOW(main_ui.window),
1686 GTK_DIALOG_DESTROY_WITH_PARENT,
1687 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1688 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1689
1690 frame = gtk_frame_new("Hostname / socket name");
1691 /* gtk_dialog_get_content_area() is 2.14 and newer */
1692 vbox = GTK_DIALOG(dialog)->vbox;
1693 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1694
1695 box = gtk_vbox_new(FALSE, 6);
1696 gtk_container_add(GTK_CONTAINER(frame), box);
1697
1698 hbox = gtk_hbox_new(TRUE, 10);
1699 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1700 cw.hentry = gtk_entry_new();
1701 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1702 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
1703
1704 frame = gtk_frame_new("Port");
1705 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1706 box = gtk_vbox_new(FALSE, 10);
1707 gtk_container_add(GTK_CONTAINER(frame), box);
1708
1709 hbox = gtk_hbox_new(TRUE, 4);
1710 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1711 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1712
1713 frame = gtk_frame_new("Type");
1714 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1715 box = gtk_vbox_new(FALSE, 10);
1716 gtk_container_add(GTK_CONTAINER(frame), box);
1717
1718 hbox = gtk_hbox_new(TRUE, 4);
1719 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1720
1721 cw.combo = gtk_combo_box_new_text();
1722 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1723 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1724 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1725 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
1726
1727 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
1728
1729 frame = gtk_frame_new("Options");
1730 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1731 box = gtk_vbox_new(FALSE, 10);
1732 gtk_container_add(GTK_CONTAINER(frame), box);
1733
1734 hbox = gtk_hbox_new(TRUE, 4);
1735 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1736
1737 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1738 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1739 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.");
1740 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1741
1742 /*
1743 * Connect edit signal, so we can show/not-show the auto start button
1744 */
1745 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1746 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
1747
1748 gtk_widget_show_all(dialog);
1749
1750 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1751 gtk_widget_destroy(dialog);
1752 return 1;
1753 }
1754
1755 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
1756 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1757
1758 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
1759 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1760 *type = Fio_client_ipv4;
1761 else if (!strncmp(typeentry, "IPv6", 4))
1762 *type = Fio_client_ipv6;
1763 else
1764 *type = Fio_client_socket;
1765 g_free(typeentry);
1766
1767 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
1768
1769 gtk_widget_destroy(dialog);
1770 return 0;
1771}
1772
1773static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
1774{
1775 struct gfio_client *gc;
1776
1777 gc = malloc(sizeof(*gc));
1778 memset(gc, 0, sizeof(*gc));
1779 gc->ge = ge;
1780 gc->client = fio_get_client(client);
1781
1782 ge->client = gc;
1783
1784 client->client_data = gc;
1785}
1786
1787static GtkWidget *new_client_page(struct gui_entry *ge);
1788
1789static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1790{
1791 struct gui_entry *ge;
1792
1793 ge = malloc(sizeof(*ge));
1794 memset(ge, 0, sizeof(*ge));
1795 INIT_FLIST_HEAD(&ge->list);
1796 flist_add_tail(&ge->list, &ui->list);
1797 ge->ui = ui;
1798 return ge;
1799}
1800
1801/*
1802 * FIXME: need more handling here
1803 */
1804static void ge_destroy(GtkWidget *w, gpointer data)
1805{
1806 struct gui_entry *ge = data;
1807 struct gfio_client *gc = ge->client;
1808
1809 if (gc->client)
1810 fio_put_client(gc->client);
1811
1812 flist_del(&ge->list);
1813 free(ge);
1814}
1815
1816static struct gui_entry *get_new_ge_with_tab(const char *name)
1817{
1818 struct gui_entry *ge;
1819
1820 ge = alloc_new_gui_entry(&main_ui);
1821
1822 ge->vbox = new_client_page(ge);
1823 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1824
1825 ge->page_label = gtk_label_new(name);
1826 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1827
1828 gtk_widget_show_all(main_ui.window);
1829 return ge;
1830}
1831
1832static void file_new(GtkWidget *w, gpointer data)
1833{
1834 get_new_ge_with_tab("Untitled");
1835}
1836
1837/*
1838 * Return the 'ge' corresponding to the tab. If the active tab is the
1839 * main tab, open a new tab.
1840 */
1841static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1842{
1843 struct flist_head *entry;
1844 struct gui_entry *ge;
1845
1846 if (!cur_page)
1847 return get_new_ge_with_tab("Untitled");
1848
1849 flist_for_each(entry, &main_ui.list) {
1850 ge = flist_entry(entry, struct gui_entry, list);
1851 if (ge->page_num == cur_page)
1852 return ge;
1853 }
1854
1855 return NULL;
1856}
1857
1858static void file_open(GtkWidget *w, gpointer data)
1859{
1860 struct gui *ui = data;
1861 GtkWidget *dialog;
1862 GSList *filenames, *fn_glist;
1863 GtkFileFilter *filter;
1864 char *host;
1865 int port, type, server_start;
1866 struct gui_entry *ge;
1867 gint cur_page;
1868
1869 /*
1870 * Creates new tab if current tab is the main window, or the
1871 * current tab already has a client.
1872 */
1873 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1874 ge = get_ge_from_page(cur_page);
1875 if (ge->client)
1876 ge = get_new_ge_with_tab("Untitled");
1877
1878 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
1879
1880 dialog = gtk_file_chooser_dialog_new("Open File",
1881 GTK_WINDOW(ui->window),
1882 GTK_FILE_CHOOSER_ACTION_OPEN,
1883 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1884 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1885 NULL);
1886 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1887
1888 filter = gtk_file_filter_new();
1889 gtk_file_filter_add_pattern(filter, "*.fio");
1890 gtk_file_filter_add_pattern(filter, "*.job");
1891 gtk_file_filter_add_pattern(filter, "*.ini");
1892 gtk_file_filter_add_mime_type(filter, "text/fio");
1893 gtk_file_filter_set_name(filter, "Fio job file");
1894 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1895
1896 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1897 gtk_widget_destroy(dialog);
1898 return;
1899 }
1900
1901 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
1902
1903 gtk_widget_destroy(dialog);
1904
1905 if (get_connection_details(&host, &port, &type, &server_start))
1906 goto err;
1907
1908 filenames = fn_glist;
1909 while (filenames != NULL) {
1910 struct fio_client *client;
1911
1912 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1913 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1914 ge->nr_job_files++;
1915
1916 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1917 if (!client) {
1918 GError *error;
1919
1920 error = g_error_new(g_quark_from_string("fio"), 1,
1921 "Failed to add client %s", host);
1922 report_error(error);
1923 g_error_free(error);
1924 }
1925 gfio_client_added(ge, client);
1926
1927 g_free(filenames->data);
1928 filenames = g_slist_next(filenames);
1929 }
1930 free(host);
1931
1932 if (server_start)
1933 gfio_start_server();
1934err:
1935 g_slist_free(fn_glist);
1936}
1937
1938static void file_save(GtkWidget *w, gpointer data)
1939{
1940 struct gui *ui = data;
1941 GtkWidget *dialog;
1942
1943 dialog = gtk_file_chooser_dialog_new("Save File",
1944 GTK_WINDOW(ui->window),
1945 GTK_FILE_CHOOSER_ACTION_SAVE,
1946 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1947 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1948 NULL);
1949
1950 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1951 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1952
1953 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1954 char *filename;
1955
1956 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1957 // save_job_file(filename);
1958 g_free(filename);
1959 }
1960 gtk_widget_destroy(dialog);
1961}
1962
1963static void view_log_destroy(GtkWidget *w, gpointer data)
1964{
1965 struct gui *ui = (struct gui *) data;
1966
1967 gtk_widget_ref(ui->log_tree);
1968 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1969 gtk_widget_destroy(w);
1970 ui->log_view = NULL;
1971}
1972
1973static void view_log(GtkWidget *w, gpointer data)
1974{
1975 GtkWidget *win, *scroll, *vbox, *box;
1976 struct gui *ui = (struct gui *) data;
1977
1978 if (ui->log_view)
1979 return;
1980
1981 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1982 gtk_window_set_title(GTK_WINDOW(win), "Log");
1983 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
1984
1985 scroll = gtk_scrolled_window_new(NULL, NULL);
1986
1987 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1988
1989 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1990
1991 box = gtk_hbox_new(TRUE, 0);
1992 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1993 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1994 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1995
1996 vbox = gtk_vbox_new(TRUE, 5);
1997 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1998
1999 gtk_container_add(GTK_CONTAINER(win), vbox);
2000 gtk_widget_show_all(win);
2001}
2002
2003static void preferences(GtkWidget *w, gpointer data)
2004{
2005 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
2006 int i;
2007
2008 dialog = gtk_dialog_new_with_buttons("Preferences",
2009 GTK_WINDOW(main_ui.window),
2010 GTK_DIALOG_DESTROY_WITH_PARENT,
2011 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2012 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2013 NULL);
2014
2015 frame = gtk_frame_new("Debug logging");
2016 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2017
2018 vbox = gtk_vbox_new(FALSE, 6);
2019 gtk_container_add(GTK_CONTAINER(frame), vbox);
2020
2021 box = gtk_hbox_new(FALSE, 6);
2022 gtk_container_add(GTK_CONTAINER(vbox), box);
2023
2024 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2025
2026 for (i = 0; i < FD_DEBUG_MAX; i++) {
2027 if (i == 7) {
2028 box = gtk_hbox_new(FALSE, 6);
2029 gtk_container_add(GTK_CONTAINER(vbox), box);
2030 }
2031
2032
2033 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2034 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2035 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2036 }
2037
2038 frame = gtk_frame_new("Graph font");
2039 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2040 vbox = gtk_vbox_new(FALSE, 6);
2041 gtk_container_add(GTK_CONTAINER(frame), vbox);
2042
2043 font = gtk_font_button_new();
2044 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2045
2046 gtk_widget_show_all(dialog);
2047
2048 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2049 gtk_widget_destroy(dialog);
2050 return;
2051 }
2052
2053 for (i = 0; i < FD_DEBUG_MAX; i++) {
2054 int set;
2055
2056 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2057 if (set)
2058 fio_debug |= (1UL << i);
2059 }
2060
2061 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
2062 gtk_widget_destroy(dialog);
2063}
2064
2065static void about_dialog(GtkWidget *w, gpointer data)
2066{
2067 const char *authors[] = {
2068 "Jens Axboe <axboe@kernel.dk>",
2069 "Stephen Carmeron <stephenmcameron@gmail.com>",
2070 NULL
2071 };
2072 const char *license[] = {
2073 "Fio is free software; you can redistribute it and/or modify "
2074 "it under the terms of the GNU General Public License as published by "
2075 "the Free Software Foundation; either version 2 of the License, or "
2076 "(at your option) any later version.\n",
2077 "Fio is distributed in the hope that it will be useful, "
2078 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2079 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2080 "GNU General Public License for more details.\n",
2081 "You should have received a copy of the GNU General Public License "
2082 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2083 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2084 };
2085 char *license_trans;
2086
2087 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2088 license[2], "\n", NULL);
2089
2090 gtk_show_about_dialog(NULL,
2091 "program-name", "gfio",
2092 "comments", "Gtk2 UI for fio",
2093 "license", license_trans,
2094 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2095 "authors", authors,
2096 "version", fio_version_string,
2097 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
2098 "logo-icon-name", "fio",
2099 /* Must be last: */
2100 "wrap-license", TRUE,
2101 NULL);
2102
2103 g_free(license_trans);
2104}
2105
2106static GtkActionEntry menu_items[] = {
2107 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
2108 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
2109 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2110 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
2111 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2112 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2113 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
2114 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
2115 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2116 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
2117};
2118static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
2119
2120static const gchar *ui_string = " \
2121 <ui> \
2122 <menubar name=\"MainMenu\"> \
2123 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2124 <menuitem name=\"New\" action=\"NewFile\" /> \
2125 <separator name=\"Separator1\"/> \
2126 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2127 <menuitem name=\"Save\" action=\"SaveFile\" /> \
2128 <separator name=\"Separator2\"/> \
2129 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2130 <separator name=\"Separator3\"/> \
2131 <menuitem name=\"Quit\" action=\"Quit\" /> \
2132 </menu> \
2133 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2134 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2135 </menu>\
2136 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2137 <menuitem name=\"About\" action=\"About\" /> \
2138 </menu> \
2139 </menubar> \
2140 </ui> \
2141";
2142
2143static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2144 struct gui *ui)
2145{
2146 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2147 GError *error = 0;
2148
2149 action_group = gtk_action_group_new("Menu");
2150 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
2151
2152 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2153 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2154
2155 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2156 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2157}
2158
2159void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2160 GtkWidget *vbox, GtkUIManager *ui_manager)
2161{
2162 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2163}
2164
2165static GtkWidget *new_client_page(struct gui_entry *ge)
2166{
2167 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2168 GdkColor white;
2169
2170 main_vbox = gtk_vbox_new(FALSE, 3);
2171
2172 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2173 ge->topvbox = gtk_vbox_new(FALSE, 3);
2174 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2175 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2176
2177 probe = gtk_frame_new("Job");
2178 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2179 probe_frame = gtk_vbox_new(FALSE, 3);
2180 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2181
2182 probe_box = gtk_hbox_new(FALSE, 3);
2183 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2184 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2185 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2186 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2187 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2188
2189 probe_box = gtk_hbox_new(FALSE, 3);
2190 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2191
2192 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2193 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2194 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2195 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2196 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2197 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2198
2199 probe_box = gtk_hbox_new(FALSE, 3);
2200 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2201 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2202 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2203 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2204 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2205
2206 /*
2207 * Only add this if we have a commit rate
2208 */
2209#if 0
2210 probe_box = gtk_hbox_new(FALSE, 3);
2211 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2212
2213 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2214 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2215
2216 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2217 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2218#endif
2219
2220 /*
2221 * Set up a drawing area and IOPS and bandwidth graphs
2222 */
2223 gdk_color_parse("white", &white);
2224 ge->graphs.drawing_area = gtk_drawing_area_new();
2225 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2226 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2227 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2228 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2229 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2230 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2231 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2232 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2233 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2234 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2235 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2236 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2237 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2238 ge->graphs.drawing_area);
2239 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2240 TRUE, TRUE, 0);
2241
2242 setup_graphs(&ge->graphs);
2243
2244 /*
2245 * Set up alignments for widgets at the bottom of ui,
2246 * align bottom left, expand horizontally but not vertically
2247 */
2248 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2249 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2250 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2251 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2252 FALSE, FALSE, 0);
2253
2254 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2255
2256 /*
2257 * Set up thread status progress bar
2258 */
2259 ge->thread_status_pb = gtk_progress_bar_new();
2260 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2261 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2262 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2263
2264
2265 return main_vbox;
2266}
2267
2268static GtkWidget *new_main_page(struct gui *ui)
2269{
2270 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2271 GdkColor white;
2272
2273 main_vbox = gtk_vbox_new(FALSE, 3);
2274
2275 /*
2276 * Set up alignments for widgets at the top of ui,
2277 * align top left, expand horizontally but not vertically
2278 */
2279 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2280 ui->topvbox = gtk_vbox_new(FALSE, 0);
2281 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2282 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2283
2284 probe = gtk_frame_new("Run statistics");
2285 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2286 probe_frame = gtk_vbox_new(FALSE, 3);
2287 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2288
2289 probe_box = gtk_hbox_new(FALSE, 3);
2290 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2291 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2292 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2293 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2294 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2295
2296 /*
2297 * Only add this if we have a commit rate
2298 */
2299#if 0
2300 probe_box = gtk_hbox_new(FALSE, 3);
2301 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2302
2303 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2304 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2305
2306 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2307 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2308#endif
2309
2310 /*
2311 * Set up a drawing area and IOPS and bandwidth graphs
2312 */
2313 gdk_color_parse("white", &white);
2314 ui->graphs.drawing_area = gtk_drawing_area_new();
2315 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2316 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2317 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2318 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2319 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2320 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2321 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2322 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2323 G_CALLBACK(on_config_drawing_area), &ui->graphs);
2324 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2325 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2326 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2327 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2328 ui->graphs.drawing_area);
2329 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
2330 TRUE, TRUE, 0);
2331
2332 setup_graphs(&ui->graphs);
2333
2334 /*
2335 * Set up alignments for widgets at the bottom of ui,
2336 * align bottom left, expand horizontally but not vertically
2337 */
2338 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2339 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2340 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2341 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
2342 FALSE, FALSE, 0);
2343
2344 /*
2345 * Set up thread status progress bar
2346 */
2347 ui->thread_status_pb = gtk_progress_bar_new();
2348 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
2349 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
2350 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2351
2352 return main_vbox;
2353}
2354
2355static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2356 guint page, gpointer data)
2357
2358{
2359 return TRUE;
2360}
2361
2362static void init_ui(int *argc, char **argv[], struct gui *ui)
2363{
2364 GtkSettings *settings;
2365 GtkUIManager *uimanager;
2366 GtkWidget *menu, *vbox;
2367
2368 /* Magical g*thread incantation, you just need this thread stuff.
2369 * Without it, the update that happens in gfio_update_thread_status
2370 * doesn't really happen in a timely fashion, you need expose events
2371 */
2372 if (!g_thread_supported())
2373 g_thread_init(NULL);
2374 gdk_threads_init();
2375
2376 gtk_init(argc, argv);
2377 settings = gtk_settings_get_default();
2378 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2379 g_type_init();
2380
2381 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2382 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2383 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2384
2385 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2386 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2387
2388 ui->vbox = gtk_vbox_new(FALSE, 0);
2389 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2390
2391 uimanager = gtk_ui_manager_new();
2392 menu = get_menubar_menu(ui->window, uimanager, ui);
2393 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2394
2395 ui->notebook = gtk_notebook_new();
2396 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2397 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
2398 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2399
2400 vbox = new_main_page(ui);
2401
2402 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2403
2404 gfio_ui_setup_log(ui);
2405
2406 gtk_widget_show_all(ui->window);
2407}
2408
2409int main(int argc, char *argv[], char *envp[])
2410{
2411 if (initialize_fio(envp))
2412 return 1;
2413 if (fio_init_options())
2414 return 1;
2415
2416 memset(&main_ui, 0, sizeof(main_ui));
2417 INIT_FLIST_HEAD(&main_ui.list);
2418
2419 init_ui(&argc, &argv, &main_ui);
2420
2421 gdk_threads_enter();
2422 gtk_main();
2423 gdk_threads_leave();
2424 return 0;
2425}