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