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