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