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