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