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