gfio: add graph axis unit change notification callbacks
[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,
6433ee05 1467 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
3ec62ec4 1468 .stay_connected = 1,
a1820207
SC
1469};
1470
ff1f3280
SC
1471static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1472 __attribute__((unused)) gpointer data)
1473{
1474 gtk_main_quit();
1475}
1476
25927259
SC
1477static void *job_thread(void *arg)
1478{
a9eccde4
JA
1479 struct gui *ui = arg;
1480
1481 ui->handler_running = 1;
25927259 1482 fio_handle_clients(&gfio_client_ops);
a9eccde4 1483 ui->handler_running = 0;
25927259
SC
1484 return NULL;
1485}
1486
2f99deb0 1487static int send_job_files(struct gui_entry *ge)
60f6b330 1488{
9988ca70 1489 struct gfio_client *gc = ge->client;
441013b4 1490 int i, ret = 0;
0420ba6a 1491
2f99deb0 1492 for (i = 0; i < ge->nr_job_files; i++) {
9988ca70 1493 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
c724926b
JA
1494 if (ret < 0) {
1495 GError *error;
1496
1497 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1498 report_error(error);
1499 g_error_free(error);
1500 break;
1501 } else if (ret)
441013b4
JA
1502 break;
1503
2f99deb0
JA
1504 free(ge->job_files[i]);
1505 ge->job_files[i] = NULL;
441013b4 1506 }
2f99deb0
JA
1507 while (i < ge->nr_job_files) {
1508 free(ge->job_files[i]);
1509 ge->job_files[i] = NULL;
441013b4 1510 i++;
0420ba6a
JA
1511 }
1512
441013b4 1513 return ret;
60f6b330
SC
1514}
1515
63a130b7
JA
1516static void *server_thread(void *arg)
1517{
1518 is_backend = 1;
1519 gfio_server_running = 1;
1520 fio_start_server(NULL);
1521 gfio_server_running = 0;
1522 return NULL;
1523}
1524
2f99deb0 1525static void gfio_start_server(void)
63a130b7 1526{
2f99deb0
JA
1527 struct gui *ui = &main_ui;
1528
63a130b7
JA
1529 if (!gfio_server_running) {
1530 gfio_server_running = 1;
1531 pthread_create(&ui->server_t, NULL, server_thread, NULL);
e34f6ad7 1532 pthread_detach(ui->server_t);
63a130b7
JA
1533 }
1534}
1535
f3074008 1536static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 1537 gpointer data)
f3074008 1538{
2f99deb0
JA
1539 struct gui_entry *ge = data;
1540 struct gfio_client *gc = ge->client;
25927259 1541
2f99deb0
JA
1542 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1543 fio_start_client(gc->client);
f3074008
SC
1544}
1545
df06f220
JA
1546static void file_open(GtkWidget *w, gpointer data);
1547
1548static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 1549{
2f99deb0
JA
1550 struct gui_entry *ge = data;
1551 struct gfio_client *gc = ge->client;
3ec62ec4 1552
2f99deb0 1553 if (!ge->connected) {
c724926b
JA
1554 int ret;
1555
2f99deb0 1556 if (!ge->nr_job_files)
df06f220 1557 file_open(widget, data);
2f99deb0
JA
1558 if (!ge->nr_job_files)
1559 return;
1560
1561 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1562 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
c724926b
JA
1563 ret = fio_client_connect(gc->client);
1564 if (!ret) {
a9eccde4
JA
1565 if (!ge->ui->handler_running)
1566 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2f99deb0
JA
1567 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1568 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
c724926b
JA
1569 } else {
1570 GError *error;
1571
1572 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1573 report_error(error);
1574 g_error_free(error);
69406b92 1575 }
df06f220 1576 } else {
2f99deb0
JA
1577 fio_client_terminate(gc->client);
1578 gfio_set_connected(ge, 0);
1579 clear_ge_ui_info(ge);
df06f220 1580 }
3e47bd25
JA
1581}
1582
b9d2f30a
JA
1583static void send_clicked(GtkWidget *widget, gpointer data)
1584{
2f99deb0 1585 struct gui_entry *ge = data;
b9d2f30a 1586
2f99deb0 1587 if (send_job_files(ge)) {
c724926b
JA
1588 GError *error;
1589
1590 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);
1591 report_error(error);
1592 g_error_free(error);
1593
2f99deb0 1594 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
b9d2f30a
JA
1595 }
1596
2f99deb0
JA
1597 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1598 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
b9d2f30a
JA
1599}
1600
2f99deb0
JA
1601static GtkWidget *add_button(GtkWidget *buttonbox,
1602 struct button_spec *buttonspec, gpointer data)
f3074008 1603{
2f99deb0
JA
1604 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1605
1606 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1607 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1608 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1609 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1610
1611 return button;
f3074008
SC
1612}
1613
2f99deb0
JA
1614static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1615 int nbuttons)
f3074008
SC
1616{
1617 int i;
1618
f3074008 1619 for (i = 0; i < nbuttons; i++)
2f99deb0 1620 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
f3074008
SC
1621}
1622
0420ba6a
JA
1623static void on_info_bar_response(GtkWidget *widget, gint response,
1624 gpointer data)
1625{
2f99deb0
JA
1626 struct gui *ui = &main_ui;
1627
0420ba6a
JA
1628 if (response == GTK_RESPONSE_OK) {
1629 gtk_widget_destroy(widget);
2f99deb0 1630 ui->error_info_bar = NULL;
0420ba6a
JA
1631 }
1632}
1633
df06f220 1634void report_error(GError *error)
0420ba6a 1635{
2f99deb0
JA
1636 struct gui *ui = &main_ui;
1637
1638 if (ui->error_info_bar == NULL) {
1639 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
0420ba6a
JA
1640 GTK_RESPONSE_OK,
1641 NULL);
2f99deb0
JA
1642 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1643 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
0420ba6a
JA
1644 GTK_MESSAGE_ERROR);
1645
2f99deb0
JA
1646 ui->error_label = gtk_label_new(error->message);
1647 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1648 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
0420ba6a 1649
2f99deb0
JA
1650 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1651 gtk_widget_show_all(ui->vbox);
0420ba6a
JA
1652 } else {
1653 char buffer[256];
1654 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2f99deb0 1655 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
0420ba6a
JA
1656 }
1657}
1658
62bc937f
JA
1659struct connection_widgets
1660{
1661 GtkWidget *hentry;
1662 GtkWidget *combo;
1663 GtkWidget *button;
1664};
1665
1666static void hostname_cb(GtkEntry *entry, gpointer data)
1667{
1668 struct connection_widgets *cw = data;
1669 int uses_net = 0, is_localhost = 0;
1670 const gchar *text;
1671 gchar *ctext;
1672
1673 /*
1674 * Check whether to display the 'auto start backend' box
1675 * or not. Show it if we are a localhost and using network,
1676 * or using a socket.
1677 */
1678 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1679 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1680 uses_net = 1;
1681 g_free(ctext);
1682
1683 if (uses_net) {
1684 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1685 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1686 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1687 !strcmp(text, "ip6-loopback"))
1688 is_localhost = 1;
1689 }
1690
1691 if (!uses_net || is_localhost) {
1692 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1693 gtk_widget_set_sensitive(cw->button, 1);
1694 } else {
1695 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1696 gtk_widget_set_sensitive(cw->button, 0);
1697 }
1698}
1699
b9f3c7ed
JA
1700static int get_connection_details(char **host, int *port, int *type,
1701 int *server_start)
a7a42ce1 1702{
62bc937f
JA
1703 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1704 struct connection_widgets cw;
a7a42ce1
JA
1705 char *typeentry;
1706
1707 dialog = gtk_dialog_new_with_buttons("Connection details",
2f99deb0 1708 GTK_WINDOW(main_ui.window),
a7a42ce1
JA
1709 GTK_DIALOG_DESTROY_WITH_PARENT,
1710 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1711 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1712
1713 frame = gtk_frame_new("Hostname / socket name");
f129909e
JA
1714 /* gtk_dialog_get_content_area() is 2.14 and newer */
1715 vbox = GTK_DIALOG(dialog)->vbox;
a7a42ce1
JA
1716 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1717
1718 box = gtk_vbox_new(FALSE, 6);
1719 gtk_container_add(GTK_CONTAINER(frame), box);
1720
1721 hbox = gtk_hbox_new(TRUE, 10);
1722 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
62bc937f
JA
1723 cw.hentry = gtk_entry_new();
1724 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1725 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
a7a42ce1
JA
1726
1727 frame = gtk_frame_new("Port");
1728 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1729 box = gtk_vbox_new(FALSE, 10);
1730 gtk_container_add(GTK_CONTAINER(frame), box);
1731
1732 hbox = gtk_hbox_new(TRUE, 4);
1733 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1734 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1735
1736 frame = gtk_frame_new("Type");
1737 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1738 box = gtk_vbox_new(FALSE, 10);
1739 gtk_container_add(GTK_CONTAINER(frame), box);
1740
1741 hbox = gtk_hbox_new(TRUE, 4);
1742 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1743
62bc937f
JA
1744 cw.combo = gtk_combo_box_new_text();
1745 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1746 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1747 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1748 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
a7a42ce1 1749
62bc937f 1750 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
a7a42ce1 1751
b9f3c7ed
JA
1752 frame = gtk_frame_new("Options");
1753 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1754 box = gtk_vbox_new(FALSE, 10);
1755 gtk_container_add(GTK_CONTAINER(frame), box);
1756
1757 hbox = gtk_hbox_new(TRUE, 4);
1758 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1759
62bc937f
JA
1760 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1761 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1762 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.");
1763 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1764
1765 /*
1766 * Connect edit signal, so we can show/not-show the auto start button
1767 */
1768 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1769 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
b9f3c7ed 1770
a7a42ce1
JA
1771 gtk_widget_show_all(dialog);
1772
1773 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1774 gtk_widget_destroy(dialog);
1775 return 1;
1776 }
1777
62bc937f 1778 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
a7a42ce1
JA
1779 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1780
62bc937f 1781 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
a7a42ce1
JA
1782 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1783 *type = Fio_client_ipv4;
1784 else if (!strncmp(typeentry, "IPv6", 4))
1785 *type = Fio_client_ipv6;
1786 else
1787 *type = Fio_client_socket;
1788 g_free(typeentry);
1789
62bc937f 1790 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
b9f3c7ed 1791
a7a42ce1
JA
1792 gtk_widget_destroy(dialog);
1793 return 0;
1794}
1795
2f99deb0 1796static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
e0681f3e
JA
1797{
1798 struct gfio_client *gc;
1799
1800 gc = malloc(sizeof(*gc));
1801 memset(gc, 0, sizeof(*gc));
2f99deb0 1802 gc->ge = ge;
343cb4a9 1803 gc->client = fio_get_client(client);
b9d2f30a 1804
2f99deb0 1805 ge->client = gc;
e0681f3e
JA
1806
1807 client->client_data = gc;
1808}
1809
2f99deb0
JA
1810static GtkWidget *new_client_page(struct gui_entry *ge);
1811
1812static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1813{
1814 struct gui_entry *ge;
1815
1816 ge = malloc(sizeof(*ge));
1817 memset(ge, 0, sizeof(*ge));
1818 INIT_FLIST_HEAD(&ge->list);
1819 flist_add_tail(&ge->list, &ui->list);
1820 ge->ui = ui;
1821 return ge;
1822}
1823
1824/*
1825 * FIXME: need more handling here
1826 */
1827static void ge_destroy(GtkWidget *w, gpointer data)
1828{
1829 struct gui_entry *ge = data;
343cb4a9
JA
1830 struct gfio_client *gc = ge->client;
1831
1832 if (gc->client)
1833 fio_put_client(gc->client);
2f99deb0
JA
1834
1835 flist_del(&ge->list);
1836 free(ge);
1837}
1838
1839static struct gui_entry *get_new_ge_with_tab(const char *name)
1840{
1841 struct gui_entry *ge;
1842
1843 ge = alloc_new_gui_entry(&main_ui);
1844
1845 ge->vbox = new_client_page(ge);
1846 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1847
1848 ge->page_label = gtk_label_new(name);
1849 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1850
1851 gtk_widget_show_all(main_ui.window);
1852 return ge;
1853}
1854
1855static void file_new(GtkWidget *w, gpointer data)
1856{
1857 get_new_ge_with_tab("Untitled");
1858}
1859
1860/*
1861 * Return the 'ge' corresponding to the tab. If the active tab is the
1862 * main tab, open a new tab.
1863 */
1864static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1865{
1866 struct flist_head *entry;
1867 struct gui_entry *ge;
1868
1869 if (!cur_page)
1870 return get_new_ge_with_tab("Untitled");
1871
1872 flist_for_each(entry, &main_ui.list) {
1873 ge = flist_entry(entry, struct gui_entry, list);
1874 if (ge->page_num == cur_page)
1875 return ge;
1876 }
1877
1878 return NULL;
1879}
1880
0420ba6a
JA
1881static void file_open(GtkWidget *w, gpointer data)
1882{
63a130b7 1883 struct gui *ui = data;
2f99deb0 1884 GtkWidget *dialog;
0420ba6a
JA
1885 GSList *filenames, *fn_glist;
1886 GtkFileFilter *filter;
a7a42ce1 1887 char *host;
b9f3c7ed 1888 int port, type, server_start;
2f99deb0
JA
1889 struct gui_entry *ge;
1890 gint cur_page;
1891
1892 /*
1893 * Creates new tab if current tab is the main window, or the
1894 * current tab already has a client.
1895 */
1896 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1897 ge = get_ge_from_page(cur_page);
1898 if (ge->client)
1899 ge = get_new_ge_with_tab("Untitled");
1900
1901 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
0420ba6a
JA
1902
1903 dialog = gtk_file_chooser_dialog_new("Open File",
63a130b7 1904 GTK_WINDOW(ui->window),
0420ba6a
JA
1905 GTK_FILE_CHOOSER_ACTION_OPEN,
1906 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1907 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1908 NULL);
1909 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1910
1911 filter = gtk_file_filter_new();
1912 gtk_file_filter_add_pattern(filter, "*.fio");
1913 gtk_file_filter_add_pattern(filter, "*.job");
2d262990 1914 gtk_file_filter_add_pattern(filter, "*.ini");
0420ba6a
JA
1915 gtk_file_filter_add_mime_type(filter, "text/fio");
1916 gtk_file_filter_set_name(filter, "Fio job file");
1917 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1918
1919 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1920 gtk_widget_destroy(dialog);
1921 return;
1922 }
1923
1924 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
1925
1926 gtk_widget_destroy(dialog);
1927
b9f3c7ed 1928 if (get_connection_details(&host, &port, &type, &server_start))
a7a42ce1
JA
1929 goto err;
1930
0420ba6a
JA
1931 filenames = fn_glist;
1932 while (filenames != NULL) {
e0681f3e
JA
1933 struct fio_client *client;
1934
2f99deb0
JA
1935 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1936 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1937 ge->nr_job_files++;
0420ba6a 1938
e0681f3e
JA
1939 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1940 if (!client) {
df06f220
JA
1941 GError *error;
1942
1943 error = g_error_new(g_quark_from_string("fio"), 1,
1944 "Failed to add client %s", host);
0420ba6a
JA
1945 report_error(error);
1946 g_error_free(error);
0420ba6a 1947 }
2f99deb0 1948 gfio_client_added(ge, client);
0420ba6a
JA
1949
1950 g_free(filenames->data);
1951 filenames = g_slist_next(filenames);
1952 }
a7a42ce1 1953 free(host);
63a130b7
JA
1954
1955 if (server_start)
2f99deb0 1956 gfio_start_server();
a7a42ce1 1957err:
0420ba6a 1958 g_slist_free(fn_glist);
0420ba6a
JA
1959}
1960
1961static void file_save(GtkWidget *w, gpointer data)
1962{
63a130b7 1963 struct gui *ui = data;
0420ba6a
JA
1964 GtkWidget *dialog;
1965
1966 dialog = gtk_file_chooser_dialog_new("Save File",
63a130b7 1967 GTK_WINDOW(ui->window),
0420ba6a
JA
1968 GTK_FILE_CHOOSER_ACTION_SAVE,
1969 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1970 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1971 NULL);
1972
1973 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1974 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1975
1976 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1977 char *filename;
1978
1979 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1980 // save_job_file(filename);
1981 g_free(filename);
1982 }
1983 gtk_widget_destroy(dialog);
1984}
1985
9b260bdf
JA
1986static void view_log_destroy(GtkWidget *w, gpointer data)
1987{
1988 struct gui *ui = (struct gui *) data;
1989
1990 gtk_widget_ref(ui->log_tree);
1991 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1992 gtk_widget_destroy(w);
4cbe7211 1993 ui->log_view = NULL;
9b260bdf
JA
1994}
1995
1996static void view_log(GtkWidget *w, gpointer data)
1997{
4cbe7211
JA
1998 GtkWidget *win, *scroll, *vbox, *box;
1999 struct gui *ui = (struct gui *) data;
9b260bdf 2000
4cbe7211
JA
2001 if (ui->log_view)
2002 return;
2003
2004 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 2005 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 2006 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 2007
4cbe7211
JA
2008 scroll = gtk_scrolled_window_new(NULL, NULL);
2009
2010 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2011
2012 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2013
2014 box = gtk_hbox_new(TRUE, 0);
2015 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2016 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2017 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2018
2019 vbox = gtk_vbox_new(TRUE, 5);
2020 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
9b260bdf 2021
4cbe7211 2022 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
2023 gtk_widget_show_all(win);
2024}
2025
8577f4fd
JA
2026static void __update_graph_limits(struct gfio_graphs *g)
2027{
2028 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2029 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2030}
2031
2032static void update_graph_limits(void)
2033{
2034 struct flist_head *entry;
2035 struct gui_entry *ge;
2036
2037 __update_graph_limits(&main_ui.graphs);
2038
2039 flist_for_each(entry, &main_ui.list) {
2040 ge = flist_entry(entry, struct gui_entry, list);
2041 __update_graph_limits(&ge->graphs);
2042 }
2043}
2044
46974a7d
JA
2045static void preferences(GtkWidget *w, gpointer data)
2046{
f3e8440f 2047 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
1cf6bca5 2048 GtkWidget *hbox, *spin, *entry, *spin_int;
46974a7d
JA
2049 int i;
2050
2051 dialog = gtk_dialog_new_with_buttons("Preferences",
2f99deb0 2052 GTK_WINDOW(main_ui.window),
46974a7d
JA
2053 GTK_DIALOG_DESTROY_WITH_PARENT,
2054 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2055 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2056 NULL);
2057
8577f4fd 2058 frame = gtk_frame_new("Graphing");
f3e8440f
JA
2059 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2060 vbox = gtk_vbox_new(FALSE, 6);
2061 gtk_container_add(GTK_CONTAINER(frame), vbox);
2062
1cf6bca5
JA
2063 hbox = gtk_hbox_new(FALSE, 5);
2064 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2065 entry = gtk_label_new("Font face to use for graph labels");
2066 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2067
f3e8440f 2068 font = gtk_font_button_new();
1cf6bca5 2069 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
f3e8440f 2070
8577f4fd
JA
2071 box = gtk_vbox_new(FALSE, 6);
2072 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2073
2074 hbox = gtk_hbox_new(FALSE, 5);
1cf6bca5 2075 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
8577f4fd
JA
2076 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2077 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2078
2079 spin = create_spinbutton(hbox, 10, 1000000, 100);
2080
1cf6bca5
JA
2081 box = gtk_vbox_new(FALSE, 6);
2082 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2083
2084 hbox = gtk_hbox_new(FALSE, 5);
2085 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2086 entry = gtk_label_new("Client ETA request interval (msec)");
2087 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2088
2089 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
a31d9fa4
JA
2090 frame = gtk_frame_new("Debug logging");
2091 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2092 vbox = gtk_vbox_new(FALSE, 6);
2093 gtk_container_add(GTK_CONTAINER(frame), vbox);
2094
2095 box = gtk_hbox_new(FALSE, 6);
2096 gtk_container_add(GTK_CONTAINER(vbox), box);
2097
2098 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2099
2100 for (i = 0; i < FD_DEBUG_MAX; i++) {
2101 if (i == 7) {
2102 box = gtk_hbox_new(FALSE, 6);
2103 gtk_container_add(GTK_CONTAINER(vbox), box);
2104 }
2105
2106
2107 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2108 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2109 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2110 }
2111
46974a7d
JA
2112 gtk_widget_show_all(dialog);
2113
2114 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2115 gtk_widget_destroy(dialog);
2116 return;
2117 }
2118
2119 for (i = 0; i < FD_DEBUG_MAX; i++) {
2120 int set;
2121
2122 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2123 if (set)
2124 fio_debug |= (1UL << i);
2125 }
2126
f3e8440f 2127 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
8577f4fd
JA
2128 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2129 update_graph_limits();
1cf6bca5 2130 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
8577f4fd 2131
46974a7d
JA
2132 gtk_widget_destroy(dialog);
2133}
2134
0420ba6a
JA
2135static void about_dialog(GtkWidget *w, gpointer data)
2136{
81e4ea6e
JA
2137 const char *authors[] = {
2138 "Jens Axboe <axboe@kernel.dk>",
2139 "Stephen Carmeron <stephenmcameron@gmail.com>",
2140 NULL
2141 };
84a72ed3
JA
2142 const char *license[] = {
2143 "Fio is free software; you can redistribute it and/or modify "
2144 "it under the terms of the GNU General Public License as published by "
2145 "the Free Software Foundation; either version 2 of the License, or "
2146 "(at your option) any later version.\n",
2147 "Fio is distributed in the hope that it will be useful, "
2148 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2149 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2150 "GNU General Public License for more details.\n",
2151 "You should have received a copy of the GNU General Public License "
2152 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2153 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2154 };
2155 char *license_trans;
2156
2157 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2158 license[2], "\n", NULL);
81e4ea6e 2159
0420ba6a
JA
2160 gtk_show_about_dialog(NULL,
2161 "program-name", "gfio",
2162 "comments", "Gtk2 UI for fio",
84a72ed3 2163 "license", license_trans,
81e4ea6e
JA
2164 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2165 "authors", authors,
0420ba6a 2166 "version", fio_version_string,
81e4ea6e 2167 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
0420ba6a
JA
2168 "logo-icon-name", "fio",
2169 /* Must be last: */
81e4ea6e 2170 "wrap-license", TRUE,
0420ba6a 2171 NULL);
84a72ed3 2172
2f99deb0 2173 g_free(license_trans);
0420ba6a
JA
2174}
2175
2176static GtkActionEntry menu_items[] = {
46974a7d 2177 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 2178 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
46974a7d 2179 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2f99deb0 2180 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
46974a7d
JA
2181 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2182 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2183 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 2184 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
46974a7d
JA
2185 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2186 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 2187};
3e47bd25 2188static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
2189
2190static const gchar *ui_string = " \
2191 <ui> \
2192 <menubar name=\"MainMenu\"> \
2193 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2f99deb0
JA
2194 <menuitem name=\"New\" action=\"NewFile\" /> \
2195 <separator name=\"Separator1\"/> \
0420ba6a
JA
2196 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2197 <menuitem name=\"Save\" action=\"SaveFile\" /> \
46974a7d 2198 <separator name=\"Separator2\"/> \
2f99deb0
JA
2199 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2200 <separator name=\"Separator3\"/> \
0420ba6a
JA
2201 <menuitem name=\"Quit\" action=\"Quit\" /> \
2202 </menu> \
9b260bdf
JA
2203 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2204 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2205 </menu>\
0420ba6a
JA
2206 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2207 <menuitem name=\"About\" action=\"About\" /> \
2208 </menu> \
2209 </menubar> \
2210 </ui> \
2211";
2212
4cbe7211
JA
2213static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2214 struct gui *ui)
0420ba6a
JA
2215{
2216 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2217 GError *error = 0;
2218
2219 action_group = gtk_action_group_new("Menu");
4cbe7211 2220 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
2221
2222 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2223 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2224
2225 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2226 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2227}
2228
2229void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2230 GtkWidget *vbox, GtkUIManager *ui_manager)
2231{
2232 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2233}
2234
2f99deb0 2235static GtkWidget *new_client_page(struct gui_entry *ge)
ff1f3280 2236{
2f99deb0 2237 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
aaa71f68 2238 GdkColor white;
0420ba6a 2239
2f99deb0 2240 main_vbox = gtk_vbox_new(FALSE, 3);
45032dd8 2241
2f99deb0
JA
2242 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2243 ge->topvbox = gtk_vbox_new(FALSE, 3);
2244 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2245 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2246
2247 probe = gtk_frame_new("Job");
2248 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2249 probe_frame = gtk_vbox_new(FALSE, 3);
2250 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2251
2252 probe_box = gtk_hbox_new(FALSE, 3);
2253 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2254 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2255 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2256 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2257 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2258
2259 probe_box = gtk_hbox_new(FALSE, 3);
2260 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2261
3863d1ad 2262 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
2f99deb0
JA
2263 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2264 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2265 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2266 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2267 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2268
2269 probe_box = gtk_hbox_new(FALSE, 3);
2270 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2271 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2272 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2273 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2274 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2275
2276 /*
2277 * Only add this if we have a commit rate
2839f0c6 2278 */
2f99deb0
JA
2279#if 0
2280 probe_box = gtk_hbox_new(FALSE, 3);
2281 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2839f0c6 2282
2f99deb0
JA
2283 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2284 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
ff1f3280 2285
2f99deb0
JA
2286 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2287 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2288#endif
ff1f3280 2289
2f99deb0
JA
2290 /*
2291 * Set up a drawing area and IOPS and bandwidth graphs
2292 */
2293 gdk_color_parse("white", &white);
2294 ge->graphs.drawing_area = gtk_drawing_area_new();
2295 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2296 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2297 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2298 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2299 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2300 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2301 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2302 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2303 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2304 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2305 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2306 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2307 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2308 ge->graphs.drawing_area);
2309 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2310 TRUE, TRUE, 0);
04cc6b77 2311
2f99deb0
JA
2312 setup_graphs(&ge->graphs);
2313
2314 /*
2315 * Set up alignments for widgets at the bottom of ui,
2316 * align bottom left, expand horizontally but not vertically
2317 */
2318 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2319 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2320 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2321 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2322 FALSE, FALSE, 0);
2323
2324 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
0420ba6a 2325
c36f98d9 2326 /*
2f99deb0
JA
2327 * Set up thread status progress bar
2328 */
2329 ge->thread_status_pb = gtk_progress_bar_new();
2330 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2331 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2332 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2333
2334
2335 return main_vbox;
2336}
2337
2338static GtkWidget *new_main_page(struct gui *ui)
2339{
2340 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2341 GdkColor white;
2342
2343 main_vbox = gtk_vbox_new(FALSE, 3);
2344
2345 /*
2346 * Set up alignments for widgets at the top of ui,
c36f98d9
SC
2347 * align top left, expand horizontally but not vertically
2348 */
2349 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2f99deb0 2350 ui->topvbox = gtk_vbox_new(FALSE, 0);
c36f98d9 2351 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2f99deb0 2352 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
c36f98d9 2353
2f99deb0
JA
2354 probe = gtk_frame_new("Run statistics");
2355 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
843ad237
JA
2356 probe_frame = gtk_vbox_new(FALSE, 3);
2357 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2358
2359 probe_box = gtk_hbox_new(FALSE, 3);
2f99deb0 2360 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3863d1ad 2361 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
ca850992
JA
2362 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2363 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2364 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2365 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 2366
807f9971
JA
2367 /*
2368 * Only add this if we have a commit rate
2369 */
2370#if 0
3e47bd25
JA
2371 probe_box = gtk_hbox_new(FALSE, 3);
2372 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
2373
2374 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2375 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2376
3e47bd25
JA
2377 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2378 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 2379#endif
3e47bd25 2380
736f2dff 2381 /*
2fd3bb0e 2382 * Set up a drawing area and IOPS and bandwidth graphs
736f2dff 2383 */
aaa71f68 2384 gdk_color_parse("white", &white);
2f99deb0
JA
2385 ui->graphs.drawing_area = gtk_drawing_area_new();
2386 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2387 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2388 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2389 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2390 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2391 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2392 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2393 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2394 G_CALLBACK(on_config_drawing_area), &ui->graphs);
736f2dff
SC
2395 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2396 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2397 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2fd3bb0e 2398 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2f99deb0
JA
2399 ui->graphs.drawing_area);
2400 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
e164534f 2401 TRUE, TRUE, 0);
736f2dff 2402
2f99deb0 2403 setup_graphs(&ui->graphs);
2fd3bb0e 2404
c36f98d9
SC
2405 /*
2406 * Set up alignments for widgets at the bottom of ui,
2407 * align bottom left, expand horizontally but not vertically
2408 */
2409 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2410 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2411 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2f99deb0 2412 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
e164534f 2413 FALSE, FALSE, 0);
c36f98d9 2414
3ec62ec4
JA
2415 /*
2416 * Set up thread status progress bar
2417 */
2418 ui->thread_status_pb = gtk_progress_bar_new();
2419 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 2420 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
2421 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2422
2f99deb0
JA
2423 return main_vbox;
2424}
2425
2426static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2427 guint page, gpointer data)
2428
2429{
2430 return TRUE;
2431}
2432
2433static void init_ui(int *argc, char **argv[], struct gui *ui)
2434{
2435 GtkSettings *settings;
2436 GtkUIManager *uimanager;
2437 GtkWidget *menu, *vbox;
2438
2439 /* Magical g*thread incantation, you just need this thread stuff.
2440 * Without it, the update that happens in gfio_update_thread_status
2441 * doesn't really happen in a timely fashion, you need expose events
2442 */
2443 if (!g_thread_supported())
2444 g_thread_init(NULL);
2445 gdk_threads_init();
2446
2447 gtk_init(argc, argv);
2448 settings = gtk_settings_get_default();
2449 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2450 g_type_init();
2451
2452 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2453 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2454 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2455
2456 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2457 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2458
2459 ui->vbox = gtk_vbox_new(FALSE, 0);
2460 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2461
2462 uimanager = gtk_ui_manager_new();
2463 menu = get_menubar_menu(ui->window, uimanager, ui);
2464 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2465
2466 ui->notebook = gtk_notebook_new();
2467 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
b870c31b 2468 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
0aa928c4 2469 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
2f99deb0
JA
2470 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2471
2472 vbox = new_main_page(ui);
2473
2474 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2475
9b260bdf 2476 gfio_ui_setup_log(ui);
3ec62ec4 2477
ff1f3280
SC
2478 gtk_widget_show_all(ui->window);
2479}
2480
8232e285 2481int main(int argc, char *argv[], char *envp[])
ff1f3280 2482{
8232e285
SC
2483 if (initialize_fio(envp))
2484 return 1;
0420ba6a
JA
2485 if (fio_init_options())
2486 return 1;
a1820207 2487
2f99deb0
JA
2488 memset(&main_ui, 0, sizeof(main_ui));
2489 INIT_FLIST_HEAD(&main_ui.list);
2490
2491 init_ui(&argc, &argv, &main_ui);
5b7573ab 2492
2839f0c6 2493 gdk_threads_enter();
ff1f3280 2494 gtk_main();
2839f0c6 2495 gdk_threads_leave();
ff1f3280
SC
2496 return 0;
2497}