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