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