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