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