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