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