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