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