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