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