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