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