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