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