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