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