gfio: hold a reference to the fio_client structure
[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();
879 gtk_container_add(GTK_CONTAINER(win), notebook);
880
2f99deb0
JA
881 ge->results_window = win;
882 ge->results_notebook = notebook;
883 return ge->results_notebook;
f9d40b48
JA
884}
885
3650a3ca
JA
886static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
887 struct group_run_stats *rs)
888{
b01329d0 889 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
e0681f3e 890 struct gfio_client *gc = client->client_data;
3650a3ca
JA
891
892 gdk_threads_enter();
893
2f99deb0 894 res_win = get_results_window(gc->ge);
3650a3ca 895
b01329d0
JA
896 scroll = gtk_scrolled_window_new(NULL, NULL);
897 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
898 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
899
3650a3ca 900 vbox = gtk_vbox_new(FALSE, 3);
3650a3ca 901
b01329d0
JA
902 box = gtk_hbox_new(FALSE, 0);
903 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
904
905 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
3650a3ca 906
b01329d0 907 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
f9d40b48 908
e0681f3e
JA
909 gc->results_widget = vbox;
910
3650a3ca
JA
911 entry = new_info_entry_in_frame(box, "Name");
912 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
913 if (strlen(ts->description)) {
914 entry = new_info_entry_in_frame(box, "Description");
915 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
916 }
917 entry = new_info_entry_in_frame(box, "Group ID");
918 entry_set_int_value(entry, ts->groupid);
919 entry = new_info_entry_in_frame(box, "Jobs");
920 entry_set_int_value(entry, ts->members);
6b79c80c 921 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
3650a3ca
JA
922 entry_set_int_value(entry, ts->error);
923 entry = new_info_entry_in_frame(box, "PID");
924 entry_set_int_value(entry, ts->pid);
925
926 if (ts->io_bytes[DDIR_READ])
927 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
928 if (ts->io_bytes[DDIR_WRITE])
929 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
930
e5bd1347 931 gfio_show_latency_buckets(vbox, ts);
2e33101f
JA
932 gfio_show_cpu_usage(vbox, ts);
933 gfio_show_io_depths(vbox, ts);
e5bd1347 934
2f99deb0 935 gtk_widget_show_all(gc->ge->results_window);
3650a3ca
JA
936 gdk_threads_leave();
937}
938
084d1c6f 939static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
a1820207 940{
9b260bdf 941 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
2f99deb0 942 struct gui *ui = &main_ui;
9b260bdf
JA
943 GtkTreeIter iter;
944 struct tm *tm;
945 time_t sec;
946 char tmp[64], timebuf[80];
947
948 sec = p->log_sec;
949 tm = localtime(&sec);
950 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
951 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
736f2dff 952
736f2dff 953 gdk_threads_enter();
9b260bdf 954
2f99deb0
JA
955 gtk_list_store_append(ui->log_model, &iter);
956 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
957 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
958 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
959 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
9b260bdf 960
6b79c80c 961 if (p->level == FIO_LOG_ERR)
2f99deb0 962 view_log(NULL, (gpointer) ui);
6b79c80c 963
736f2dff 964 gdk_threads_leave();
a1820207
SC
965}
966
967static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
968{
e0681f3e
JA
969 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
970 struct gfio_client *gc = client->client_data;
971 GtkWidget *box, *frame, *entry, *vbox;
604cfe30
JA
972 double util;
973 char tmp[16];
e0681f3e 974
0050e5f2 975 gdk_threads_enter();
e0681f3e 976
45dcb2eb 977 if (!gc->results_widget)
e0681f3e 978 goto out;
e0681f3e
JA
979
980 if (!gc->disk_util_frame) {
981 gc->disk_util_frame = gtk_frame_new("Disk utilization");
982 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
983 }
984
985 vbox = gtk_vbox_new(FALSE, 3);
986 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
987
988 frame = gtk_frame_new((char *) p->dus.name);
989 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
990
991 box = gtk_vbox_new(FALSE, 3);
992 gtk_container_add(GTK_CONTAINER(frame), box);
993
994 frame = gtk_frame_new("Read");
995 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
996 vbox = gtk_hbox_new(TRUE, 3);
997 gtk_container_add(GTK_CONTAINER(frame), vbox);
998 entry = new_info_entry_in_frame(vbox, "IOs");
999 entry_set_int_value(entry, p->dus.ios[0]);
1000 entry = new_info_entry_in_frame(vbox, "Merges");
1001 entry_set_int_value(entry, p->dus.merges[0]);
1002 entry = new_info_entry_in_frame(vbox, "Sectors");
1003 entry_set_int_value(entry, p->dus.sectors[0]);
1004 entry = new_info_entry_in_frame(vbox, "Ticks");
1005 entry_set_int_value(entry, p->dus.ticks[0]);
1006
1007 frame = gtk_frame_new("Write");
1008 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1009 vbox = gtk_hbox_new(TRUE, 3);
1010 gtk_container_add(GTK_CONTAINER(frame), vbox);
1011 entry = new_info_entry_in_frame(vbox, "IOs");
1012 entry_set_int_value(entry, p->dus.ios[1]);
1013 entry = new_info_entry_in_frame(vbox, "Merges");
1014 entry_set_int_value(entry, p->dus.merges[1]);
1015 entry = new_info_entry_in_frame(vbox, "Sectors");
1016 entry_set_int_value(entry, p->dus.sectors[1]);
1017 entry = new_info_entry_in_frame(vbox, "Ticks");
1018 entry_set_int_value(entry, p->dus.ticks[1]);
1019
1020 frame = gtk_frame_new("Shared");
1021 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1022 vbox = gtk_hbox_new(TRUE, 3);
1023 gtk_container_add(GTK_CONTAINER(frame), vbox);
1024 entry = new_info_entry_in_frame(vbox, "IO ticks");
1025 entry_set_int_value(entry, p->dus.io_ticks);
1026 entry = new_info_entry_in_frame(vbox, "Time in queue");
1027 entry_set_int_value(entry, p->dus.time_in_queue);
1028
604cfe30
JA
1029 util = 0.0;
1030 if (p->dus.msec)
1031 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1032 if (util > 100.0)
1033 util = 100.0;
1034
1035 sprintf(tmp, "%3.2f%%", util);
1036 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1037 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1038
e0681f3e
JA
1039 gtk_widget_show_all(gc->results_widget);
1040out:
0050e5f2 1041 gdk_threads_leave();
a1820207
SC
1042}
1043
3650a3ca
JA
1044extern int sum_stat_clients;
1045extern struct thread_stat client_ts;
1046extern struct group_run_stats client_gs;
1047
1048static int sum_stat_nr;
1049
89e5fad9
JA
1050static void gfio_thread_status_op(struct fio_client *client,
1051 struct fio_net_cmd *cmd)
a1820207 1052{
3650a3ca
JA
1053 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1054
1055 gfio_display_ts(client, &p->ts, &p->rs);
1056
1057 if (sum_stat_clients == 1)
1058 return;
1059
1060 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1061 sum_group_stats(&client_gs, &p->rs);
1062
1063 client_ts.members++;
1064 client_ts.groupid = p->ts.groupid;
1065
1066 if (++sum_stat_nr == sum_stat_clients) {
1067 strcpy(client_ts.name, "All clients");
1068 gfio_display_ts(client, &client_ts, &client_gs);
1069 }
a1820207
SC
1070}
1071
89e5fad9
JA
1072static void gfio_group_stats_op(struct fio_client *client,
1073 struct fio_net_cmd *cmd)
a1820207 1074{
98ceabd6 1075 /* We're ignoring group stats for now */
a1820207
SC
1076}
1077
2f99deb0
JA
1078static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1079 gpointer data)
3ea48b88 1080{
2f99deb0
JA
1081 struct gfio_graphs *g = data;
1082
1083 g->drawing_area_xdim = w->allocation.width;
1084 g->drawing_area_ydim = w->allocation.height;
3ea48b88
SC
1085 return TRUE;
1086}
1087
2fd3bb0e
JA
1088static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1089{
2f99deb0 1090 struct gfio_graphs *g = p;
2fd3bb0e
JA
1091 cairo_t *cr;
1092
2f99deb0
JA
1093 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1094 g->drawing_area_ydim);
1095 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1096 g->drawing_area_ydim);
2fd3bb0e
JA
1097 cr = gdk_cairo_create(w->window);
1098
1099 cairo_set_source_rgb(cr, 0, 0, 0);
1100
1101 cairo_save(cr);
1102 cairo_translate(cr, 0, 0);
2f99deb0 1103 line_graph_draw(g->bandwidth_graph, cr);
2fd3bb0e
JA
1104 cairo_stroke(cr);
1105 cairo_restore(cr);
1106
1107 cairo_save(cr);
2f99deb0
JA
1108 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1109 line_graph_draw(g->iops_graph, cr);
2fd3bb0e
JA
1110 cairo_stroke(cr);
1111 cairo_restore(cr);
1112 cairo_destroy(cr);
1113
1114 return FALSE;
1115}
1116
2f99deb0
JA
1117/*
1118 * Client specific ETA
1119 */
1120static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
3e47bd25 1121{
2f99deb0
JA
1122 struct gfio_client *gc = client->client_data;
1123 struct gui_entry *ge = gc->ge;
3e47bd25
JA
1124 static int eta_good;
1125 char eta_str[128];
1126 char output[256];
1127 char tmp[32];
1128 double perc = 0.0;
1129 int i2p = 0;
1130
0050e5f2
JA
1131 gdk_threads_enter();
1132
3e47bd25
JA
1133 eta_str[0] = '\0';
1134 output[0] = '\0';
1135
1136 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1137 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1138 eta_to_str(eta_str, je->eta_sec);
1139 }
1140
1141 sprintf(tmp, "%u", je->nr_running);
2f99deb0 1142 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
3e47bd25 1143 sprintf(tmp, "%u", je->files_open);
2f99deb0 1144 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
3e47bd25
JA
1145
1146#if 0
1147 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1148 if (je->m_rate || je->t_rate) {
1149 char *tr, *mr;
1150
1151 mr = num2str(je->m_rate, 4, 0, i2p);
1152 tr = num2str(je->t_rate, 4, 0, i2p);
2f99deb0 1153 gtk_entry_set_text(GTK_ENTRY(ge->eta);
3e47bd25
JA
1154 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1155 free(tr);
1156 free(mr);
1157 } else if (je->m_iops || je->t_iops)
1158 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
ebbd89cc 1159
2f99deb0
JA
1160 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1161 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1162 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1163 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
3e47bd25
JA
1164#endif
1165
1166 if (je->eta_sec != INT_MAX && je->nr_running) {
1167 char *iops_str[2];
1168 char *rate_str[2];
1169
1170 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1171 strcpy(output, "-.-% done");
1172 else {
1173 eta_good = 1;
1174 perc *= 100.0;
1175 sprintf(output, "%3.1f%% done", perc);
1176 }
1177
1178 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1179 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1180
1181 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1182 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1183
2f99deb0
JA
1184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1185 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1187 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
3e47bd25 1188
2f99deb0
JA
1189 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1190 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1191 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1192 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1193
1194 free(rate_str[0]);
1195 free(rate_str[1]);
1196 free(iops_str[0]);
1197 free(iops_str[1]);
1198 }
1199
1200 if (eta_str[0]) {
1201 char *dst = output + strlen(output);
1202
1203 sprintf(dst, " - %s", eta_str);
1204 }
1205
9988ca70 1206 gfio_update_thread_status(ge, output, perc);
2f99deb0
JA
1207 gdk_threads_leave();
1208}
1209
1210/*
1211 * Update ETA in main window for all clients
1212 */
1213static void gfio_update_all_eta(struct jobs_eta *je)
1214{
1215 struct gui *ui = &main_ui;
1216 static int eta_good;
1217 char eta_str[128];
1218 char output[256];
1219 double perc = 0.0;
1220 int i2p = 0;
1221
1222 gdk_threads_enter();
1223
1224 eta_str[0] = '\0';
1225 output[0] = '\0';
1226
1227 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1228 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1229 eta_to_str(eta_str, je->eta_sec);
1230 }
1231
1232#if 0
1233 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1234 if (je->m_rate || je->t_rate) {
1235 char *tr, *mr;
1236
1237 mr = num2str(je->m_rate, 4, 0, i2p);
1238 tr = num2str(je->t_rate, 4, 0, i2p);
1239 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1240 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1241 free(tr);
1242 free(mr);
1243 } else if (je->m_iops || je->t_iops)
1244 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1245
1246 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1247 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1248 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1249 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1250#endif
1251
1252 if (je->eta_sec != INT_MAX && je->nr_running) {
1253 char *iops_str[2];
1254 char *rate_str[2];
1255
1256 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1257 strcpy(output, "-.-% done");
1258 else {
1259 eta_good = 1;
1260 perc *= 100.0;
1261 sprintf(output, "%3.1f%% done", perc);
1262 }
1263
1264 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1265 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1266
1267 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1268 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1269
1270 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1271 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1272 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1273 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1274
1275 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1276 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1277 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1278 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
2fd3bb0e 1279
3e47bd25
JA
1280 free(rate_str[0]);
1281 free(rate_str[1]);
1282 free(iops_str[0]);
1283 free(iops_str[1]);
1284 }
1285
1286 if (eta_str[0]) {
1287 char *dst = output + strlen(output);
1288
1289 sprintf(dst, " - %s", eta_str);
1290 }
1291
9988ca70 1292 gfio_update_thread_status_all(output, perc);
0050e5f2 1293 gdk_threads_leave();
3e47bd25
JA
1294}
1295
a1820207
SC
1296static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1297{
843ad237 1298 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
88f6e7ad 1299 struct gfio_client *gc = client->client_data;
2f99deb0 1300 struct gui_entry *ge = gc->ge;
843ad237
JA
1301 const char *os, *arch;
1302 char buf[64];
1303
1304 os = fio_get_os_string(probe->os);
1305 if (!os)
1306 os = "unknown";
1307
1308 arch = fio_get_arch_string(probe->arch);
1309 if (!arch)
1310 os = "unknown";
1311
1312 if (!client->name)
1313 client->name = strdup((char *) probe->hostname);
1314
0050e5f2
JA
1315 gdk_threads_enter();
1316
2f99deb0
JA
1317 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1318 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1319 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
843ad237 1320 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
2f99deb0 1321 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
88f6e7ad 1322
2f99deb0 1323 gfio_set_connected(ge, 1);
0050e5f2
JA
1324
1325 gdk_threads_leave();
a1820207
SC
1326}
1327
9988ca70
JA
1328static void gfio_update_thread_status(struct gui_entry *ge,
1329 char *status_message, double perc)
1330{
1331 static char message[100];
1332 const char *m = message;
1333
1334 strncpy(message, status_message, sizeof(message) - 1);
1335 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1336 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1337 gtk_widget_queue_draw(main_ui.window);
1338}
1339
1340static void gfio_update_thread_status_all(char *status_message, double perc)
5b7573ab 1341{
2f99deb0 1342 struct gui *ui = &main_ui;
5b7573ab
SC
1343 static char message[100];
1344 const char *m = message;
1345
1346 strncpy(message, status_message, sizeof(message) - 1);
2f99deb0
JA
1347 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1348 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1349 gtk_widget_queue_draw(ui->window);
5b7573ab
SC
1350}
1351
3ec62ec4
JA
1352static void gfio_quit_op(struct fio_client *client)
1353{
e0681f3e 1354 struct gfio_client *gc = client->client_data;
3ec62ec4 1355
0050e5f2 1356 gdk_threads_enter();
2f99deb0 1357 gfio_set_connected(gc->ge, 0);
0050e5f2 1358 gdk_threads_leave();
3ec62ec4
JA
1359}
1360
807f9971
JA
1361static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1362{
1363 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
e0681f3e 1364 struct gfio_client *gc = client->client_data;
dcaeb606 1365 struct thread_options *o = &gc->o;
2f99deb0 1366 struct gui_entry *ge = gc->ge;
807f9971 1367 char tmp[8];
807f9971 1368
dcaeb606 1369 convert_thread_options_to_cpu(o, &p->top);
807f9971 1370
0050e5f2
JA
1371 gdk_threads_enter();
1372
2f99deb0
JA
1373 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1374
1375 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1376 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1377 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
807f9971 1378
dcaeb606 1379 sprintf(tmp, "%u", o->iodepth);
2f99deb0 1380 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
0050e5f2 1381
dcaeb606
JA
1382 gc->job_added++;
1383
0050e5f2 1384 gdk_threads_leave();
807f9971
JA
1385}
1386
ed727a46
JA
1387static void gfio_client_timed_out(struct fio_client *client)
1388{
e0681f3e 1389 struct gfio_client *gc = client->client_data;
ed727a46
JA
1390 GtkWidget *dialog, *label, *content;
1391 char buf[256];
1392
1393 gdk_threads_enter();
1394
2f99deb0
JA
1395 gfio_set_connected(gc->ge, 0);
1396 clear_ge_ui_info(gc->ge);
ed727a46
JA
1397
1398 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1399
1400 dialog = gtk_dialog_new_with_buttons("Timed out!",
2f99deb0 1401 GTK_WINDOW(main_ui.window),
ed727a46
JA
1402 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1403 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1404
f129909e
JA
1405 /* gtk_dialog_get_content_area() is 2.14 and newer */
1406 content = GTK_DIALOG(dialog)->vbox;
1407
ed727a46
JA
1408 label = gtk_label_new((const gchar *) buf);
1409 gtk_container_add(GTK_CONTAINER(content), label);
1410 gtk_widget_show_all(dialog);
1411 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1412
1413 gtk_dialog_run(GTK_DIALOG(dialog));
1414 gtk_widget_destroy(dialog);
1415
1416 gdk_threads_leave();
1417}
1418
6b79c80c
JA
1419static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1420{
1421 struct gfio_client *gc = client->client_data;
1422
1423 gdk_threads_enter();
1424
2f99deb0 1425 gfio_set_connected(gc->ge, 0);
6b79c80c
JA
1426
1427 if (gc->err_entry)
1428 entry_set_int_value(gc->err_entry, client->error);
1429
1430 gdk_threads_leave();
1431}
1432
a1820207 1433struct client_ops gfio_client_ops = {
0420ba6a
JA
1434 .text_op = gfio_text_op,
1435 .disk_util = gfio_disk_util_op,
1436 .thread_status = gfio_thread_status_op,
1437 .group_stats = gfio_group_stats_op,
2f99deb0
JA
1438 .jobs_eta = gfio_update_client_eta,
1439 .eta = gfio_update_all_eta,
0420ba6a 1440 .probe = gfio_probe_op,
3ec62ec4 1441 .quit = gfio_quit_op,
807f9971 1442 .add_job = gfio_add_job_op,
ed727a46 1443 .timed_out = gfio_client_timed_out,
6b79c80c 1444 .stop = gfio_client_stop,
3ec62ec4 1445 .stay_connected = 1,
a1820207
SC
1446};
1447
ff1f3280
SC
1448static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1449 __attribute__((unused)) gpointer data)
1450{
1451 gtk_main_quit();
1452}
1453
25927259
SC
1454static void *job_thread(void *arg)
1455{
a9eccde4
JA
1456 struct gui *ui = arg;
1457
1458 ui->handler_running = 1;
25927259 1459 fio_handle_clients(&gfio_client_ops);
a9eccde4 1460 ui->handler_running = 0;
25927259
SC
1461 return NULL;
1462}
1463
2f99deb0 1464static int send_job_files(struct gui_entry *ge)
60f6b330 1465{
9988ca70 1466 struct gfio_client *gc = ge->client;
441013b4 1467 int i, ret = 0;
0420ba6a 1468
2f99deb0 1469 for (i = 0; i < ge->nr_job_files; i++) {
9988ca70 1470 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
c724926b
JA
1471 if (ret < 0) {
1472 GError *error;
1473
1474 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1475 report_error(error);
1476 g_error_free(error);
1477 break;
1478 } else if (ret)
441013b4
JA
1479 break;
1480
2f99deb0
JA
1481 free(ge->job_files[i]);
1482 ge->job_files[i] = NULL;
441013b4 1483 }
2f99deb0
JA
1484 while (i < ge->nr_job_files) {
1485 free(ge->job_files[i]);
1486 ge->job_files[i] = NULL;
441013b4 1487 i++;
0420ba6a
JA
1488 }
1489
441013b4 1490 return ret;
60f6b330
SC
1491}
1492
63a130b7
JA
1493static void *server_thread(void *arg)
1494{
1495 is_backend = 1;
1496 gfio_server_running = 1;
1497 fio_start_server(NULL);
1498 gfio_server_running = 0;
1499 return NULL;
1500}
1501
2f99deb0 1502static void gfio_start_server(void)
63a130b7 1503{
2f99deb0
JA
1504 struct gui *ui = &main_ui;
1505
63a130b7
JA
1506 if (!gfio_server_running) {
1507 gfio_server_running = 1;
1508 pthread_create(&ui->server_t, NULL, server_thread, NULL);
e34f6ad7 1509 pthread_detach(ui->server_t);
63a130b7
JA
1510 }
1511}
1512
f3074008 1513static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 1514 gpointer data)
f3074008 1515{
2f99deb0
JA
1516 struct gui_entry *ge = data;
1517 struct gfio_client *gc = ge->client;
25927259 1518
2f99deb0
JA
1519 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1520 fio_start_client(gc->client);
f3074008
SC
1521}
1522
df06f220
JA
1523static void file_open(GtkWidget *w, gpointer data);
1524
1525static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 1526{
2f99deb0
JA
1527 struct gui_entry *ge = data;
1528 struct gfio_client *gc = ge->client;
3ec62ec4 1529
2f99deb0 1530 if (!ge->connected) {
c724926b
JA
1531 int ret;
1532
2f99deb0 1533 if (!ge->nr_job_files)
df06f220 1534 file_open(widget, data);
2f99deb0
JA
1535 if (!ge->nr_job_files)
1536 return;
1537
1538 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1539 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
c724926b
JA
1540 ret = fio_client_connect(gc->client);
1541 if (!ret) {
a9eccde4
JA
1542 if (!ge->ui->handler_running)
1543 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
2f99deb0
JA
1544 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1545 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
c724926b
JA
1546 } else {
1547 GError *error;
1548
1549 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1550 report_error(error);
1551 g_error_free(error);
69406b92 1552 }
df06f220 1553 } else {
2f99deb0
JA
1554 fio_client_terminate(gc->client);
1555 gfio_set_connected(ge, 0);
1556 clear_ge_ui_info(ge);
df06f220 1557 }
3e47bd25
JA
1558}
1559
b9d2f30a
JA
1560static void send_clicked(GtkWidget *widget, gpointer data)
1561{
2f99deb0 1562 struct gui_entry *ge = data;
b9d2f30a 1563
2f99deb0 1564 if (send_job_files(ge)) {
c724926b
JA
1565 GError *error;
1566
1567 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);
1568 report_error(error);
1569 g_error_free(error);
1570
2f99deb0 1571 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
b9d2f30a
JA
1572 }
1573
2f99deb0
JA
1574 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1575 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
b9d2f30a
JA
1576}
1577
2f99deb0
JA
1578static GtkWidget *add_button(GtkWidget *buttonbox,
1579 struct button_spec *buttonspec, gpointer data)
f3074008 1580{
2f99deb0
JA
1581 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1582
1583 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1584 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1585 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1586 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1587
1588 return button;
f3074008
SC
1589}
1590
2f99deb0
JA
1591static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1592 int nbuttons)
f3074008
SC
1593{
1594 int i;
1595
f3074008 1596 for (i = 0; i < nbuttons; i++)
2f99deb0 1597 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
f3074008
SC
1598}
1599
0420ba6a
JA
1600static void on_info_bar_response(GtkWidget *widget, gint response,
1601 gpointer data)
1602{
2f99deb0
JA
1603 struct gui *ui = &main_ui;
1604
0420ba6a
JA
1605 if (response == GTK_RESPONSE_OK) {
1606 gtk_widget_destroy(widget);
2f99deb0 1607 ui->error_info_bar = NULL;
0420ba6a
JA
1608 }
1609}
1610
df06f220 1611void report_error(GError *error)
0420ba6a 1612{
2f99deb0
JA
1613 struct gui *ui = &main_ui;
1614
1615 if (ui->error_info_bar == NULL) {
1616 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
0420ba6a
JA
1617 GTK_RESPONSE_OK,
1618 NULL);
2f99deb0
JA
1619 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1620 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
0420ba6a
JA
1621 GTK_MESSAGE_ERROR);
1622
2f99deb0
JA
1623 ui->error_label = gtk_label_new(error->message);
1624 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1625 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
0420ba6a 1626
2f99deb0
JA
1627 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1628 gtk_widget_show_all(ui->vbox);
0420ba6a
JA
1629 } else {
1630 char buffer[256];
1631 snprintf(buffer, sizeof(buffer), "Failed to open file.");
2f99deb0 1632 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
0420ba6a
JA
1633 }
1634}
1635
62bc937f
JA
1636struct connection_widgets
1637{
1638 GtkWidget *hentry;
1639 GtkWidget *combo;
1640 GtkWidget *button;
1641};
1642
1643static void hostname_cb(GtkEntry *entry, gpointer data)
1644{
1645 struct connection_widgets *cw = data;
1646 int uses_net = 0, is_localhost = 0;
1647 const gchar *text;
1648 gchar *ctext;
1649
1650 /*
1651 * Check whether to display the 'auto start backend' box
1652 * or not. Show it if we are a localhost and using network,
1653 * or using a socket.
1654 */
1655 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1656 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1657 uses_net = 1;
1658 g_free(ctext);
1659
1660 if (uses_net) {
1661 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1662 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1663 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1664 !strcmp(text, "ip6-loopback"))
1665 is_localhost = 1;
1666 }
1667
1668 if (!uses_net || is_localhost) {
1669 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1670 gtk_widget_set_sensitive(cw->button, 1);
1671 } else {
1672 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1673 gtk_widget_set_sensitive(cw->button, 0);
1674 }
1675}
1676
b9f3c7ed
JA
1677static int get_connection_details(char **host, int *port, int *type,
1678 int *server_start)
a7a42ce1 1679{
62bc937f
JA
1680 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1681 struct connection_widgets cw;
a7a42ce1
JA
1682 char *typeentry;
1683
1684 dialog = gtk_dialog_new_with_buttons("Connection details",
2f99deb0 1685 GTK_WINDOW(main_ui.window),
a7a42ce1
JA
1686 GTK_DIALOG_DESTROY_WITH_PARENT,
1687 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1688 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1689
1690 frame = gtk_frame_new("Hostname / socket name");
f129909e
JA
1691 /* gtk_dialog_get_content_area() is 2.14 and newer */
1692 vbox = GTK_DIALOG(dialog)->vbox;
a7a42ce1
JA
1693 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1694
1695 box = gtk_vbox_new(FALSE, 6);
1696 gtk_container_add(GTK_CONTAINER(frame), box);
1697
1698 hbox = gtk_hbox_new(TRUE, 10);
1699 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
62bc937f
JA
1700 cw.hentry = gtk_entry_new();
1701 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1702 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
a7a42ce1
JA
1703
1704 frame = gtk_frame_new("Port");
1705 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1706 box = gtk_vbox_new(FALSE, 10);
1707 gtk_container_add(GTK_CONTAINER(frame), box);
1708
1709 hbox = gtk_hbox_new(TRUE, 4);
1710 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1711 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1712
1713 frame = gtk_frame_new("Type");
1714 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1715 box = gtk_vbox_new(FALSE, 10);
1716 gtk_container_add(GTK_CONTAINER(frame), box);
1717
1718 hbox = gtk_hbox_new(TRUE, 4);
1719 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1720
62bc937f
JA
1721 cw.combo = gtk_combo_box_new_text();
1722 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1723 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1724 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1725 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
a7a42ce1 1726
62bc937f 1727 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
a7a42ce1 1728
b9f3c7ed
JA
1729 frame = gtk_frame_new("Options");
1730 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1731 box = gtk_vbox_new(FALSE, 10);
1732 gtk_container_add(GTK_CONTAINER(frame), box);
1733
1734 hbox = gtk_hbox_new(TRUE, 4);
1735 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1736
62bc937f
JA
1737 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1738 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1739 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.");
1740 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1741
1742 /*
1743 * Connect edit signal, so we can show/not-show the auto start button
1744 */
1745 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1746 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
b9f3c7ed 1747
a7a42ce1
JA
1748 gtk_widget_show_all(dialog);
1749
1750 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1751 gtk_widget_destroy(dialog);
1752 return 1;
1753 }
1754
62bc937f 1755 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
a7a42ce1
JA
1756 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1757
62bc937f 1758 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
a7a42ce1
JA
1759 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1760 *type = Fio_client_ipv4;
1761 else if (!strncmp(typeentry, "IPv6", 4))
1762 *type = Fio_client_ipv6;
1763 else
1764 *type = Fio_client_socket;
1765 g_free(typeentry);
1766
62bc937f 1767 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
b9f3c7ed 1768
a7a42ce1
JA
1769 gtk_widget_destroy(dialog);
1770 return 0;
1771}
1772
2f99deb0 1773static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
e0681f3e
JA
1774{
1775 struct gfio_client *gc;
1776
1777 gc = malloc(sizeof(*gc));
1778 memset(gc, 0, sizeof(*gc));
2f99deb0 1779 gc->ge = ge;
343cb4a9 1780 gc->client = fio_get_client(client);
b9d2f30a 1781
2f99deb0 1782 ge->client = gc;
e0681f3e
JA
1783
1784 client->client_data = gc;
1785}
1786
2f99deb0
JA
1787static GtkWidget *new_client_page(struct gui_entry *ge);
1788
1789static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1790{
1791 struct gui_entry *ge;
1792
1793 ge = malloc(sizeof(*ge));
1794 memset(ge, 0, sizeof(*ge));
1795 INIT_FLIST_HEAD(&ge->list);
1796 flist_add_tail(&ge->list, &ui->list);
1797 ge->ui = ui;
1798 return ge;
1799}
1800
1801/*
1802 * FIXME: need more handling here
1803 */
1804static void ge_destroy(GtkWidget *w, gpointer data)
1805{
1806 struct gui_entry *ge = data;
343cb4a9
JA
1807 struct gfio_client *gc = ge->client;
1808
1809 if (gc->client)
1810 fio_put_client(gc->client);
2f99deb0
JA
1811
1812 flist_del(&ge->list);
1813 free(ge);
1814}
1815
1816static struct gui_entry *get_new_ge_with_tab(const char *name)
1817{
1818 struct gui_entry *ge;
1819
1820 ge = alloc_new_gui_entry(&main_ui);
1821
1822 ge->vbox = new_client_page(ge);
1823 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1824
1825 ge->page_label = gtk_label_new(name);
1826 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1827
1828 gtk_widget_show_all(main_ui.window);
1829 return ge;
1830}
1831
1832static void file_new(GtkWidget *w, gpointer data)
1833{
1834 get_new_ge_with_tab("Untitled");
1835}
1836
1837/*
1838 * Return the 'ge' corresponding to the tab. If the active tab is the
1839 * main tab, open a new tab.
1840 */
1841static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1842{
1843 struct flist_head *entry;
1844 struct gui_entry *ge;
1845
1846 if (!cur_page)
1847 return get_new_ge_with_tab("Untitled");
1848
1849 flist_for_each(entry, &main_ui.list) {
1850 ge = flist_entry(entry, struct gui_entry, list);
1851 if (ge->page_num == cur_page)
1852 return ge;
1853 }
1854
1855 return NULL;
1856}
1857
0420ba6a
JA
1858static void file_open(GtkWidget *w, gpointer data)
1859{
63a130b7 1860 struct gui *ui = data;
2f99deb0 1861 GtkWidget *dialog;
0420ba6a
JA
1862 GSList *filenames, *fn_glist;
1863 GtkFileFilter *filter;
a7a42ce1 1864 char *host;
b9f3c7ed 1865 int port, type, server_start;
2f99deb0
JA
1866 struct gui_entry *ge;
1867 gint cur_page;
1868
1869 /*
1870 * Creates new tab if current tab is the main window, or the
1871 * current tab already has a client.
1872 */
1873 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1874 ge = get_ge_from_page(cur_page);
1875 if (ge->client)
1876 ge = get_new_ge_with_tab("Untitled");
1877
1878 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
0420ba6a
JA
1879
1880 dialog = gtk_file_chooser_dialog_new("Open File",
63a130b7 1881 GTK_WINDOW(ui->window),
0420ba6a
JA
1882 GTK_FILE_CHOOSER_ACTION_OPEN,
1883 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1884 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1885 NULL);
1886 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1887
1888 filter = gtk_file_filter_new();
1889 gtk_file_filter_add_pattern(filter, "*.fio");
1890 gtk_file_filter_add_pattern(filter, "*.job");
2d262990 1891 gtk_file_filter_add_pattern(filter, "*.ini");
0420ba6a
JA
1892 gtk_file_filter_add_mime_type(filter, "text/fio");
1893 gtk_file_filter_set_name(filter, "Fio job file");
1894 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1895
1896 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1897 gtk_widget_destroy(dialog);
1898 return;
1899 }
1900
1901 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
1902
1903 gtk_widget_destroy(dialog);
1904
b9f3c7ed 1905 if (get_connection_details(&host, &port, &type, &server_start))
a7a42ce1
JA
1906 goto err;
1907
0420ba6a
JA
1908 filenames = fn_glist;
1909 while (filenames != NULL) {
e0681f3e
JA
1910 struct fio_client *client;
1911
2f99deb0
JA
1912 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1913 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1914 ge->nr_job_files++;
0420ba6a 1915
e0681f3e
JA
1916 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1917 if (!client) {
df06f220
JA
1918 GError *error;
1919
1920 error = g_error_new(g_quark_from_string("fio"), 1,
1921 "Failed to add client %s", host);
0420ba6a
JA
1922 report_error(error);
1923 g_error_free(error);
0420ba6a 1924 }
2f99deb0 1925 gfio_client_added(ge, client);
0420ba6a
JA
1926
1927 g_free(filenames->data);
1928 filenames = g_slist_next(filenames);
1929 }
a7a42ce1 1930 free(host);
63a130b7
JA
1931
1932 if (server_start)
2f99deb0 1933 gfio_start_server();
a7a42ce1 1934err:
0420ba6a 1935 g_slist_free(fn_glist);
0420ba6a
JA
1936}
1937
1938static void file_save(GtkWidget *w, gpointer data)
1939{
63a130b7 1940 struct gui *ui = data;
0420ba6a
JA
1941 GtkWidget *dialog;
1942
1943 dialog = gtk_file_chooser_dialog_new("Save File",
63a130b7 1944 GTK_WINDOW(ui->window),
0420ba6a
JA
1945 GTK_FILE_CHOOSER_ACTION_SAVE,
1946 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1947 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1948 NULL);
1949
1950 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1951 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1952
1953 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1954 char *filename;
1955
1956 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1957 // save_job_file(filename);
1958 g_free(filename);
1959 }
1960 gtk_widget_destroy(dialog);
1961}
1962
9b260bdf
JA
1963static void view_log_destroy(GtkWidget *w, gpointer data)
1964{
1965 struct gui *ui = (struct gui *) data;
1966
1967 gtk_widget_ref(ui->log_tree);
1968 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1969 gtk_widget_destroy(w);
4cbe7211 1970 ui->log_view = NULL;
9b260bdf
JA
1971}
1972
1973static void view_log(GtkWidget *w, gpointer data)
1974{
4cbe7211
JA
1975 GtkWidget *win, *scroll, *vbox, *box;
1976 struct gui *ui = (struct gui *) data;
9b260bdf 1977
4cbe7211
JA
1978 if (ui->log_view)
1979 return;
1980
1981 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 1982 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 1983 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 1984
4cbe7211
JA
1985 scroll = gtk_scrolled_window_new(NULL, NULL);
1986
1987 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1988
1989 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1990
1991 box = gtk_hbox_new(TRUE, 0);
1992 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1993 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1994 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1995
1996 vbox = gtk_vbox_new(TRUE, 5);
1997 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
9b260bdf 1998
4cbe7211 1999 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
2000 gtk_widget_show_all(win);
2001}
2002
46974a7d
JA
2003static void preferences(GtkWidget *w, gpointer data)
2004{
f3e8440f 2005 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
46974a7d
JA
2006 int i;
2007
2008 dialog = gtk_dialog_new_with_buttons("Preferences",
2f99deb0 2009 GTK_WINDOW(main_ui.window),
46974a7d
JA
2010 GTK_DIALOG_DESTROY_WITH_PARENT,
2011 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2012 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2013 NULL);
2014
0b8d11ed 2015 frame = gtk_frame_new("Debug logging");
46974a7d 2016 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
f3e8440f
JA
2017
2018 vbox = gtk_vbox_new(FALSE, 6);
2019 gtk_container_add(GTK_CONTAINER(frame), vbox);
2020
46974a7d 2021 box = gtk_hbox_new(FALSE, 6);
f3e8440f 2022 gtk_container_add(GTK_CONTAINER(vbox), box);
46974a7d
JA
2023
2024 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2025
2026 for (i = 0; i < FD_DEBUG_MAX; i++) {
f3e8440f
JA
2027 if (i == 7) {
2028 box = gtk_hbox_new(FALSE, 6);
2029 gtk_container_add(GTK_CONTAINER(vbox), box);
2030 }
2031
2032
46974a7d 2033 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
0b8d11ed 2034 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
46974a7d
JA
2035 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2036 }
2037
f3e8440f
JA
2038 frame = gtk_frame_new("Graph font");
2039 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2040 vbox = gtk_vbox_new(FALSE, 6);
2041 gtk_container_add(GTK_CONTAINER(frame), vbox);
2042
2043 font = gtk_font_button_new();
2044 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2045
46974a7d
JA
2046 gtk_widget_show_all(dialog);
2047
2048 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2049 gtk_widget_destroy(dialog);
2050 return;
2051 }
2052
2053 for (i = 0; i < FD_DEBUG_MAX; i++) {
2054 int set;
2055
2056 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2057 if (set)
2058 fio_debug |= (1UL << i);
2059 }
2060
f3e8440f 2061 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
46974a7d
JA
2062 gtk_widget_destroy(dialog);
2063}
2064
0420ba6a
JA
2065static void about_dialog(GtkWidget *w, gpointer data)
2066{
81e4ea6e
JA
2067 const char *authors[] = {
2068 "Jens Axboe <axboe@kernel.dk>",
2069 "Stephen Carmeron <stephenmcameron@gmail.com>",
2070 NULL
2071 };
84a72ed3
JA
2072 const char *license[] = {
2073 "Fio is free software; you can redistribute it and/or modify "
2074 "it under the terms of the GNU General Public License as published by "
2075 "the Free Software Foundation; either version 2 of the License, or "
2076 "(at your option) any later version.\n",
2077 "Fio is distributed in the hope that it will be useful, "
2078 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2079 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2080 "GNU General Public License for more details.\n",
2081 "You should have received a copy of the GNU General Public License "
2082 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2083 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2084 };
2085 char *license_trans;
2086
2087 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2088 license[2], "\n", NULL);
81e4ea6e 2089
0420ba6a
JA
2090 gtk_show_about_dialog(NULL,
2091 "program-name", "gfio",
2092 "comments", "Gtk2 UI for fio",
84a72ed3 2093 "license", license_trans,
81e4ea6e
JA
2094 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2095 "authors", authors,
0420ba6a 2096 "version", fio_version_string,
81e4ea6e 2097 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
0420ba6a
JA
2098 "logo-icon-name", "fio",
2099 /* Must be last: */
81e4ea6e 2100 "wrap-license", TRUE,
0420ba6a 2101 NULL);
84a72ed3 2102
2f99deb0 2103 g_free(license_trans);
0420ba6a
JA
2104}
2105
2106static GtkActionEntry menu_items[] = {
46974a7d 2107 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 2108 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
46974a7d 2109 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
2f99deb0 2110 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
46974a7d
JA
2111 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2112 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2113 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 2114 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
46974a7d
JA
2115 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2116 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 2117};
3e47bd25 2118static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
2119
2120static const gchar *ui_string = " \
2121 <ui> \
2122 <menubar name=\"MainMenu\"> \
2123 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
2f99deb0
JA
2124 <menuitem name=\"New\" action=\"NewFile\" /> \
2125 <separator name=\"Separator1\"/> \
0420ba6a
JA
2126 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2127 <menuitem name=\"Save\" action=\"SaveFile\" /> \
46974a7d 2128 <separator name=\"Separator2\"/> \
2f99deb0
JA
2129 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2130 <separator name=\"Separator3\"/> \
0420ba6a
JA
2131 <menuitem name=\"Quit\" action=\"Quit\" /> \
2132 </menu> \
9b260bdf
JA
2133 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2134 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2135 </menu>\
0420ba6a
JA
2136 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2137 <menuitem name=\"About\" action=\"About\" /> \
2138 </menu> \
2139 </menubar> \
2140 </ui> \
2141";
2142
4cbe7211
JA
2143static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2144 struct gui *ui)
0420ba6a
JA
2145{
2146 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2147 GError *error = 0;
2148
2149 action_group = gtk_action_group_new("Menu");
4cbe7211 2150 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
2151
2152 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2153 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2154
2155 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2156 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2157}
2158
2159void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2160 GtkWidget *vbox, GtkUIManager *ui_manager)
2161{
2162 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2163}
2164
2f99deb0 2165static GtkWidget *new_client_page(struct gui_entry *ge)
ff1f3280 2166{
2f99deb0 2167 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
aaa71f68 2168 GdkColor white;
0420ba6a 2169
2f99deb0 2170 main_vbox = gtk_vbox_new(FALSE, 3);
45032dd8 2171
2f99deb0
JA
2172 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2173 ge->topvbox = gtk_vbox_new(FALSE, 3);
2174 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2175 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
2176
2177 probe = gtk_frame_new("Job");
2178 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2179 probe_frame = gtk_vbox_new(FALSE, 3);
2180 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2181
2182 probe_box = gtk_hbox_new(FALSE, 3);
2183 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2184 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2185 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2186 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2187 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
2188
2189 probe_box = gtk_hbox_new(FALSE, 3);
2190 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2191
2192 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2193 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2194 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2195 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2196 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2197 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2198
2199 probe_box = gtk_hbox_new(FALSE, 3);
2200 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2201 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2202 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2203 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2204 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2205
2206 /*
2207 * Only add this if we have a commit rate
2839f0c6 2208 */
2f99deb0
JA
2209#if 0
2210 probe_box = gtk_hbox_new(FALSE, 3);
2211 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2839f0c6 2212
2f99deb0
JA
2213 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2214 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
ff1f3280 2215
2f99deb0
JA
2216 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2217 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2218#endif
ff1f3280 2219
2f99deb0
JA
2220 /*
2221 * Set up a drawing area and IOPS and bandwidth graphs
2222 */
2223 gdk_color_parse("white", &white);
2224 ge->graphs.drawing_area = gtk_drawing_area_new();
2225 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2226 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2227 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2228 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2229 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2230 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2231 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2232 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2233 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2234 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2235 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2236 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2237 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2238 ge->graphs.drawing_area);
2239 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2240 TRUE, TRUE, 0);
04cc6b77 2241
2f99deb0
JA
2242 setup_graphs(&ge->graphs);
2243
2244 /*
2245 * Set up alignments for widgets at the bottom of ui,
2246 * align bottom left, expand horizontally but not vertically
2247 */
2248 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2249 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2250 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2251 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2252 FALSE, FALSE, 0);
2253
2254 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
0420ba6a 2255
c36f98d9 2256 /*
2f99deb0
JA
2257 * Set up thread status progress bar
2258 */
2259 ge->thread_status_pb = gtk_progress_bar_new();
2260 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2261 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2262 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2263
2264
2265 return main_vbox;
2266}
2267
2268static GtkWidget *new_main_page(struct gui *ui)
2269{
2270 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2271 GdkColor white;
2272
2273 main_vbox = gtk_vbox_new(FALSE, 3);
2274
2275 /*
2276 * Set up alignments for widgets at the top of ui,
c36f98d9
SC
2277 * align top left, expand horizontally but not vertically
2278 */
2279 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2f99deb0 2280 ui->topvbox = gtk_vbox_new(FALSE, 0);
c36f98d9 2281 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2f99deb0 2282 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
c36f98d9 2283
2f99deb0
JA
2284 probe = gtk_frame_new("Run statistics");
2285 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
843ad237
JA
2286 probe_frame = gtk_vbox_new(FALSE, 3);
2287 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2288
2289 probe_box = gtk_hbox_new(FALSE, 3);
2f99deb0 2290 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
ca850992
JA
2291 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2292 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2293 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2294 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 2295
807f9971
JA
2296 /*
2297 * Only add this if we have a commit rate
2298 */
2299#if 0
3e47bd25
JA
2300 probe_box = gtk_hbox_new(FALSE, 3);
2301 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
2302
2303 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2304 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2305
3e47bd25
JA
2306 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2307 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 2308#endif
3e47bd25 2309
736f2dff 2310 /*
2fd3bb0e 2311 * Set up a drawing area and IOPS and bandwidth graphs
736f2dff 2312 */
aaa71f68 2313 gdk_color_parse("white", &white);
2f99deb0
JA
2314 ui->graphs.drawing_area = gtk_drawing_area_new();
2315 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2316 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2317 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2318 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2319 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2320 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2321 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2322 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2323 G_CALLBACK(on_config_drawing_area), &ui->graphs);
736f2dff
SC
2324 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2325 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2326 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2fd3bb0e 2327 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2f99deb0
JA
2328 ui->graphs.drawing_area);
2329 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
e164534f 2330 TRUE, TRUE, 0);
736f2dff 2331
2f99deb0 2332 setup_graphs(&ui->graphs);
2fd3bb0e 2333
c36f98d9
SC
2334 /*
2335 * Set up alignments for widgets at the bottom of ui,
2336 * align bottom left, expand horizontally but not vertically
2337 */
2338 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2339 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2340 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
2f99deb0 2341 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
e164534f 2342 FALSE, FALSE, 0);
c36f98d9 2343
3ec62ec4
JA
2344 /*
2345 * Set up thread status progress bar
2346 */
2347 ui->thread_status_pb = gtk_progress_bar_new();
2348 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 2349 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
2350 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2351
2f99deb0
JA
2352 return main_vbox;
2353}
2354
2355static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2356 guint page, gpointer data)
2357
2358{
2359 return TRUE;
2360}
2361
2362static void init_ui(int *argc, char **argv[], struct gui *ui)
2363{
2364 GtkSettings *settings;
2365 GtkUIManager *uimanager;
2366 GtkWidget *menu, *vbox;
2367
2368 /* Magical g*thread incantation, you just need this thread stuff.
2369 * Without it, the update that happens in gfio_update_thread_status
2370 * doesn't really happen in a timely fashion, you need expose events
2371 */
2372 if (!g_thread_supported())
2373 g_thread_init(NULL);
2374 gdk_threads_init();
2375
2376 gtk_init(argc, argv);
2377 settings = gtk_settings_get_default();
2378 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2379 g_type_init();
2380
2381 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2382 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2383 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2384
2385 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2386 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2387
2388 ui->vbox = gtk_vbox_new(FALSE, 0);
2389 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2390
2391 uimanager = gtk_ui_manager_new();
2392 menu = get_menubar_menu(ui->window, uimanager, ui);
2393 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2394
2395 ui->notebook = gtk_notebook_new();
2396 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2397 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2398
2399 vbox = new_main_page(ui);
2400
2401 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2402
9b260bdf 2403 gfio_ui_setup_log(ui);
3ec62ec4 2404
ff1f3280
SC
2405 gtk_widget_show_all(ui->window);
2406}
2407
8232e285 2408int main(int argc, char *argv[], char *envp[])
ff1f3280 2409{
8232e285
SC
2410 if (initialize_fio(envp))
2411 return 1;
0420ba6a
JA
2412 if (fio_init_options())
2413 return 1;
a1820207 2414
2f99deb0
JA
2415 memset(&main_ui, 0, sizeof(main_ui));
2416 INIT_FLIST_HEAD(&main_ui.list);
2417
2418 init_ui(&argc, &argv, &main_ui);
5b7573ab 2419
2839f0c6 2420 gdk_threads_enter();
ff1f3280 2421 gtk_main();
2839f0c6 2422 gdk_threads_leave();
ff1f3280
SC
2423 return 0;
2424}