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