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