blob: 13e3684bf5ad3ce36b11f3a64877a3922ab52390 [file] [log] [blame]
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
Jens Axboec0187f32012-03-06 15:39:15 +01005 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01006 *
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 */
Stephen M. Cameron8232e282012-02-24 08:17:31 +010024#include <locale.h>
Stephen M. Cameron60f6b332012-02-24 08:17:32 +010025#include <malloc.h>
Jens Axboe6b79c802012-03-08 10:51:36 +010026#include <string.h>
Stephen M. Cameron8232e282012-02-24 08:17:31 +010027
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +010028#include <glib.h>
Jens Axboe2fd3bb02012-03-07 08:07:39 +010029#include <cairo.h>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +010030#include <gtk/gtk.h>
31
Stephen M. Cameron8232e282012-02-24 08:17:31 +010032#include "fio.h"
Jens Axboe2fd3bb02012-03-07 08:07:39 +010033#include "graph.h"
Stephen M. Cameron8232e282012-02-24 08:17:31 +010034
Jens Axboe63a130b2012-03-06 20:08:59 +010035static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010036static const char *gfio_graph_font;
Jens Axboe63a130b2012-03-06 20:08:59 +010037
Jens Axboe6b79c802012-03-08 10:51:36 +010038static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010039
Stephen M. Cameronf3074002012-02-24 08:17:30 +010040#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
41
42typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
43
Jens Axboe3e47bd22012-02-29 13:45:02 +010044static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010045static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010046static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010047
48static struct button_spec {
49 const char *buttontext;
50 clickfunction f;
51 const char *tooltiptext;
Jens Axboe3e47bd22012-02-29 13:45:02 +010052 const int start_insensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010053} buttonspeclist[] = {
Jens Axboe3e47bd22012-02-29 13:45:02 +010054#define CONNECT_BUTTON 0
Jens Axboeb9d2f302012-03-08 20:36:28 +010055#define SEND_BUTTON 1
56#define START_JOB_BUTTON 2
Jens Axboe3e47bd22012-02-29 13:45:02 +010057 { "Connect", connect_clicked, "Connect to host", 0 },
Jens Axboeb9d2f302012-03-08 20:36:28 +010058 { "Send", send_clicked, "Send job description to host", 1 },
59 { "Start Job", start_job_clicked,
Jens Axboe2f99deb2012-03-09 14:37:29 +010060 "Start the current job on the server", 1 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010061};
62
Jens Axboe843ad232012-02-29 11:44:53 +010063struct probe_widget {
64 GtkWidget *hostname;
65 GtkWidget *os;
66 GtkWidget *arch;
67 GtkWidget *fio_ver;
68};
69
Jens Axboe3e47bd22012-02-29 13:45:02 +010070struct eta_widget {
Jens Axboe807f9972012-03-02 10:25:24 +010071 GtkWidget *name;
72 GtkWidget *iotype;
73 GtkWidget *ioengine;
74 GtkWidget *iodepth;
Jens Axboe3e47bd22012-02-29 13:45:02 +010075 GtkWidget *jobs;
76 GtkWidget *files;
77 GtkWidget *read_bw;
78 GtkWidget *read_iops;
79 GtkWidget *cr_bw;
80 GtkWidget *cr_iops;
81 GtkWidget *write_bw;
82 GtkWidget *write_iops;
83 GtkWidget *cw_bw;
84 GtkWidget *cw_iops;
85};
86
Jens Axboe2f99deb2012-03-09 14:37:29 +010087struct gfio_graphs {
88#define DRAWING_AREA_XDIM 1000
89#define DRAWING_AREA_YDIM 400
90 GtkWidget *drawing_area;
91 int drawing_area_xdim;
92 int drawing_area_ydim;
93
94 struct graph *iops_graph;
95 struct graph *bandwidth_graph;
96};
97
98/*
99 * Main window widgets and data
100 */
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100101struct gui {
102 GtkWidget *window;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100103 GtkWidget *vbox;
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +0100104 GtkWidget *topvbox;
105 GtkWidget *topalign;
106 GtkWidget *bottomalign;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100107 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100108 GtkWidget *buttonbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100109 GtkWidget *scrolled_window;
110 GtkWidget *notebook;
111 GtkWidget *error_info_bar;
112 GtkWidget *error_label;
113 GtkListStore *log_model;
114 GtkWidget *log_tree;
115 GtkWidget *log_view;
116 struct gfio_graphs graphs;
117 struct probe_widget probe;
118 struct eta_widget eta;
119 pthread_t server_t;
120
Jens Axboea9eccde2012-03-09 14:59:42 +0100121 pthread_t t;
122 int handler_running;
123
Jens Axboe2f99deb2012-03-09 14:37:29 +0100124 struct flist_head list;
125} main_ui;
126
127/*
128 * Notebook entry
129 */
130struct gui_entry {
131 struct flist_head list;
132 struct gui *ui;
133
134 GtkWidget *vbox;
135 GtkWidget *topvbox;
136 GtkWidget *topalign;
137 GtkWidget *bottomalign;
138 GtkWidget *thread_status_pb;
139 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100140 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100141 GtkWidget *scrolled_window;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100142 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100143 GtkWidget *error_info_bar;
144 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100145 GtkWidget *results_notebook;
146 GtkWidget *results_window;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100147 GtkListStore *log_model;
148 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100149 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100150 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100151 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100152 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100153 GtkWidget *page_label;
154 gint page_num;
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100155 int connected;
Jens Axboe0420ba62012-02-29 11:16:52 +0100156
Jens Axboeb9d2f302012-03-08 20:36:28 +0100157 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100158 int nr_job_files;
159 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100160};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100161
Jens Axboee0681f32012-03-06 12:14:42 +0100162struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100163 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100164 struct fio_client *client;
Jens Axboee0681f32012-03-06 12:14:42 +0100165 GtkWidget *results_widget;
166 GtkWidget *disk_util_frame;
Jens Axboe6b79c802012-03-08 10:51:36 +0100167 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100168 unsigned int job_added;
169 struct thread_options o;
Jens Axboee0681f32012-03-06 12:14:42 +0100170};
171
Jens Axboe9988ca72012-03-09 15:14:06 +0100172static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
173static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +0100174void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +0100175
Jens Axboe2f99deb2012-03-09 14:37:29 +0100176static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100177{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100178 struct graph *g;
179
180 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
181 graph_title(g, "IOPS");
182 graph_x_title(g, "Time (secs)");
183 graph_y_title(g, "IOs / sec");
184 graph_add_label(g, "Read IOPS");
185 graph_add_label(g, "Write IOPS");
186 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
187 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
188 line_graph_set_data_count_limit(g, 100);
189 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100190}
191
Jens Axboe2f99deb2012-03-09 14:37:29 +0100192static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100193{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100194 struct graph *g;
195
196 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
197 graph_title(g, "Bandwidth");
198 graph_x_title(g, "Time (secs)");
199 graph_y_title(g, "Kbytes / sec");
200 graph_add_label(g, "Read Bandwidth");
201 graph_add_label(g, "Write Bandwidth");
202 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
203 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
204 line_graph_set_data_count_limit(g, 100);
205 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100206}
207
Jens Axboe2f99deb2012-03-09 14:37:29 +0100208static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100209{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100210 g->iops_graph = setup_iops_graph();
211 g->bandwidth_graph = setup_bandwidth_graph();
212}
213
214static void clear_ge_ui_info(struct gui_entry *ge)
215{
216 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
217 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
218 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
219 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
220 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
221 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
225 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
226 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
227 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
228 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
229 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100230}
231
Jens Axboe3650a3c2012-03-05 14:09:03 +0100232static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
233{
234 GtkWidget *entry, *frame;
235
236 frame = gtk_frame_new(label);
237 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100238 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100239 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
240 gtk_container_add(GTK_CONTAINER(frame), entry);
241
242 return entry;
243}
244
245static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
246{
247 GtkWidget *label_widget;
248 GtkWidget *frame;
249
250 frame = gtk_frame_new(label);
251 label_widget = gtk_label_new(NULL);
252 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
253 gtk_container_add(GTK_CONTAINER(frame), label_widget);
254
255 return label_widget;
256}
257
258static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
259{
260 GtkWidget *button, *box;
261
262 box = gtk_hbox_new(FALSE, 3);
263 gtk_container_add(GTK_CONTAINER(hbox), box);
264
265 button = gtk_spin_button_new_with_range(min, max, 1.0);
266 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
267
268 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
269 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
270
271 return button;
272}
273
Jens Axboe2f99deb2012-03-09 14:37:29 +0100274static void gfio_set_connected(struct gui_entry *ge, int connected)
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100275{
276 if (connected) {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100277 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
278 ge->connected = 1;
279 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
280 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100281 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100282 ge->connected = 0;
283 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
284 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
285 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
286 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100287 }
288}
289
Jens Axboe3650a3c2012-03-05 14:09:03 +0100290static void label_set_int_value(GtkWidget *entry, unsigned int val)
291{
292 char tmp[80];
293
294 sprintf(tmp, "%u", val);
295 gtk_label_set_text(GTK_LABEL(entry), tmp);
296}
297
298static void entry_set_int_value(GtkWidget *entry, unsigned int val)
299{
300 char tmp[80];
301
302 sprintf(tmp, "%u", val);
303 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
304}
305
Jens Axboea2697902012-03-05 16:43:49 +0100306#define ALIGN_LEFT 1
307#define ALIGN_RIGHT 2
308#define INVISIBLE 4
309#define UNSORTABLE 8
310
311GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
312{
313 GtkCellRenderer *renderer;
314 GtkTreeViewColumn *col;
315 double xalign = 0.0; /* left as default */
316 PangoAlignment align;
317 gboolean visible;
318
319 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
320 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
321 PANGO_ALIGN_CENTER;
322 visible = !(flags & INVISIBLE);
323
324 renderer = gtk_cell_renderer_text_new();
325 col = gtk_tree_view_column_new();
326
327 gtk_tree_view_column_set_title(col, title);
328 if (!(flags & UNSORTABLE))
329 gtk_tree_view_column_set_sort_column_id(col, index);
330 gtk_tree_view_column_set_resizable(col, TRUE);
331 gtk_tree_view_column_pack_start(col, renderer, TRUE);
332 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
333 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
334 switch (align) {
335 case PANGO_ALIGN_LEFT:
336 xalign = 0.0;
337 break;
338 case PANGO_ALIGN_CENTER:
339 xalign = 0.5;
340 break;
341 case PANGO_ALIGN_RIGHT:
342 xalign = 1.0;
343 break;
344 }
345 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
346 gtk_tree_view_column_set_visible(col, visible);
347 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
348 return col;
349}
350
Jens Axboe9b260bd2012-03-06 11:02:52 +0100351static void gfio_ui_setup_log(struct gui *ui)
352{
353 GtkTreeSelection *selection;
354 GtkListStore *model;
355 GtkWidget *tree_view;
356
357 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
358
359 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
360 gtk_widget_set_can_focus(tree_view, FALSE);
361
362 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
363 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100364 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
365 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100366
367 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
368 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
369 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100370 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100371
372 ui->log_model = model;
373 ui->log_tree = tree_view;
374}
375
Jens Axboea2697902012-03-05 16:43:49 +0100376static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
377 fio_fp64_t *plist,
378 unsigned int len,
379 const char *base,
380 unsigned int scale)
381{
382 GType types[FIO_IO_U_LIST_MAX_LEN];
383 GtkWidget *tree_view;
384 GtkTreeSelection *selection;
385 GtkListStore *model;
386 GtkTreeIter iter;
387 int i;
388
389 for (i = 0; i < len; i++)
390 types[i] = G_TYPE_INT;
391
392 model = gtk_list_store_newv(len, types);
393
394 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
395 gtk_widget_set_can_focus(tree_view, FALSE);
396
Jens Axboe661f7412012-03-06 13:55:45 +0100397 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
398 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
399
Jens Axboea2697902012-03-05 16:43:49 +0100400 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
401 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
402
403 for (i = 0; i < len; i++) {
404 char fbuf[8];
405
406 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
407 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
408 }
409
410 gtk_list_store_append(model, &iter);
411
Jens Axboee0681f32012-03-06 12:14:42 +0100412 for (i = 0; i < len; i++) {
413 if (scale)
414 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100415 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100416 }
Jens Axboea2697902012-03-05 16:43:49 +0100417
418 return tree_view;
419}
420
421static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
422 int ddir)
423{
424 unsigned int *io_u_plat = ts->io_u_plat[ddir];
425 unsigned long nr = ts->clat_stat[ddir].samples;
426 fio_fp64_t *plist = ts->percentile_list;
427 unsigned int *ovals, len, minv, maxv, scale_down;
428 const char *base;
429 GtkWidget *tree_view, *frame, *hbox;
430 char tmp[64];
431
432 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
433 if (!len)
434 goto out;
435
436 /*
437 * We default to usecs, but if the value range is such that we
438 * should scale down to msecs, do that.
439 */
440 if (minv > 2000 && maxv > 99999) {
441 scale_down = 1;
442 base = "msec";
443 } else {
444 scale_down = 0;
445 base = "usec";
446 }
447
448 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
449
450 sprintf(tmp, "Completion percentiles (%s)", base);
451 frame = gtk_frame_new(tmp);
452 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
453
454 hbox = gtk_hbox_new(FALSE, 3);
455 gtk_container_add(GTK_CONTAINER(frame), hbox);
456
457 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
458out:
459 if (ovals)
460 free(ovals);
461}
462
Jens Axboe3650a3c2012-03-05 14:09:03 +0100463static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
464 unsigned long max, double mean, double dev)
465{
466 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100467 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100468 char *minp, *maxp;
469 char tmp[64];
470
471 if (!usec_to_msec(&min, &max, &mean, &dev))
472 base = "(msec)";
473
474 minp = num2str(min, 6, 1, 0);
475 maxp = num2str(max, 6, 1, 0);
476
Jens Axboe3650a3c2012-03-05 14:09:03 +0100477 sprintf(tmp, "%s %s", name, base);
478 frame = gtk_frame_new(tmp);
479 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
480
Jens Axboe3650a3c2012-03-05 14:09:03 +0100481 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100482 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100483
484 label = new_info_label_in_frame(hbox, "Minimum");
485 gtk_label_set_text(GTK_LABEL(label), minp);
486 label = new_info_label_in_frame(hbox, "Maximum");
487 gtk_label_set_text(GTK_LABEL(label), maxp);
488 label = new_info_label_in_frame(hbox, "Average");
489 sprintf(tmp, "%5.02f", mean);
490 gtk_label_set_text(GTK_LABEL(label), tmp);
491 label = new_info_label_in_frame(hbox, "Standard deviation");
492 sprintf(tmp, "%5.02f", dev);
493 gtk_label_set_text(GTK_LABEL(label), tmp);
494
495 free(minp);
496 free(maxp);
497
498}
499
Jens Axboeca850992012-03-05 20:04:43 +0100500#define GFIO_CLAT 1
501#define GFIO_SLAT 2
502#define GFIO_LAT 4
503
Jens Axboe3650a3c2012-03-05 14:09:03 +0100504static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
505 struct thread_stat *ts, int ddir)
506{
507 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100508 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100509 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100510 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100511 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100512 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100513 char *io_p, *bw_p, *iops_p;
514 int i2p;
515
516 if (!ts->runtime[ddir])
517 return;
518
519 i2p = is_power_of_2(rs->kb_base);
520 runt = ts->runtime[ddir];
521
522 bw = (1000 * ts->io_bytes[ddir]) / runt;
523 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
524 bw_p = num2str(bw, 6, 1, i2p);
525
526 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
527 iops_p = num2str(iops, 6, 1, 0);
528
529 box = gtk_hbox_new(FALSE, 3);
530 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
531
532 frame = gtk_frame_new(ddir_label[ddir]);
533 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
534
Jens Axboe0b761302012-03-05 20:44:11 +0100535 main_vbox = gtk_vbox_new(FALSE, 3);
536 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100537
538 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100539 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100540
541 label = new_info_label_in_frame(box, "IO");
542 gtk_label_set_text(GTK_LABEL(label), io_p);
543 label = new_info_label_in_frame(box, "Bandwidth");
544 gtk_label_set_text(GTK_LABEL(label), bw_p);
545 label = new_info_label_in_frame(box, "IOPS");
546 gtk_label_set_text(GTK_LABEL(label), iops_p);
547 label = new_info_label_in_frame(box, "Runtime (msec)");
548 label_set_int_value(label, ts->runtime[ddir]);
549
Jens Axboee0681f32012-03-06 12:14:42 +0100550 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100551 double p_of_agg = 100.0;
552 const char *bw_str = "KB";
553 char tmp[32];
554
555 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100556 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100557 if (p_of_agg > 100.0)
558 p_of_agg = 100.0;
559 }
560
Jens Axboee0681f32012-03-06 12:14:42 +0100561 if (mean[0] > 999999.9) {
562 min[0] /= 1000.0;
563 max[0] /= 1000.0;
564 mean[0] /= 1000.0;
565 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100566 bw_str = "MB";
567 }
568
Jens Axboe0b761302012-03-05 20:44:11 +0100569 sprintf(tmp, "Bandwidth (%s)", bw_str);
570 frame = gtk_frame_new(tmp);
571 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100572
Jens Axboe0b761302012-03-05 20:44:11 +0100573 box = gtk_hbox_new(FALSE, 3);
574 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100575
Jens Axboe0b761302012-03-05 20:44:11 +0100576 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100577 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100578 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100579 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100580 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100581 sprintf(tmp, "%3.2f%%", p_of_agg);
582 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100583 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100584 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100585 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100586 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100587 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100588 gtk_label_set_text(GTK_LABEL(label), tmp);
589 }
590
Jens Axboee0681f32012-03-06 12:14:42 +0100591 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100592 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100593 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100594 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100595 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100596 flags |= GFIO_LAT;
597
598 if (flags) {
599 frame = gtk_frame_new("Latency");
600 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
601
602 vbox = gtk_vbox_new(FALSE, 3);
603 gtk_container_add(GTK_CONTAINER(frame), vbox);
604
605 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100606 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100607 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100608 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100609 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100610 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100611 }
612
613 if (ts->clat_percentiles)
614 gfio_show_clat_percentiles(main_vbox, ts, ddir);
615
616
Jens Axboe3650a3c2012-03-05 14:09:03 +0100617 free(io_p);
618 free(bw_p);
619 free(iops_p);
620}
621
Jens Axboee5bd1342012-03-05 21:38:12 +0100622static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
623 const char **labels)
624{
625 GtkWidget *tree_view;
626 GtkTreeSelection *selection;
627 GtkListStore *model;
628 GtkTreeIter iter;
629 GType *types;
630 int i, skipped;
631
632 /*
633 * Check if all are empty, in which case don't bother
634 */
635 for (i = 0, skipped = 0; i < num; i++)
636 if (lat[i] <= 0.0)
637 skipped++;
638
639 if (skipped == num)
640 return NULL;
641
642 types = malloc(num * sizeof(GType));
643
644 for (i = 0; i < num; i++)
645 types[i] = G_TYPE_STRING;
646
647 model = gtk_list_store_newv(num, types);
648 free(types);
649 types = NULL;
650
651 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
652 gtk_widget_set_can_focus(tree_view, FALSE);
653
Jens Axboe661f7412012-03-06 13:55:45 +0100654 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
655 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
656
Jens Axboee5bd1342012-03-05 21:38:12 +0100657 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
658 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
659
660 for (i = 0; i < num; i++)
661 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
662
663 gtk_list_store_append(model, &iter);
664
665 for (i = 0; i < num; i++) {
666 char fbuf[32];
667
668 if (lat[i] <= 0.0)
669 sprintf(fbuf, "0.00");
670 else
671 sprintf(fbuf, "%3.2f%%", lat[i]);
672
673 gtk_list_store_set(model, &iter, i, fbuf, -1);
674 }
675
676 return tree_view;
677}
678
679static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
680{
681 GtkWidget *box, *frame, *tree_view;
682 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
683 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
684 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
685 "250", "500", "750", "1000", };
686 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
687 "250", "500", "750", "1000", "2000",
688 ">= 2000", };
689
690 stat_calc_lat_u(ts, io_u_lat_u);
691 stat_calc_lat_m(ts, io_u_lat_m);
692
693 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
694 if (tree_view) {
695 frame = gtk_frame_new("Latency buckets (usec)");
696 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
697
698 box = gtk_hbox_new(FALSE, 3);
699 gtk_container_add(GTK_CONTAINER(frame), box);
700 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
701 }
702
703 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
704 if (tree_view) {
705 frame = gtk_frame_new("Latency buckets (msec)");
706 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
707
708 box = gtk_hbox_new(FALSE, 3);
709 gtk_container_add(GTK_CONTAINER(frame), box);
710 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
711 }
712}
713
Jens Axboe2e331012012-03-05 22:07:54 +0100714static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
715{
716 GtkWidget *box, *frame, *entry;
717 double usr_cpu, sys_cpu;
718 unsigned long runtime;
719 char tmp[32];
720
721 runtime = ts->total_run_time;
722 if (runtime) {
723 double runt = (double) runtime;
724
725 usr_cpu = (double) ts->usr_time * 100 / runt;
726 sys_cpu = (double) ts->sys_time * 100 / runt;
727 } else {
728 usr_cpu = 0;
729 sys_cpu = 0;
730 }
731
732 frame = gtk_frame_new("OS resources");
733 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
734
735 box = gtk_hbox_new(FALSE, 3);
736 gtk_container_add(GTK_CONTAINER(frame), box);
737
738 entry = new_info_entry_in_frame(box, "User CPU");
739 sprintf(tmp, "%3.2f%%", usr_cpu);
740 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
741 entry = new_info_entry_in_frame(box, "System CPU");
742 sprintf(tmp, "%3.2f%%", sys_cpu);
743 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
744 entry = new_info_entry_in_frame(box, "Context switches");
745 entry_set_int_value(entry, ts->ctx);
746 entry = new_info_entry_in_frame(box, "Major faults");
747 entry_set_int_value(entry, ts->majf);
748 entry = new_info_entry_in_frame(box, "Minor faults");
749 entry_set_int_value(entry, ts->minf);
750}
Jens Axboe19998db2012-03-06 09:17:59 +0100751static void gfio_add_sc_depths_tree(GtkListStore *model,
752 struct thread_stat *ts, unsigned int len,
753 int submit)
754{
755 double io_u_dist[FIO_IO_U_MAP_NR];
756 GtkTreeIter iter;
757 /* Bits 0, and 3-8 */
758 const int add_mask = 0x1f9;
759 int i, j;
760
761 if (submit)
762 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
763 else
764 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
765
766 gtk_list_store_append(model, &iter);
767
768 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
769
770 for (i = 1, j = 0; i < len; i++) {
771 char fbuf[32];
772
773 if (!(add_mask & (1UL << (i - 1))))
774 sprintf(fbuf, "0.0%%");
775 else {
776 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
777 j++;
778 }
779
780 gtk_list_store_set(model, &iter, i, fbuf, -1);
781 }
782
783}
784
785static void gfio_add_total_depths_tree(GtkListStore *model,
786 struct thread_stat *ts, unsigned int len)
787{
788 double io_u_dist[FIO_IO_U_MAP_NR];
789 GtkTreeIter iter;
790 /* Bits 1-6, and 8 */
791 const int add_mask = 0x17e;
792 int i, j;
793
794 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
795
796 gtk_list_store_append(model, &iter);
797
798 gtk_list_store_set(model, &iter, 0, "Total", -1);
799
800 for (i = 1, j = 0; i < len; i++) {
801 char fbuf[32];
802
803 if (!(add_mask & (1UL << (i - 1))))
804 sprintf(fbuf, "0.0%%");
805 else {
806 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
807 j++;
808 }
809
810 gtk_list_store_set(model, &iter, i, fbuf, -1);
811 }
812
813}
Jens Axboe2e331012012-03-05 22:07:54 +0100814
815static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
816{
Jens Axboe2e331012012-03-05 22:07:54 +0100817 GtkWidget *frame, *box, *tree_view;
818 GtkTreeSelection *selection;
819 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100820 GType types[FIO_IO_U_MAP_NR + 1];
821 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100822#define NR_LABELS 10
823 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100824
825 frame = gtk_frame_new("IO depths");
826 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
827
828 box = gtk_hbox_new(FALSE, 3);
829 gtk_container_add(GTK_CONTAINER(frame), box);
830
Jens Axboe19998db2012-03-06 09:17:59 +0100831 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100832 types[i] = G_TYPE_STRING;
833
Jens Axboe19998db2012-03-06 09:17:59 +0100834 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100835
836 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
837 gtk_widget_set_can_focus(tree_view, FALSE);
838
Jens Axboe661f7412012-03-06 13:55:45 +0100839 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
840 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
841
Jens Axboe2e331012012-03-05 22:07:54 +0100842 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
843 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
844
Jens Axboe19998db2012-03-06 09:17:59 +0100845 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100846 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
847
Jens Axboe19998db2012-03-06 09:17:59 +0100848 gfio_add_total_depths_tree(model, ts, NR_LABELS);
849 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
850 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +0100851
852 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
853}
854
Jens Axboef9d40b42012-03-06 09:52:49 +0100855static gboolean results_window_delete(GtkWidget *w, gpointer data)
856{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100857 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +0100858
859 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100860 ge->results_window = NULL;
861 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +0100862 return TRUE;
863}
864
Jens Axboe2f99deb2012-03-09 14:37:29 +0100865static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +0100866{
867 GtkWidget *win, *notebook;
868
Jens Axboe2f99deb2012-03-09 14:37:29 +0100869 if (ge->results_window)
870 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100871
872 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
873 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +0100874 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100875 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
876 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +0100877
878 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +0100879 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
880 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboef9d40b42012-03-06 09:52:49 +0100881 gtk_container_add(GTK_CONTAINER(win), notebook);
882
Jens Axboe2f99deb2012-03-09 14:37:29 +0100883 ge->results_window = win;
884 ge->results_notebook = notebook;
885 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100886}
887
Jens Axboe3650a3c2012-03-05 14:09:03 +0100888static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
889 struct group_run_stats *rs)
890{
Jens Axboeb01329d2012-03-07 20:31:28 +0100891 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
Jens Axboee0681f32012-03-06 12:14:42 +0100892 struct gfio_client *gc = client->client_data;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100893
894 gdk_threads_enter();
895
Jens Axboe2f99deb2012-03-09 14:37:29 +0100896 res_win = get_results_window(gc->ge);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100897
Jens Axboeb01329d2012-03-07 20:31:28 +0100898 scroll = gtk_scrolled_window_new(NULL, NULL);
899 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
900 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
901
Jens Axboe3650a3c2012-03-05 14:09:03 +0100902 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100903
Jens Axboeb01329d2012-03-07 20:31:28 +0100904 box = gtk_hbox_new(FALSE, 0);
905 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100906
Jens Axboeb01329d2012-03-07 20:31:28 +0100907 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
908
909 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +0100910
Jens Axboee0681f32012-03-06 12:14:42 +0100911 gc->results_widget = vbox;
912
Jens Axboe3650a3c2012-03-05 14:09:03 +0100913 entry = new_info_entry_in_frame(box, "Name");
914 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
915 if (strlen(ts->description)) {
916 entry = new_info_entry_in_frame(box, "Description");
917 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
918 }
919 entry = new_info_entry_in_frame(box, "Group ID");
920 entry_set_int_value(entry, ts->groupid);
921 entry = new_info_entry_in_frame(box, "Jobs");
922 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +0100923 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +0100924 entry_set_int_value(entry, ts->error);
925 entry = new_info_entry_in_frame(box, "PID");
926 entry_set_int_value(entry, ts->pid);
927
928 if (ts->io_bytes[DDIR_READ])
929 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
930 if (ts->io_bytes[DDIR_WRITE])
931 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
932
Jens Axboee5bd1342012-03-05 21:38:12 +0100933 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +0100934 gfio_show_cpu_usage(vbox, ts);
935 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +0100936
Jens Axboe2f99deb2012-03-09 14:37:29 +0100937 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100938 gdk_threads_leave();
939}
940
Jens Axboe084d1c62012-03-03 20:28:07 +0100941static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100942{
Jens Axboe9b260bd2012-03-06 11:02:52 +0100943 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100944 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100945 GtkTreeIter iter;
946 struct tm *tm;
947 time_t sec;
948 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100949
Jens Axboe9b260bd2012-03-06 11:02:52 +0100950 sec = p->log_sec;
951 tm = localtime(&sec);
952 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
953 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
954
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100955 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +0100956
Jens Axboe2f99deb2012-03-09 14:37:29 +0100957 gtk_list_store_append(ui->log_model, &iter);
958 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
959 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
960 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
961 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100962
Jens Axboe6b79c802012-03-08 10:51:36 +0100963 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +0100964 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +0100965
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100966 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100967}
968
969static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
970{
Jens Axboee0681f32012-03-06 12:14:42 +0100971 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
972 struct gfio_client *gc = client->client_data;
973 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +0100974 double util;
975 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +0100976
Jens Axboe0050e5f2012-03-06 09:23:27 +0100977 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +0100978
Jens Axboe45dcb2e2012-03-07 16:16:50 +0100979 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +0100980 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +0100981
982 if (!gc->disk_util_frame) {
983 gc->disk_util_frame = gtk_frame_new("Disk utilization");
984 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
985 }
986
987 vbox = gtk_vbox_new(FALSE, 3);
988 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
989
990 frame = gtk_frame_new((char *) p->dus.name);
991 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
992
993 box = gtk_vbox_new(FALSE, 3);
994 gtk_container_add(GTK_CONTAINER(frame), box);
995
996 frame = gtk_frame_new("Read");
997 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
998 vbox = gtk_hbox_new(TRUE, 3);
999 gtk_container_add(GTK_CONTAINER(frame), vbox);
1000 entry = new_info_entry_in_frame(vbox, "IOs");
1001 entry_set_int_value(entry, p->dus.ios[0]);
1002 entry = new_info_entry_in_frame(vbox, "Merges");
1003 entry_set_int_value(entry, p->dus.merges[0]);
1004 entry = new_info_entry_in_frame(vbox, "Sectors");
1005 entry_set_int_value(entry, p->dus.sectors[0]);
1006 entry = new_info_entry_in_frame(vbox, "Ticks");
1007 entry_set_int_value(entry, p->dus.ticks[0]);
1008
1009 frame = gtk_frame_new("Write");
1010 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1011 vbox = gtk_hbox_new(TRUE, 3);
1012 gtk_container_add(GTK_CONTAINER(frame), vbox);
1013 entry = new_info_entry_in_frame(vbox, "IOs");
1014 entry_set_int_value(entry, p->dus.ios[1]);
1015 entry = new_info_entry_in_frame(vbox, "Merges");
1016 entry_set_int_value(entry, p->dus.merges[1]);
1017 entry = new_info_entry_in_frame(vbox, "Sectors");
1018 entry_set_int_value(entry, p->dus.sectors[1]);
1019 entry = new_info_entry_in_frame(vbox, "Ticks");
1020 entry_set_int_value(entry, p->dus.ticks[1]);
1021
1022 frame = gtk_frame_new("Shared");
1023 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1024 vbox = gtk_hbox_new(TRUE, 3);
1025 gtk_container_add(GTK_CONTAINER(frame), vbox);
1026 entry = new_info_entry_in_frame(vbox, "IO ticks");
1027 entry_set_int_value(entry, p->dus.io_ticks);
1028 entry = new_info_entry_in_frame(vbox, "Time in queue");
1029 entry_set_int_value(entry, p->dus.time_in_queue);
1030
Jens Axboe604cfe32012-03-07 19:51:36 +01001031 util = 0.0;
1032 if (p->dus.msec)
1033 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1034 if (util > 100.0)
1035 util = 100.0;
1036
1037 sprintf(tmp, "%3.2f%%", util);
1038 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1039 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1040
Jens Axboee0681f32012-03-06 12:14:42 +01001041 gtk_widget_show_all(gc->results_widget);
1042out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001043 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001044}
1045
Jens Axboe3650a3c2012-03-05 14:09:03 +01001046extern int sum_stat_clients;
1047extern struct thread_stat client_ts;
1048extern struct group_run_stats client_gs;
1049
1050static int sum_stat_nr;
1051
Jens Axboe89e5fad2012-03-05 09:21:12 +01001052static void gfio_thread_status_op(struct fio_client *client,
1053 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001054{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001055 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1056
1057 gfio_display_ts(client, &p->ts, &p->rs);
1058
1059 if (sum_stat_clients == 1)
1060 return;
1061
1062 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1063 sum_group_stats(&client_gs, &p->rs);
1064
1065 client_ts.members++;
1066 client_ts.groupid = p->ts.groupid;
1067
1068 if (++sum_stat_nr == sum_stat_clients) {
1069 strcpy(client_ts.name, "All clients");
1070 gfio_display_ts(client, &client_ts, &client_gs);
1071 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001072}
1073
Jens Axboe89e5fad2012-03-05 09:21:12 +01001074static void gfio_group_stats_op(struct fio_client *client,
1075 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001076{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001077 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001078}
1079
Jens Axboe2f99deb2012-03-09 14:37:29 +01001080static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1081 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001082{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001083 struct gfio_graphs *g = data;
1084
1085 g->drawing_area_xdim = w->allocation.width;
1086 g->drawing_area_ydim = w->allocation.height;
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001087 return TRUE;
1088}
1089
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001090static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1091{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001092 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001093 cairo_t *cr;
1094
Jens Axboe2f99deb2012-03-09 14:37:29 +01001095 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1096 g->drawing_area_ydim);
1097 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1098 g->drawing_area_ydim);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001099 cr = gdk_cairo_create(w->window);
1100
1101 cairo_set_source_rgb(cr, 0, 0, 0);
1102
1103 cairo_save(cr);
1104 cairo_translate(cr, 0, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001105 line_graph_draw(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001106 cairo_stroke(cr);
1107 cairo_restore(cr);
1108
1109 cairo_save(cr);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001110 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1111 line_graph_draw(g->iops_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001112 cairo_stroke(cr);
1113 cairo_restore(cr);
1114 cairo_destroy(cr);
1115
1116 return FALSE;
1117}
1118
Jens Axboe2f99deb2012-03-09 14:37:29 +01001119/*
1120 * Client specific ETA
1121 */
1122static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001123{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001124 struct gfio_client *gc = client->client_data;
1125 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001126 static int eta_good;
1127 char eta_str[128];
1128 char output[256];
1129 char tmp[32];
1130 double perc = 0.0;
1131 int i2p = 0;
1132
Jens Axboe0050e5f2012-03-06 09:23:27 +01001133 gdk_threads_enter();
1134
Jens Axboe3e47bd22012-02-29 13:45:02 +01001135 eta_str[0] = '\0';
1136 output[0] = '\0';
1137
1138 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1139 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1140 eta_to_str(eta_str, je->eta_sec);
1141 }
1142
1143 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001144 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001145 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001146 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001147
1148#if 0
1149 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1150 if (je->m_rate || je->t_rate) {
1151 char *tr, *mr;
1152
1153 mr = num2str(je->m_rate, 4, 0, i2p);
1154 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001155 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001156 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1157 free(tr);
1158 free(mr);
1159 } else if (je->m_iops || je->t_iops)
1160 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001161
Jens Axboe2f99deb2012-03-09 14:37:29 +01001162 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1163 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1164 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1165 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001166#endif
1167
1168 if (je->eta_sec != INT_MAX && je->nr_running) {
1169 char *iops_str[2];
1170 char *rate_str[2];
1171
1172 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1173 strcpy(output, "-.-% done");
1174 else {
1175 eta_good = 1;
1176 perc *= 100.0;
1177 sprintf(output, "%3.1f%% done", perc);
1178 }
1179
1180 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1181 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1182
1183 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1184 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1185
Jens Axboe2f99deb2012-03-09 14:37:29 +01001186 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1187 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1188 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1189 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001190
Jens Axboe2f99deb2012-03-09 14:37:29 +01001191 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1192 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1193 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1194 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1195
1196 free(rate_str[0]);
1197 free(rate_str[1]);
1198 free(iops_str[0]);
1199 free(iops_str[1]);
1200 }
1201
1202 if (eta_str[0]) {
1203 char *dst = output + strlen(output);
1204
1205 sprintf(dst, " - %s", eta_str);
1206 }
1207
Jens Axboe9988ca72012-03-09 15:14:06 +01001208 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001209 gdk_threads_leave();
1210}
1211
1212/*
1213 * Update ETA in main window for all clients
1214 */
1215static void gfio_update_all_eta(struct jobs_eta *je)
1216{
1217 struct gui *ui = &main_ui;
1218 static int eta_good;
1219 char eta_str[128];
1220 char output[256];
1221 double perc = 0.0;
1222 int i2p = 0;
1223
1224 gdk_threads_enter();
1225
1226 eta_str[0] = '\0';
1227 output[0] = '\0';
1228
1229 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1230 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1231 eta_to_str(eta_str, je->eta_sec);
1232 }
1233
1234#if 0
1235 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1236 if (je->m_rate || je->t_rate) {
1237 char *tr, *mr;
1238
1239 mr = num2str(je->m_rate, 4, 0, i2p);
1240 tr = num2str(je->t_rate, 4, 0, i2p);
1241 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1242 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1243 free(tr);
1244 free(mr);
1245 } else if (je->m_iops || je->t_iops)
1246 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1247
1248 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1249 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1250 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1251 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1252#endif
1253
1254 if (je->eta_sec != INT_MAX && je->nr_running) {
1255 char *iops_str[2];
1256 char *rate_str[2];
1257
1258 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1259 strcpy(output, "-.-% done");
1260 else {
1261 eta_good = 1;
1262 perc *= 100.0;
1263 sprintf(output, "%3.1f%% done", perc);
1264 }
1265
1266 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1267 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1268
1269 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1270 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1271
1272 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1273 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1274 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1275 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1276
1277 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1278 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1279 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1280 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001281
Jens Axboe3e47bd22012-02-29 13:45:02 +01001282 free(rate_str[0]);
1283 free(rate_str[1]);
1284 free(iops_str[0]);
1285 free(iops_str[1]);
1286 }
1287
1288 if (eta_str[0]) {
1289 char *dst = output + strlen(output);
1290
1291 sprintf(dst, " - %s", eta_str);
1292 }
1293
Jens Axboe9988ca72012-03-09 15:14:06 +01001294 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001295 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001296}
1297
Stephen M. Camerona1820202012-02-24 08:17:31 +01001298static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1299{
Jens Axboe843ad232012-02-29 11:44:53 +01001300 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001301 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001302 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001303 const char *os, *arch;
1304 char buf[64];
1305
1306 os = fio_get_os_string(probe->os);
1307 if (!os)
1308 os = "unknown";
1309
1310 arch = fio_get_arch_string(probe->arch);
1311 if (!arch)
1312 os = "unknown";
1313
1314 if (!client->name)
1315 client->name = strdup((char *) probe->hostname);
1316
Jens Axboe0050e5f2012-03-06 09:23:27 +01001317 gdk_threads_enter();
1318
Jens Axboe2f99deb2012-03-09 14:37:29 +01001319 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1320 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1321 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001322 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001323 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001324
Jens Axboe2f99deb2012-03-09 14:37:29 +01001325 gfio_set_connected(ge, 1);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001326
1327 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001328}
1329
Jens Axboe9988ca72012-03-09 15:14:06 +01001330static void gfio_update_thread_status(struct gui_entry *ge,
1331 char *status_message, double perc)
1332{
1333 static char message[100];
1334 const char *m = message;
1335
1336 strncpy(message, status_message, sizeof(message) - 1);
1337 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1338 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1339 gtk_widget_queue_draw(main_ui.window);
1340}
1341
1342static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001343{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001344 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001345 static char message[100];
1346 const char *m = message;
1347
1348 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001349 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1350 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1351 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001352}
1353
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001354static void gfio_quit_op(struct fio_client *client)
1355{
Jens Axboee0681f32012-03-06 12:14:42 +01001356 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001357
Jens Axboe0050e5f2012-03-06 09:23:27 +01001358 gdk_threads_enter();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001359 gfio_set_connected(gc->ge, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001360 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001361}
1362
Jens Axboe807f9972012-03-02 10:25:24 +01001363static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1364{
1365 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001366 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001367 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001368 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001369 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001370
Jens Axboedcaeb602012-03-08 19:45:37 +01001371 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001372
Jens Axboe0050e5f2012-03-06 09:23:27 +01001373 gdk_threads_enter();
1374
Jens Axboe2f99deb2012-03-09 14:37:29 +01001375 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1376
1377 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1378 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1379 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001380
Jens Axboedcaeb602012-03-08 19:45:37 +01001381 sprintf(tmp, "%u", o->iodepth);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001382 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001383
Jens Axboedcaeb602012-03-08 19:45:37 +01001384 gc->job_added++;
1385
Jens Axboe0050e5f2012-03-06 09:23:27 +01001386 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001387}
1388
Jens Axboeed727a42012-03-02 12:14:40 +01001389static void gfio_client_timed_out(struct fio_client *client)
1390{
Jens Axboee0681f32012-03-06 12:14:42 +01001391 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001392 GtkWidget *dialog, *label, *content;
1393 char buf[256];
1394
1395 gdk_threads_enter();
1396
Jens Axboe2f99deb2012-03-09 14:37:29 +01001397 gfio_set_connected(gc->ge, 0);
1398 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001399
1400 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1401
1402 dialog = gtk_dialog_new_with_buttons("Timed out!",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001403 GTK_WINDOW(main_ui.window),
Jens Axboeed727a42012-03-02 12:14:40 +01001404 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1405 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1406
Jens Axboef1299092012-03-07 20:00:02 +01001407 /* gtk_dialog_get_content_area() is 2.14 and newer */
1408 content = GTK_DIALOG(dialog)->vbox;
1409
Jens Axboeed727a42012-03-02 12:14:40 +01001410 label = gtk_label_new((const gchar *) buf);
1411 gtk_container_add(GTK_CONTAINER(content), label);
1412 gtk_widget_show_all(dialog);
1413 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1414
1415 gtk_dialog_run(GTK_DIALOG(dialog));
1416 gtk_widget_destroy(dialog);
1417
1418 gdk_threads_leave();
1419}
1420
Jens Axboe6b79c802012-03-08 10:51:36 +01001421static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1422{
1423 struct gfio_client *gc = client->client_data;
1424
1425 gdk_threads_enter();
1426
Jens Axboe2f99deb2012-03-09 14:37:29 +01001427 gfio_set_connected(gc->ge, 0);
Jens Axboe6b79c802012-03-08 10:51:36 +01001428
1429 if (gc->err_entry)
1430 entry_set_int_value(gc->err_entry, client->error);
1431
1432 gdk_threads_leave();
1433}
1434
Stephen M. Camerona1820202012-02-24 08:17:31 +01001435struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001436 .text_op = gfio_text_op,
1437 .disk_util = gfio_disk_util_op,
1438 .thread_status = gfio_thread_status_op,
1439 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001440 .jobs_eta = gfio_update_client_eta,
1441 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001442 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001443 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001444 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001445 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001446 .stop = gfio_client_stop,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001447 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001448};
1449
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001450static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1451 __attribute__((unused)) gpointer data)
1452{
1453 gtk_main_quit();
1454}
1455
Stephen M. Cameron25927252012-02-24 08:17:31 +01001456static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001457{
Jens Axboea9eccde2012-03-09 14:59:42 +01001458 struct gui *ui = arg;
1459
1460 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001461 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001462 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001463 return NULL;
1464}
1465
Jens Axboe2f99deb2012-03-09 14:37:29 +01001466static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001467{
Jens Axboe9988ca72012-03-09 15:14:06 +01001468 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001469 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001470
Jens Axboe2f99deb2012-03-09 14:37:29 +01001471 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001472 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001473 if (ret < 0) {
1474 GError *error;
1475
1476 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1477 report_error(error);
1478 g_error_free(error);
1479 break;
1480 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001481 break;
1482
Jens Axboe2f99deb2012-03-09 14:37:29 +01001483 free(ge->job_files[i]);
1484 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001485 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001486 while (i < ge->nr_job_files) {
1487 free(ge->job_files[i]);
1488 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001489 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001490 }
1491
Jens Axboe441013b2012-03-01 08:01:52 +01001492 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001493}
1494
Jens Axboe63a130b2012-03-06 20:08:59 +01001495static void *server_thread(void *arg)
1496{
1497 is_backend = 1;
1498 gfio_server_running = 1;
1499 fio_start_server(NULL);
1500 gfio_server_running = 0;
1501 return NULL;
1502}
1503
Jens Axboe2f99deb2012-03-09 14:37:29 +01001504static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001505{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001506 struct gui *ui = &main_ui;
1507
Jens Axboe63a130b2012-03-06 20:08:59 +01001508 if (!gfio_server_running) {
1509 gfio_server_running = 1;
1510 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001511 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001512 }
1513}
1514
Stephen M. Cameron25927252012-02-24 08:17:31 +01001515static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1516 gpointer data)
1517{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001518 struct gui_entry *ge = data;
1519 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001520
Jens Axboe2f99deb2012-03-09 14:37:29 +01001521 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1522 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001523}
1524
Jens Axboedf06f222012-03-02 13:32:04 +01001525static void file_open(GtkWidget *w, gpointer data);
1526
1527static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001528{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001529 struct gui_entry *ge = data;
1530 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001531
Jens Axboe2f99deb2012-03-09 14:37:29 +01001532 if (!ge->connected) {
Jens Axboec7249262012-03-09 17:11:04 +01001533 int ret;
1534
Jens Axboe2f99deb2012-03-09 14:37:29 +01001535 if (!ge->nr_job_files)
Jens Axboedf06f222012-03-02 13:32:04 +01001536 file_open(widget, data);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001537 if (!ge->nr_job_files)
1538 return;
1539
1540 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1541 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001542 ret = fio_client_connect(gc->client);
1543 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001544 if (!ge->ui->handler_running)
1545 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001546 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1547 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
Jens Axboec7249262012-03-09 17:11:04 +01001548 } else {
1549 GError *error;
1550
1551 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1552 report_error(error);
1553 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001554 }
Jens Axboedf06f222012-03-02 13:32:04 +01001555 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001556 fio_client_terminate(gc->client);
1557 gfio_set_connected(ge, 0);
1558 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001559 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001560}
1561
Jens Axboeb9d2f302012-03-08 20:36:28 +01001562static void send_clicked(GtkWidget *widget, gpointer data)
1563{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001564 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001565
Jens Axboe2f99deb2012-03-09 14:37:29 +01001566 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001567 GError *error;
1568
1569 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
1570 report_error(error);
1571 g_error_free(error);
1572
Jens Axboe2f99deb2012-03-09 14:37:29 +01001573 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001574 }
1575
Jens Axboe2f99deb2012-03-09 14:37:29 +01001576 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1577 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001578}
1579
Jens Axboe2f99deb2012-03-09 14:37:29 +01001580static GtkWidget *add_button(GtkWidget *buttonbox,
1581 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001582{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001583 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1584
1585 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1586 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1587 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1588 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1589
1590 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001591}
1592
Jens Axboe2f99deb2012-03-09 14:37:29 +01001593static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1594 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001595{
1596 int i;
1597
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001598 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001599 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001600}
1601
Jens Axboe0420ba62012-02-29 11:16:52 +01001602static void on_info_bar_response(GtkWidget *widget, gint response,
1603 gpointer data)
1604{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001605 struct gui *ui = &main_ui;
1606
Jens Axboe0420ba62012-02-29 11:16:52 +01001607 if (response == GTK_RESPONSE_OK) {
1608 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001609 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001610 }
1611}
1612
Jens Axboedf06f222012-03-02 13:32:04 +01001613void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001614{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001615 struct gui *ui = &main_ui;
1616
1617 if (ui->error_info_bar == NULL) {
1618 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001619 GTK_RESPONSE_OK,
1620 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001621 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1622 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001623 GTK_MESSAGE_ERROR);
1624
Jens Axboe2f99deb2012-03-09 14:37:29 +01001625 ui->error_label = gtk_label_new(error->message);
1626 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1627 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001628
Jens Axboe2f99deb2012-03-09 14:37:29 +01001629 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1630 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001631 } else {
1632 char buffer[256];
1633 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001634 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001635 }
1636}
1637
Jens Axboe62bc9372012-03-07 11:45:07 +01001638struct connection_widgets
1639{
1640 GtkWidget *hentry;
1641 GtkWidget *combo;
1642 GtkWidget *button;
1643};
1644
1645static void hostname_cb(GtkEntry *entry, gpointer data)
1646{
1647 struct connection_widgets *cw = data;
1648 int uses_net = 0, is_localhost = 0;
1649 const gchar *text;
1650 gchar *ctext;
1651
1652 /*
1653 * Check whether to display the 'auto start backend' box
1654 * or not. Show it if we are a localhost and using network,
1655 * or using a socket.
1656 */
1657 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1658 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1659 uses_net = 1;
1660 g_free(ctext);
1661
1662 if (uses_net) {
1663 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1664 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1665 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1666 !strcmp(text, "ip6-loopback"))
1667 is_localhost = 1;
1668 }
1669
1670 if (!uses_net || is_localhost) {
1671 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1672 gtk_widget_set_sensitive(cw->button, 1);
1673 } else {
1674 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1675 gtk_widget_set_sensitive(cw->button, 0);
1676 }
1677}
1678
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001679static int get_connection_details(char **host, int *port, int *type,
1680 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001681{
Jens Axboe62bc9372012-03-07 11:45:07 +01001682 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1683 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001684 char *typeentry;
1685
1686 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001687 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001688 GTK_DIALOG_DESTROY_WITH_PARENT,
1689 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1690 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1691
1692 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001693 /* gtk_dialog_get_content_area() is 2.14 and newer */
1694 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001695 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1696
1697 box = gtk_vbox_new(FALSE, 6);
1698 gtk_container_add(GTK_CONTAINER(frame), box);
1699
1700 hbox = gtk_hbox_new(TRUE, 10);
1701 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01001702 cw.hentry = gtk_entry_new();
1703 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1704 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001705
1706 frame = gtk_frame_new("Port");
1707 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1708 box = gtk_vbox_new(FALSE, 10);
1709 gtk_container_add(GTK_CONTAINER(frame), box);
1710
1711 hbox = gtk_hbox_new(TRUE, 4);
1712 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1713 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1714
1715 frame = gtk_frame_new("Type");
1716 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1717 box = gtk_vbox_new(FALSE, 10);
1718 gtk_container_add(GTK_CONTAINER(frame), box);
1719
1720 hbox = gtk_hbox_new(TRUE, 4);
1721 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1722
Jens Axboe62bc9372012-03-07 11:45:07 +01001723 cw.combo = gtk_combo_box_new_text();
1724 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1725 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1726 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1727 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001728
Jens Axboe62bc9372012-03-07 11:45:07 +01001729 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001730
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001731 frame = gtk_frame_new("Options");
1732 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1733 box = gtk_vbox_new(FALSE, 10);
1734 gtk_container_add(GTK_CONTAINER(frame), box);
1735
1736 hbox = gtk_hbox_new(TRUE, 4);
1737 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1738
Jens Axboe62bc9372012-03-07 11:45:07 +01001739 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1740 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1741 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.");
1742 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1743
1744 /*
1745 * Connect edit signal, so we can show/not-show the auto start button
1746 */
1747 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1748 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001749
Jens Axboea7a42ce2012-03-02 13:12:04 +01001750 gtk_widget_show_all(dialog);
1751
1752 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1753 gtk_widget_destroy(dialog);
1754 return 1;
1755 }
1756
Jens Axboe62bc9372012-03-07 11:45:07 +01001757 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001758 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1759
Jens Axboe62bc9372012-03-07 11:45:07 +01001760 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001761 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1762 *type = Fio_client_ipv4;
1763 else if (!strncmp(typeentry, "IPv6", 4))
1764 *type = Fio_client_ipv6;
1765 else
1766 *type = Fio_client_socket;
1767 g_free(typeentry);
1768
Jens Axboe62bc9372012-03-07 11:45:07 +01001769 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001770
Jens Axboea7a42ce2012-03-02 13:12:04 +01001771 gtk_widget_destroy(dialog);
1772 return 0;
1773}
1774
Jens Axboe2f99deb2012-03-09 14:37:29 +01001775static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01001776{
1777 struct gfio_client *gc;
1778
1779 gc = malloc(sizeof(*gc));
1780 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001781 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01001782 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001783
Jens Axboe2f99deb2012-03-09 14:37:29 +01001784 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01001785
1786 client->client_data = gc;
1787}
1788
Jens Axboe2f99deb2012-03-09 14:37:29 +01001789static GtkWidget *new_client_page(struct gui_entry *ge);
1790
1791static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1792{
1793 struct gui_entry *ge;
1794
1795 ge = malloc(sizeof(*ge));
1796 memset(ge, 0, sizeof(*ge));
1797 INIT_FLIST_HEAD(&ge->list);
1798 flist_add_tail(&ge->list, &ui->list);
1799 ge->ui = ui;
1800 return ge;
1801}
1802
1803/*
1804 * FIXME: need more handling here
1805 */
1806static void ge_destroy(GtkWidget *w, gpointer data)
1807{
1808 struct gui_entry *ge = data;
Jens Axboe343cb4a2012-03-09 17:16:51 +01001809 struct gfio_client *gc = ge->client;
1810
1811 if (gc->client)
1812 fio_put_client(gc->client);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001813
1814 flist_del(&ge->list);
1815 free(ge);
1816}
1817
1818static struct gui_entry *get_new_ge_with_tab(const char *name)
1819{
1820 struct gui_entry *ge;
1821
1822 ge = alloc_new_gui_entry(&main_ui);
1823
1824 ge->vbox = new_client_page(ge);
1825 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1826
1827 ge->page_label = gtk_label_new(name);
1828 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1829
1830 gtk_widget_show_all(main_ui.window);
1831 return ge;
1832}
1833
1834static void file_new(GtkWidget *w, gpointer data)
1835{
1836 get_new_ge_with_tab("Untitled");
1837}
1838
1839/*
1840 * Return the 'ge' corresponding to the tab. If the active tab is the
1841 * main tab, open a new tab.
1842 */
1843static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1844{
1845 struct flist_head *entry;
1846 struct gui_entry *ge;
1847
1848 if (!cur_page)
1849 return get_new_ge_with_tab("Untitled");
1850
1851 flist_for_each(entry, &main_ui.list) {
1852 ge = flist_entry(entry, struct gui_entry, list);
1853 if (ge->page_num == cur_page)
1854 return ge;
1855 }
1856
1857 return NULL;
1858}
1859
Jens Axboe0420ba62012-02-29 11:16:52 +01001860static void file_open(GtkWidget *w, gpointer data)
1861{
Jens Axboe63a130b2012-03-06 20:08:59 +01001862 struct gui *ui = data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001863 GtkWidget *dialog;
Jens Axboe0420ba62012-02-29 11:16:52 +01001864 GSList *filenames, *fn_glist;
1865 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001866 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001867 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001868 struct gui_entry *ge;
1869 gint cur_page;
1870
1871 /*
1872 * Creates new tab if current tab is the main window, or the
1873 * current tab already has a client.
1874 */
1875 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1876 ge = get_ge_from_page(cur_page);
1877 if (ge->client)
1878 ge = get_new_ge_with_tab("Untitled");
1879
1880 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01001881
1882 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01001883 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01001884 GTK_FILE_CHOOSER_ACTION_OPEN,
1885 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1886 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1887 NULL);
1888 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1889
1890 filter = gtk_file_filter_new();
1891 gtk_file_filter_add_pattern(filter, "*.fio");
1892 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01001893 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe0420ba62012-02-29 11:16:52 +01001894 gtk_file_filter_add_mime_type(filter, "text/fio");
1895 gtk_file_filter_set_name(filter, "Fio job file");
1896 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1897
1898 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1899 gtk_widget_destroy(dialog);
1900 return;
1901 }
1902
1903 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001904
1905 gtk_widget_destroy(dialog);
1906
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001907 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01001908 goto err;
1909
Jens Axboe0420ba62012-02-29 11:16:52 +01001910 filenames = fn_glist;
1911 while (filenames != NULL) {
Jens Axboee0681f32012-03-06 12:14:42 +01001912 struct fio_client *client;
1913
Jens Axboe2f99deb2012-03-09 14:37:29 +01001914 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1915 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1916 ge->nr_job_files++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001917
Jens Axboee0681f32012-03-06 12:14:42 +01001918 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1919 if (!client) {
Jens Axboedf06f222012-03-02 13:32:04 +01001920 GError *error;
1921
1922 error = g_error_new(g_quark_from_string("fio"), 1,
1923 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01001924 report_error(error);
1925 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01001926 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001927 gfio_client_added(ge, client);
Jens Axboe0420ba62012-02-29 11:16:52 +01001928
1929 g_free(filenames->data);
1930 filenames = g_slist_next(filenames);
1931 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01001932 free(host);
Jens Axboe63a130b2012-03-06 20:08:59 +01001933
1934 if (server_start)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001935 gfio_start_server();
Jens Axboea7a42ce2012-03-02 13:12:04 +01001936err:
Jens Axboe0420ba62012-02-29 11:16:52 +01001937 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01001938}
1939
1940static void file_save(GtkWidget *w, gpointer data)
1941{
Jens Axboe63a130b2012-03-06 20:08:59 +01001942 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01001943 GtkWidget *dialog;
1944
1945 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01001946 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01001947 GTK_FILE_CHOOSER_ACTION_SAVE,
1948 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1949 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1950 NULL);
1951
1952 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1953 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1954
1955 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1956 char *filename;
1957
1958 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1959 // save_job_file(filename);
1960 g_free(filename);
1961 }
1962 gtk_widget_destroy(dialog);
1963}
1964
Jens Axboe9b260bd2012-03-06 11:02:52 +01001965static void view_log_destroy(GtkWidget *w, gpointer data)
1966{
1967 struct gui *ui = (struct gui *) data;
1968
1969 gtk_widget_ref(ui->log_tree);
1970 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1971 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01001972 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001973}
1974
1975static void view_log(GtkWidget *w, gpointer data)
1976{
Jens Axboe4cbe7212012-03-06 13:36:17 +01001977 GtkWidget *win, *scroll, *vbox, *box;
1978 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001979
Jens Axboe4cbe7212012-03-06 13:36:17 +01001980 if (ui->log_view)
1981 return;
1982
1983 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001984 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01001985 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001986
Jens Axboe4cbe7212012-03-06 13:36:17 +01001987 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001988
Jens Axboe4cbe7212012-03-06 13:36:17 +01001989 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1990
1991 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1992
1993 box = gtk_hbox_new(TRUE, 0);
1994 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1995 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1996 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1997
1998 vbox = gtk_vbox_new(TRUE, 5);
1999 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2000
2001 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002002 gtk_widget_show_all(win);
2003}
2004
Jens Axboe46974a72012-03-02 19:34:13 +01002005static void preferences(GtkWidget *w, gpointer data)
2006{
Jens Axboef3e84402012-03-07 13:14:32 +01002007 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe46974a72012-03-02 19:34:13 +01002008 int i;
2009
2010 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002011 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002012 GTK_DIALOG_DESTROY_WITH_PARENT,
2013 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2014 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2015 NULL);
2016
Jens Axboe0b8d11e2012-03-02 19:44:15 +01002017 frame = gtk_frame_new("Debug logging");
Jens Axboe46974a72012-03-02 19:34:13 +01002018 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002019
2020 vbox = gtk_vbox_new(FALSE, 6);
2021 gtk_container_add(GTK_CONTAINER(frame), vbox);
2022
Jens Axboe46974a72012-03-02 19:34:13 +01002023 box = gtk_hbox_new(FALSE, 6);
Jens Axboef3e84402012-03-07 13:14:32 +01002024 gtk_container_add(GTK_CONTAINER(vbox), box);
Jens Axboe46974a72012-03-02 19:34:13 +01002025
2026 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2027
2028 for (i = 0; i < FD_DEBUG_MAX; i++) {
Jens Axboef3e84402012-03-07 13:14:32 +01002029 if (i == 7) {
2030 box = gtk_hbox_new(FALSE, 6);
2031 gtk_container_add(GTK_CONTAINER(vbox), box);
2032 }
2033
2034
Jens Axboe46974a72012-03-02 19:34:13 +01002035 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
Jens Axboe0b8d11e2012-03-02 19:44:15 +01002036 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
Jens Axboe46974a72012-03-02 19:34:13 +01002037 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2038 }
2039
Jens Axboef3e84402012-03-07 13:14:32 +01002040 frame = gtk_frame_new("Graph font");
2041 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2042 vbox = gtk_vbox_new(FALSE, 6);
2043 gtk_container_add(GTK_CONTAINER(frame), vbox);
2044
2045 font = gtk_font_button_new();
2046 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2047
Jens Axboe46974a72012-03-02 19:34:13 +01002048 gtk_widget_show_all(dialog);
2049
2050 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2051 gtk_widget_destroy(dialog);
2052 return;
2053 }
2054
2055 for (i = 0; i < FD_DEBUG_MAX; i++) {
2056 int set;
2057
2058 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2059 if (set)
2060 fio_debug |= (1UL << i);
2061 }
2062
Jens Axboef3e84402012-03-07 13:14:32 +01002063 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe46974a72012-03-02 19:34:13 +01002064 gtk_widget_destroy(dialog);
2065}
2066
Jens Axboe0420ba62012-02-29 11:16:52 +01002067static void about_dialog(GtkWidget *w, gpointer data)
2068{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002069 const char *authors[] = {
2070 "Jens Axboe <axboe@kernel.dk>",
2071 "Stephen Carmeron <stephenmcameron@gmail.com>",
2072 NULL
2073 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002074 const char *license[] = {
2075 "Fio is free software; you can redistribute it and/or modify "
2076 "it under the terms of the GNU General Public License as published by "
2077 "the Free Software Foundation; either version 2 of the License, or "
2078 "(at your option) any later version.\n",
2079 "Fio is distributed in the hope that it will be useful, "
2080 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2081 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2082 "GNU General Public License for more details.\n",
2083 "You should have received a copy of the GNU General Public License "
2084 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2085 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2086 };
2087 char *license_trans;
2088
2089 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2090 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002091
Jens Axboe0420ba62012-02-29 11:16:52 +01002092 gtk_show_about_dialog(NULL,
2093 "program-name", "gfio",
2094 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002095 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002096 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2097 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002098 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002099 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002100 "logo-icon-name", "fio",
2101 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002102 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002103 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002104
Jens Axboe2f99deb2012-03-09 14:37:29 +01002105 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002106}
2107
2108static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002109 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002110 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002111 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002112 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe46974a72012-03-02 19:34:13 +01002113 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2114 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2115 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002116 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe46974a72012-03-02 19:34:13 +01002117 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2118 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002119};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002120static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002121
2122static const gchar *ui_string = " \
2123 <ui> \
2124 <menubar name=\"MainMenu\"> \
2125 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002126 <menuitem name=\"New\" action=\"NewFile\" /> \
2127 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002128 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2129 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002130 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002131 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2132 <separator name=\"Separator3\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002133 <menuitem name=\"Quit\" action=\"Quit\" /> \
2134 </menu> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002135 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2136 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2137 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002138 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2139 <menuitem name=\"About\" action=\"About\" /> \
2140 </menu> \
2141 </menubar> \
2142 </ui> \
2143";
2144
Jens Axboe4cbe7212012-03-06 13:36:17 +01002145static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2146 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002147{
2148 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2149 GError *error = 0;
2150
2151 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002152 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002153
2154 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2155 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2156
2157 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2158 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2159}
2160
2161void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2162 GtkWidget *vbox, GtkUIManager *ui_manager)
2163{
2164 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2165}
2166
Jens Axboe2f99deb2012-03-09 14:37:29 +01002167static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002168{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002169 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002170 GdkColor white;
Jens Axboe0420ba62012-02-29 11:16:52 +01002171
Jens Axboe2f99deb2012-03-09 14:37:29 +01002172 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002173
Jens Axboe2f99deb2012-03-09 14:37:29 +01002174 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2175 ge->topvbox = gtk_vbox_new(FALSE, 3);
2176 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2177 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002178
Jens Axboe3e47bd22012-02-29 13:45:02 +01002179 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002180 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002181 probe_frame = gtk_vbox_new(FALSE, 3);
2182 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2183
2184 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002185 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2186 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2187 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2188 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2189 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002190
Jens Axboe3e47bd22012-02-29 13:45:02 +01002191 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002192 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2193
2194 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2195 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2196 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2197 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2198 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2199 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2200
2201 probe_box = gtk_hbox_new(FALSE, 3);
2202 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2203 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2204 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2205 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2206 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2207
2208 /*
2209 * Only add this if we have a commit rate
2210 */
2211#if 0
2212 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002213 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002214
Jens Axboe2f99deb2012-03-09 14:37:29 +01002215 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2216 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2217
2218 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2219 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2220#endif
2221
2222 /*
2223 * Set up a drawing area and IOPS and bandwidth graphs
2224 */
2225 gdk_color_parse("white", &white);
2226 ge->graphs.drawing_area = gtk_drawing_area_new();
2227 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2228 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2229 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2230 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2231 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2232 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2233 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2234 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2235 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2236 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2237 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2238 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2239 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2240 ge->graphs.drawing_area);
2241 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2242 TRUE, TRUE, 0);
2243
2244 setup_graphs(&ge->graphs);
2245
2246 /*
2247 * Set up alignments for widgets at the bottom of ui,
2248 * align bottom left, expand horizontally but not vertically
2249 */
2250 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2251 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2252 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2253 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2254 FALSE, FALSE, 0);
2255
2256 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2257
2258 /*
2259 * Set up thread status progress bar
2260 */
2261 ge->thread_status_pb = gtk_progress_bar_new();
2262 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2263 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2264 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2265
2266
2267 return main_vbox;
2268}
2269
2270static GtkWidget *new_main_page(struct gui *ui)
2271{
2272 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2273 GdkColor white;
2274
2275 main_vbox = gtk_vbox_new(FALSE, 3);
2276
2277 /*
2278 * Set up alignments for widgets at the top of ui,
2279 * align top left, expand horizontally but not vertically
2280 */
2281 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2282 ui->topvbox = gtk_vbox_new(FALSE, 0);
2283 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2284 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2285
2286 probe = gtk_frame_new("Run statistics");
2287 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2288 probe_frame = gtk_vbox_new(FALSE, 3);
2289 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002290
2291 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002292 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboeca850992012-03-05 20:04:43 +01002293 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2294 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2295 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2296 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002297
2298 /*
2299 * Only add this if we have a commit rate
2300 */
2301#if 0
2302 probe_box = gtk_hbox_new(FALSE, 3);
2303 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2304
Jens Axboe3e47bd22012-02-29 13:45:02 +01002305 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2306 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2307
Jens Axboe3e47bd22012-02-29 13:45:02 +01002308 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2309 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002310#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002311
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002312 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002313 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002314 */
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002315 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002316 ui->graphs.drawing_area = gtk_drawing_area_new();
2317 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2318 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2319 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2320 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2321 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2322 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2323 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2324 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2325 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002326 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2327 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2328 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002329 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002330 ui->graphs.drawing_area);
2331 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002332 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002333
Jens Axboe2f99deb2012-03-09 14:37:29 +01002334 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002335
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002336 /*
2337 * Set up alignments for widgets at the bottom of ui,
2338 * align bottom left, expand horizontally but not vertically
2339 */
2340 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2341 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2342 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002343 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002344 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002345
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002346 /*
2347 * Set up thread status progress bar
2348 */
2349 ui->thread_status_pb = gtk_progress_bar_new();
2350 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002351 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002352 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2353
Jens Axboe2f99deb2012-03-09 14:37:29 +01002354 return main_vbox;
2355}
2356
2357static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2358 guint page, gpointer data)
2359
2360{
2361 return TRUE;
2362}
2363
2364static void init_ui(int *argc, char **argv[], struct gui *ui)
2365{
2366 GtkSettings *settings;
2367 GtkUIManager *uimanager;
2368 GtkWidget *menu, *vbox;
2369
2370 /* Magical g*thread incantation, you just need this thread stuff.
2371 * Without it, the update that happens in gfio_update_thread_status
2372 * doesn't really happen in a timely fashion, you need expose events
2373 */
2374 if (!g_thread_supported())
2375 g_thread_init(NULL);
2376 gdk_threads_init();
2377
2378 gtk_init(argc, argv);
2379 settings = gtk_settings_get_default();
2380 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2381 g_type_init();
2382
2383 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2384 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2385 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2386
2387 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2388 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2389
2390 ui->vbox = gtk_vbox_new(FALSE, 0);
2391 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2392
2393 uimanager = gtk_ui_manager_new();
2394 menu = get_menubar_menu(ui->window, uimanager, ui);
2395 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2396
2397 ui->notebook = gtk_notebook_new();
2398 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01002399 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01002400 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002401 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2402
2403 vbox = new_main_page(ui);
2404
2405 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2406
Jens Axboe9b260bd2012-03-06 11:02:52 +01002407 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002408
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002409 gtk_widget_show_all(ui->window);
2410}
2411
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002412int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002413{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002414 if (initialize_fio(envp))
2415 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01002416 if (fio_init_options())
2417 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01002418
Jens Axboe2f99deb2012-03-09 14:37:29 +01002419 memset(&main_ui, 0, sizeof(main_ui));
2420 INIT_FLIST_HEAD(&main_ui.list);
2421
2422 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01002423
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002424 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002425 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002426 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002427 return 0;
2428}