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