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