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