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