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