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