gfio: first attempt at actually automatically starting backend server
[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);
1230 }
1231}
1232
f3074008 1233static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
25927259 1234 gpointer data)
f3074008 1235{
25927259
SC
1236 struct gui *ui = data;
1237
25927259 1238 gtk_widget_set_sensitive(ui->button[START_JOB_BUTTON], 0);
3ec62ec4 1239 start_job_thread(ui);
f3074008
SC
1240}
1241
df06f220
JA
1242static void file_open(GtkWidget *w, gpointer data);
1243
1244static void connect_clicked(GtkWidget *widget, gpointer data)
3e47bd25 1245{
3ec62ec4
JA
1246 struct gui *ui = data;
1247
1248 if (!ui->connected) {
df06f220
JA
1249 if (!ui->nr_job_files)
1250 file_open(widget, data);
8663ea65 1251 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No jobs running");
69406b92
JA
1252 if (!fio_clients_connect()) {
1253 pthread_create(&ui->t, NULL, job_thread, NULL);
1254 gtk_widget_set_sensitive(ui->button[CONNECT_BUTTON], 0);
1255 }
df06f220
JA
1256 } else {
1257 fio_clients_terminate();
3ec62ec4 1258 gfio_set_connected(ui, 0);
88432651 1259 clear_ui_info(ui);
df06f220 1260 }
3e47bd25
JA
1261}
1262
f3074008
SC
1263static void add_button(struct gui *ui, int i, GtkWidget *buttonbox,
1264 struct button_spec *buttonspec)
1265{
1266 ui->button[i] = gtk_button_new_with_label(buttonspec->buttontext);
1267 g_signal_connect(ui->button[i], "clicked", G_CALLBACK (buttonspec->f), ui);
3ec62ec4 1268 gtk_box_pack_start(GTK_BOX (ui->buttonbox), ui->button[i], FALSE, FALSE, 3);
f3074008 1269 gtk_widget_set_tooltip_text(ui->button[i], buttonspeclist[i].tooltiptext);
3e47bd25 1270 gtk_widget_set_sensitive(ui->button[i], !buttonspec->start_insensitive);
f3074008
SC
1271}
1272
1273static void add_buttons(struct gui *ui,
1274 struct button_spec *buttonlist,
1275 int nbuttons)
1276{
1277 int i;
1278
f3074008
SC
1279 for (i = 0; i < nbuttons; i++)
1280 add_button(ui, i, ui->buttonbox, &buttonlist[i]);
1281}
1282
0420ba6a
JA
1283static void on_info_bar_response(GtkWidget *widget, gint response,
1284 gpointer data)
1285{
1286 if (response == GTK_RESPONSE_OK) {
1287 gtk_widget_destroy(widget);
1288 ui.error_info_bar = NULL;
1289 }
1290}
1291
df06f220 1292void report_error(GError *error)
0420ba6a
JA
1293{
1294 if (ui.error_info_bar == NULL) {
1295 ui.error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
1296 GTK_RESPONSE_OK,
1297 NULL);
1298 g_signal_connect(ui.error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1299 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui.error_info_bar),
1300 GTK_MESSAGE_ERROR);
1301
1302 ui.error_label = gtk_label_new(error->message);
1303 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui.error_info_bar));
1304 gtk_container_add(GTK_CONTAINER(container), ui.error_label);
1305
1306 gtk_box_pack_start(GTK_BOX(ui.vbox), ui.error_info_bar, FALSE, FALSE, 0);
1307 gtk_widget_show_all(ui.vbox);
1308 } else {
1309 char buffer[256];
1310 snprintf(buffer, sizeof(buffer), "Failed to open file.");
1311 gtk_label_set(GTK_LABEL(ui.error_label), buffer);
1312 }
1313}
1314
b9f3c7ed
JA
1315static int get_connection_details(char **host, int *port, int *type,
1316 int *server_start)
a7a42ce1
JA
1317{
1318 GtkWidget *dialog, *box, *vbox, *hentry, *hbox, *frame, *pentry, *combo;
b9f3c7ed 1319 GtkWidget *button;
a7a42ce1
JA
1320 char *typeentry;
1321
1322 dialog = gtk_dialog_new_with_buttons("Connection details",
1323 GTK_WINDOW(ui.window),
1324 GTK_DIALOG_DESTROY_WITH_PARENT,
1325 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1326 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1327
1328 frame = gtk_frame_new("Hostname / socket name");
1329 vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1330 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1331
1332 box = gtk_vbox_new(FALSE, 6);
1333 gtk_container_add(GTK_CONTAINER(frame), box);
1334
1335 hbox = gtk_hbox_new(TRUE, 10);
1336 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1337 hentry = gtk_entry_new();
1338 gtk_entry_set_text(GTK_ENTRY(hentry), "localhost");
1339 gtk_box_pack_start(GTK_BOX(hbox), hentry, TRUE, TRUE, 0);
1340
1341 frame = gtk_frame_new("Port");
1342 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1343 box = gtk_vbox_new(FALSE, 10);
1344 gtk_container_add(GTK_CONTAINER(frame), box);
1345
1346 hbox = gtk_hbox_new(TRUE, 4);
1347 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1348 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1349
1350 frame = gtk_frame_new("Type");
1351 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1352 box = gtk_vbox_new(FALSE, 10);
1353 gtk_container_add(GTK_CONTAINER(frame), box);
1354
1355 hbox = gtk_hbox_new(TRUE, 4);
1356 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1357
b465562c
JA
1358 combo = gtk_combo_box_new_text();
1359 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv4");
1360 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "IPv6");
1361 gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "local socket");
a7a42ce1
JA
1362 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1363
1364 gtk_container_add(GTK_CONTAINER(hbox), combo);
1365
b9f3c7ed
JA
1366 frame = gtk_frame_new("Options");
1367 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1368 box = gtk_vbox_new(FALSE, 10);
1369 gtk_container_add(GTK_CONTAINER(frame), box);
1370
1371 hbox = gtk_hbox_new(TRUE, 4);
1372 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1373
1374 button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1375 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
1376 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.");
1377 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1378
a7a42ce1
JA
1379 gtk_widget_show_all(dialog);
1380
1381 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1382 gtk_widget_destroy(dialog);
1383 return 1;
1384 }
1385
1386 *host = strdup(gtk_entry_get_text(GTK_ENTRY(hentry)));
1387 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1388
b465562c 1389 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo));
a7a42ce1
JA
1390 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1391 *type = Fio_client_ipv4;
1392 else if (!strncmp(typeentry, "IPv6", 4))
1393 *type = Fio_client_ipv6;
1394 else
1395 *type = Fio_client_socket;
1396 g_free(typeentry);
1397
b9f3c7ed
JA
1398 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1399
a7a42ce1
JA
1400 gtk_widget_destroy(dialog);
1401 return 0;
1402}
1403
e0681f3e
JA
1404static void gfio_client_added(struct gui *ui, struct fio_client *client)
1405{
1406 struct gfio_client *gc;
1407
1408 gc = malloc(sizeof(*gc));
1409 memset(gc, 0, sizeof(*gc));
1410 gc->ui = ui;
1411
1412 client->client_data = gc;
1413}
1414
0420ba6a
JA
1415static void file_open(GtkWidget *w, gpointer data)
1416{
1417 GtkWidget *dialog;
63a130b7 1418 struct gui *ui = data;
0420ba6a
JA
1419 GSList *filenames, *fn_glist;
1420 GtkFileFilter *filter;
a7a42ce1 1421 char *host;
b9f3c7ed 1422 int port, type, server_start;
0420ba6a
JA
1423
1424 dialog = gtk_file_chooser_dialog_new("Open File",
63a130b7 1425 GTK_WINDOW(ui->window),
0420ba6a
JA
1426 GTK_FILE_CHOOSER_ACTION_OPEN,
1427 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1428 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1429 NULL);
1430 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1431
1432 filter = gtk_file_filter_new();
1433 gtk_file_filter_add_pattern(filter, "*.fio");
1434 gtk_file_filter_add_pattern(filter, "*.job");
1435 gtk_file_filter_add_mime_type(filter, "text/fio");
1436 gtk_file_filter_set_name(filter, "Fio job file");
1437 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1438
1439 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1440 gtk_widget_destroy(dialog);
1441 return;
1442 }
1443
1444 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
a7a42ce1
JA
1445
1446 gtk_widget_destroy(dialog);
1447
b9f3c7ed 1448 if (get_connection_details(&host, &port, &type, &server_start))
a7a42ce1
JA
1449 goto err;
1450
0420ba6a
JA
1451 filenames = fn_glist;
1452 while (filenames != NULL) {
e0681f3e
JA
1453 struct fio_client *client;
1454
63a130b7
JA
1455 ui->job_files = realloc(ui->job_files, (ui->nr_job_files + 1) * sizeof(char *));
1456 ui->job_files[ui->nr_job_files] = strdup(filenames->data);
1457 ui->nr_job_files++;
0420ba6a 1458
e0681f3e
JA
1459 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1460 if (!client) {
df06f220
JA
1461 GError *error;
1462
1463 error = g_error_new(g_quark_from_string("fio"), 1,
1464 "Failed to add client %s", host);
0420ba6a
JA
1465 report_error(error);
1466 g_error_free(error);
0420ba6a 1467 }
63a130b7 1468 gfio_client_added(ui, client);
0420ba6a
JA
1469
1470 g_free(filenames->data);
1471 filenames = g_slist_next(filenames);
1472 }
a7a42ce1 1473 free(host);
63a130b7
JA
1474
1475 if (server_start)
1476 gfio_start_server(ui);
a7a42ce1 1477err:
0420ba6a 1478 g_slist_free(fn_glist);
0420ba6a
JA
1479}
1480
1481static void file_save(GtkWidget *w, gpointer data)
1482{
63a130b7 1483 struct gui *ui = data;
0420ba6a
JA
1484 GtkWidget *dialog;
1485
1486 dialog = gtk_file_chooser_dialog_new("Save File",
63a130b7 1487 GTK_WINDOW(ui->window),
0420ba6a
JA
1488 GTK_FILE_CHOOSER_ACTION_SAVE,
1489 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1490 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1491 NULL);
1492
1493 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1494 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1495
1496 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1497 char *filename;
1498
1499 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1500 // save_job_file(filename);
1501 g_free(filename);
1502 }
1503 gtk_widget_destroy(dialog);
1504}
1505
9b260bdf
JA
1506static void view_log_destroy(GtkWidget *w, gpointer data)
1507{
1508 struct gui *ui = (struct gui *) data;
1509
1510 gtk_widget_ref(ui->log_tree);
1511 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1512 gtk_widget_destroy(w);
4cbe7211 1513 ui->log_view = NULL;
9b260bdf
JA
1514}
1515
1516static void view_log(GtkWidget *w, gpointer data)
1517{
4cbe7211
JA
1518 GtkWidget *win, *scroll, *vbox, *box;
1519 struct gui *ui = (struct gui *) data;
9b260bdf 1520
4cbe7211
JA
1521 if (ui->log_view)
1522 return;
1523
1524 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
9b260bdf 1525 gtk_window_set_title(GTK_WINDOW(win), "Log");
4cbe7211 1526 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
9b260bdf 1527
4cbe7211
JA
1528 scroll = gtk_scrolled_window_new(NULL, NULL);
1529
1530 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1531
1532 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1533
1534 box = gtk_hbox_new(TRUE, 0);
1535 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1536 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1537 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1538
1539 vbox = gtk_vbox_new(TRUE, 5);
1540 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
9b260bdf 1541
4cbe7211 1542 gtk_container_add(GTK_CONTAINER(win), vbox);
9b260bdf
JA
1543 gtk_widget_show_all(win);
1544}
1545
46974a7d
JA
1546static void preferences(GtkWidget *w, gpointer data)
1547{
1548 GtkWidget *dialog, *frame, *box, **buttons;
1549 int i;
1550
1551 dialog = gtk_dialog_new_with_buttons("Preferences",
1552 GTK_WINDOW(ui.window),
1553 GTK_DIALOG_DESTROY_WITH_PARENT,
1554 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1555 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1556 NULL);
1557
0b8d11ed 1558 frame = gtk_frame_new("Debug logging");
46974a7d
JA
1559 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1560 box = gtk_hbox_new(FALSE, 6);
1561 gtk_container_add(GTK_CONTAINER(frame), box);
1562
1563 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1564
1565 for (i = 0; i < FD_DEBUG_MAX; i++) {
1566 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
0b8d11ed 1567 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
46974a7d
JA
1568 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1569 }
1570
1571 gtk_widget_show_all(dialog);
1572
1573 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1574 gtk_widget_destroy(dialog);
1575 return;
1576 }
1577
1578 for (i = 0; i < FD_DEBUG_MAX; i++) {
1579 int set;
1580
1581 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
1582 if (set)
1583 fio_debug |= (1UL << i);
1584 }
1585
1586 gtk_widget_destroy(dialog);
1587}
1588
0420ba6a
JA
1589static void about_dialog(GtkWidget *w, gpointer data)
1590{
1591 gtk_show_about_dialog(NULL,
1592 "program-name", "gfio",
1593 "comments", "Gtk2 UI for fio",
1594 "license", "GPLv2",
1595 "version", fio_version_string,
1596 "copyright", "Jens Axboe <axboe@kernel.dk> 2012",
1597 "logo-icon-name", "fio",
1598 /* Must be last: */
1599 NULL, NULL,
1600 NULL);
1601}
1602
1603static GtkActionEntry menu_items[] = {
46974a7d 1604 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
9b260bdf 1605 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
46974a7d
JA
1606 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
1607 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
1608 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
1609 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
9b260bdf 1610 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
46974a7d
JA
1611 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
1612 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
0420ba6a 1613};
3e47bd25 1614static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
0420ba6a
JA
1615
1616static const gchar *ui_string = " \
1617 <ui> \
1618 <menubar name=\"MainMenu\"> \
1619 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1620 <menuitem name=\"Open\" action=\"OpenFile\" /> \
1621 <menuitem name=\"Save\" action=\"SaveFile\" /> \
1622 <separator name=\"Separator\"/> \
46974a7d
JA
1623 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
1624 <separator name=\"Separator2\"/> \
0420ba6a
JA
1625 <menuitem name=\"Quit\" action=\"Quit\" /> \
1626 </menu> \
9b260bdf
JA
1627 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
1628 <menuitem name=\"Log\" action=\"ViewLog\" /> \
1629 </menu>\
0420ba6a
JA
1630 <menu name=\"Help\" action=\"HelpMenuAction\"> \
1631 <menuitem name=\"About\" action=\"About\" /> \
1632 </menu> \
1633 </menubar> \
1634 </ui> \
1635";
1636
4cbe7211
JA
1637static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
1638 struct gui *ui)
0420ba6a
JA
1639{
1640 GtkActionGroup *action_group = gtk_action_group_new("Menu");
1641 GError *error = 0;
1642
1643 action_group = gtk_action_group_new("Menu");
4cbe7211 1644 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
0420ba6a
JA
1645
1646 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1647 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
1648
1649 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
1650 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
1651}
1652
1653void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
1654 GtkWidget *vbox, GtkUIManager *ui_manager)
1655{
1656 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
1657}
1658
ff1f3280
SC
1659static void init_ui(int *argc, char **argv[], struct gui *ui)
1660{
0420ba6a
JA
1661 GtkSettings *settings;
1662 GtkUIManager *uimanager;
843ad237 1663 GtkWidget *menu, *probe, *probe_frame, *probe_box;
0420ba6a
JA
1664
1665 memset(ui, 0, sizeof(*ui));
45032dd8 1666
2839f0c6 1667 /* Magical g*thread incantation, you just need this thread stuff.
04cc6b77 1668 * Without it, the update that happens in gfio_update_thread_status
2839f0c6
SC
1669 * doesn't really happen in a timely fashion, you need expose events
1670 */
ed727a46 1671 if (!g_thread_supported())
2839f0c6
SC
1672 g_thread_init(NULL);
1673 gdk_threads_init();
1674
ff1f3280 1675 gtk_init(argc, argv);
0420ba6a
JA
1676 settings = gtk_settings_get_default();
1677 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
1678 g_type_init();
ff1f3280
SC
1679
1680 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1681 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
1682 gtk_window_set_default_size(GTK_WINDOW(ui->window), 700, 500);
1683
0420ba6a
JA
1684 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
1685 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
ff1f3280 1686
5b7573ab
SC
1687 ui->vbox = gtk_vbox_new(FALSE, 0);
1688 gtk_container_add(GTK_CONTAINER (ui->window), ui->vbox);
04cc6b77 1689
0420ba6a 1690 uimanager = gtk_ui_manager_new();
4cbe7211 1691 menu = get_menubar_menu(ui->window, uimanager, ui);
0420ba6a
JA
1692 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
1693
c36f98d9
SC
1694 /*
1695 * Set up alignments for widgets at the top of ui,
1696 * align top left, expand horizontally but not vertically
1697 */
1698 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
3ec62ec4 1699 ui->topvbox = gtk_vbox_new(FALSE, 3);
c36f98d9 1700 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
e164534f 1701 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->topalign, FALSE, FALSE, 0);
c36f98d9 1702
3e47bd25 1703 probe = gtk_frame_new("Job");
843ad237
JA
1704 gtk_box_pack_start(GTK_BOX(ui->topvbox), probe, TRUE, FALSE, 3);
1705 probe_frame = gtk_vbox_new(FALSE, 3);
1706 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
1707
1708 probe_box = gtk_hbox_new(FALSE, 3);
1709 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
843ad237
JA
1710 ui->probe.hostname = new_info_label_in_frame(probe_box, "Host");
1711 ui->probe.os = new_info_label_in_frame(probe_box, "OS");
1712 ui->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
1713 ui->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
1714
3e47bd25
JA
1715 probe_box = gtk_hbox_new(FALSE, 3);
1716 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971 1717
ca850992
JA
1718 ui->eta.name = new_info_entry_in_frame(probe_box, "Name");
1719 ui->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
1720 ui->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
1721 ui->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
1722 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
1723 ui->eta.files = new_info_entry_in_frame(probe_box, "Open files");
3e47bd25
JA
1724
1725 probe_box = gtk_hbox_new(FALSE, 3);
1726 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
ca850992
JA
1727 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
1728 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
1729 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
1730 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3e47bd25 1731
807f9971
JA
1732 /*
1733 * Only add this if we have a commit rate
1734 */
1735#if 0
3e47bd25
JA
1736 probe_box = gtk_hbox_new(FALSE, 3);
1737 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
807f9971
JA
1738
1739 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
1740 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
1741
3e47bd25
JA
1742 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
1743 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
807f9971 1744#endif
3e47bd25 1745
736f2dff
SC
1746 /*
1747 * Add a text box for text op messages
1748 */
1749 ui->textview = gtk_text_view_new();
1750 ui->text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui->textview));
1751 gtk_text_buffer_set_text(ui->text, "", -1);
1752 gtk_text_view_set_editable(GTK_TEXT_VIEW(ui->textview), FALSE);
1753 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(ui->textview), FALSE);
1754 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1755 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
1756 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1757 gtk_container_add(GTK_CONTAINER(ui->scrolled_window), ui->textview);
e164534f
SC
1758 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->scrolled_window,
1759 TRUE, TRUE, 0);
736f2dff 1760
c36f98d9
SC
1761 /*
1762 * Set up alignments for widgets at the bottom of ui,
1763 * align bottom left, expand horizontally but not vertically
1764 */
1765 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
1766 ui->buttonbox = gtk_hbox_new(FALSE, 0);
1767 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
e164534f
SC
1768 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->bottomalign,
1769 FALSE, FALSE, 0);
c36f98d9 1770
f3074008 1771 add_buttons(ui, buttonspeclist, ARRAYSIZE(buttonspeclist));
3ec62ec4
JA
1772
1773 /*
1774 * Set up thread status progress bar
1775 */
1776 ui->thread_status_pb = gtk_progress_bar_new();
1777 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
8663ea65 1778 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
3ec62ec4
JA
1779 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
1780
9b260bdf 1781 gfio_ui_setup_log(ui);
3ec62ec4 1782
ff1f3280
SC
1783 gtk_widget_show_all(ui->window);
1784}
1785
8232e285 1786int main(int argc, char *argv[], char *envp[])
ff1f3280 1787{
8232e285
SC
1788 if (initialize_fio(envp))
1789 return 1;
0420ba6a
JA
1790 if (fio_init_options())
1791 return 1;
a1820207 1792
ff1f3280 1793 init_ui(&argc, &argv, &ui);
5b7573ab 1794
2839f0c6 1795 gdk_threads_enter();
ff1f3280 1796 gtk_main();
2839f0c6 1797 gdk_threads_leave();
ff1f3280
SC
1798 return 0;
1799}