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