blob: 52389beffae70df40f074b2b81491850ff33f672 [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();
879 gtk_container_add(GTK_CONTAINER(win), notebook);
880
Jens Axboe2f99deb2012-03-09 14:37:29 +0100881 ge->results_window = win;
882 ge->results_notebook = notebook;
883 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100884}
885
Jens Axboe3650a3c2012-03-05 14:09:03 +0100886static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
887 struct group_run_stats *rs)
888{
Jens Axboeb01329d2012-03-07 20:31:28 +0100889 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
Jens Axboee0681f32012-03-06 12:14:42 +0100890 struct gfio_client *gc = client->client_data;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100891
892 gdk_threads_enter();
893
Jens Axboe2f99deb2012-03-09 14:37:29 +0100894 res_win = get_results_window(gc->ge);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100895
Jens Axboeb01329d2012-03-07 20:31:28 +0100896 scroll = gtk_scrolled_window_new(NULL, NULL);
897 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
898 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
899
Jens Axboe3650a3c2012-03-05 14:09:03 +0100900 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100901
Jens Axboeb01329d2012-03-07 20:31:28 +0100902 box = gtk_hbox_new(FALSE, 0);
903 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100904
Jens Axboeb01329d2012-03-07 20:31:28 +0100905 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
906
907 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +0100908
Jens Axboee0681f32012-03-06 12:14:42 +0100909 gc->results_widget = vbox;
910
Jens Axboe3650a3c2012-03-05 14:09:03 +0100911 entry = new_info_entry_in_frame(box, "Name");
912 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
913 if (strlen(ts->description)) {
914 entry = new_info_entry_in_frame(box, "Description");
915 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
916 }
917 entry = new_info_entry_in_frame(box, "Group ID");
918 entry_set_int_value(entry, ts->groupid);
919 entry = new_info_entry_in_frame(box, "Jobs");
920 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +0100921 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +0100922 entry_set_int_value(entry, ts->error);
923 entry = new_info_entry_in_frame(box, "PID");
924 entry_set_int_value(entry, ts->pid);
925
926 if (ts->io_bytes[DDIR_READ])
927 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
928 if (ts->io_bytes[DDIR_WRITE])
929 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
930
Jens Axboee5bd1342012-03-05 21:38:12 +0100931 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +0100932 gfio_show_cpu_usage(vbox, ts);
933 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +0100934
Jens Axboe2f99deb2012-03-09 14:37:29 +0100935 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100936 gdk_threads_leave();
937}
938
Jens Axboe084d1c62012-03-03 20:28:07 +0100939static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100940{
Jens Axboe9b260bd2012-03-06 11:02:52 +0100941 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100942 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100943 GtkTreeIter iter;
944 struct tm *tm;
945 time_t sec;
946 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100947
Jens Axboe9b260bd2012-03-06 11:02:52 +0100948 sec = p->log_sec;
949 tm = localtime(&sec);
950 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
951 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
952
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100953 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +0100954
Jens Axboe2f99deb2012-03-09 14:37:29 +0100955 gtk_list_store_append(ui->log_model, &iter);
956 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
957 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
958 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
959 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100960
Jens Axboe6b79c802012-03-08 10:51:36 +0100961 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +0100962 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +0100963
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100964 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100965}
966
967static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
968{
Jens Axboee0681f32012-03-06 12:14:42 +0100969 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
970 struct gfio_client *gc = client->client_data;
971 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +0100972 double util;
973 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +0100974
Jens Axboe0050e5f2012-03-06 09:23:27 +0100975 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +0100976
Jens Axboe45dcb2e2012-03-07 16:16:50 +0100977 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +0100978 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +0100979
980 if (!gc->disk_util_frame) {
981 gc->disk_util_frame = gtk_frame_new("Disk utilization");
982 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
983 }
984
985 vbox = gtk_vbox_new(FALSE, 3);
986 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
987
988 frame = gtk_frame_new((char *) p->dus.name);
989 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
990
991 box = gtk_vbox_new(FALSE, 3);
992 gtk_container_add(GTK_CONTAINER(frame), box);
993
994 frame = gtk_frame_new("Read");
995 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
996 vbox = gtk_hbox_new(TRUE, 3);
997 gtk_container_add(GTK_CONTAINER(frame), vbox);
998 entry = new_info_entry_in_frame(vbox, "IOs");
999 entry_set_int_value(entry, p->dus.ios[0]);
1000 entry = new_info_entry_in_frame(vbox, "Merges");
1001 entry_set_int_value(entry, p->dus.merges[0]);
1002 entry = new_info_entry_in_frame(vbox, "Sectors");
1003 entry_set_int_value(entry, p->dus.sectors[0]);
1004 entry = new_info_entry_in_frame(vbox, "Ticks");
1005 entry_set_int_value(entry, p->dus.ticks[0]);
1006
1007 frame = gtk_frame_new("Write");
1008 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1009 vbox = gtk_hbox_new(TRUE, 3);
1010 gtk_container_add(GTK_CONTAINER(frame), vbox);
1011 entry = new_info_entry_in_frame(vbox, "IOs");
1012 entry_set_int_value(entry, p->dus.ios[1]);
1013 entry = new_info_entry_in_frame(vbox, "Merges");
1014 entry_set_int_value(entry, p->dus.merges[1]);
1015 entry = new_info_entry_in_frame(vbox, "Sectors");
1016 entry_set_int_value(entry, p->dus.sectors[1]);
1017 entry = new_info_entry_in_frame(vbox, "Ticks");
1018 entry_set_int_value(entry, p->dus.ticks[1]);
1019
1020 frame = gtk_frame_new("Shared");
1021 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1022 vbox = gtk_hbox_new(TRUE, 3);
1023 gtk_container_add(GTK_CONTAINER(frame), vbox);
1024 entry = new_info_entry_in_frame(vbox, "IO ticks");
1025 entry_set_int_value(entry, p->dus.io_ticks);
1026 entry = new_info_entry_in_frame(vbox, "Time in queue");
1027 entry_set_int_value(entry, p->dus.time_in_queue);
1028
Jens Axboe604cfe32012-03-07 19:51:36 +01001029 util = 0.0;
1030 if (p->dus.msec)
1031 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1032 if (util > 100.0)
1033 util = 100.0;
1034
1035 sprintf(tmp, "%3.2f%%", util);
1036 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1037 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1038
Jens Axboee0681f32012-03-06 12:14:42 +01001039 gtk_widget_show_all(gc->results_widget);
1040out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001041 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001042}
1043
Jens Axboe3650a3c2012-03-05 14:09:03 +01001044extern int sum_stat_clients;
1045extern struct thread_stat client_ts;
1046extern struct group_run_stats client_gs;
1047
1048static int sum_stat_nr;
1049
Jens Axboe89e5fad2012-03-05 09:21:12 +01001050static void gfio_thread_status_op(struct fio_client *client,
1051 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001052{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001053 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1054
1055 gfio_display_ts(client, &p->ts, &p->rs);
1056
1057 if (sum_stat_clients == 1)
1058 return;
1059
1060 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1061 sum_group_stats(&client_gs, &p->rs);
1062
1063 client_ts.members++;
1064 client_ts.groupid = p->ts.groupid;
1065
1066 if (++sum_stat_nr == sum_stat_clients) {
1067 strcpy(client_ts.name, "All clients");
1068 gfio_display_ts(client, &client_ts, &client_gs);
1069 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001070}
1071
Jens Axboe89e5fad2012-03-05 09:21:12 +01001072static void gfio_group_stats_op(struct fio_client *client,
1073 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001074{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001075 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001076}
1077
Jens Axboe2f99deb2012-03-09 14:37:29 +01001078static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1079 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001080{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001081 struct gfio_graphs *g = data;
1082
1083 g->drawing_area_xdim = w->allocation.width;
1084 g->drawing_area_ydim = w->allocation.height;
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001085 return TRUE;
1086}
1087
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001088static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1089{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001090 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001091 cairo_t *cr;
1092
Jens Axboe2f99deb2012-03-09 14:37:29 +01001093 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1094 g->drawing_area_ydim);
1095 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1096 g->drawing_area_ydim);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001097 cr = gdk_cairo_create(w->window);
1098
1099 cairo_set_source_rgb(cr, 0, 0, 0);
1100
1101 cairo_save(cr);
1102 cairo_translate(cr, 0, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001103 line_graph_draw(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001104 cairo_stroke(cr);
1105 cairo_restore(cr);
1106
1107 cairo_save(cr);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001108 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1109 line_graph_draw(g->iops_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001110 cairo_stroke(cr);
1111 cairo_restore(cr);
1112 cairo_destroy(cr);
1113
1114 return FALSE;
1115}
1116
Jens Axboe2f99deb2012-03-09 14:37:29 +01001117/*
1118 * Client specific ETA
1119 */
1120static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001121{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001122 struct gfio_client *gc = client->client_data;
1123 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001124 static int eta_good;
1125 char eta_str[128];
1126 char output[256];
1127 char tmp[32];
1128 double perc = 0.0;
1129 int i2p = 0;
1130
Jens Axboe0050e5f2012-03-06 09:23:27 +01001131 gdk_threads_enter();
1132
Jens Axboe3e47bd22012-02-29 13:45:02 +01001133 eta_str[0] = '\0';
1134 output[0] = '\0';
1135
1136 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1137 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1138 eta_to_str(eta_str, je->eta_sec);
1139 }
1140
1141 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001142 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001143 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001144 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001145
1146#if 0
1147 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1148 if (je->m_rate || je->t_rate) {
1149 char *tr, *mr;
1150
1151 mr = num2str(je->m_rate, 4, 0, i2p);
1152 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001153 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001154 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1155 free(tr);
1156 free(mr);
1157 } else if (je->m_iops || je->t_iops)
1158 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001159
Jens Axboe2f99deb2012-03-09 14:37:29 +01001160 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1161 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1162 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1163 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001164#endif
1165
1166 if (je->eta_sec != INT_MAX && je->nr_running) {
1167 char *iops_str[2];
1168 char *rate_str[2];
1169
1170 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1171 strcpy(output, "-.-% done");
1172 else {
1173 eta_good = 1;
1174 perc *= 100.0;
1175 sprintf(output, "%3.1f%% done", perc);
1176 }
1177
1178 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1179 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1180
1181 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1182 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1183
Jens Axboe2f99deb2012-03-09 14:37:29 +01001184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1185 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1187 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001188
Jens Axboe2f99deb2012-03-09 14:37:29 +01001189 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1190 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1191 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1192 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1193
1194 free(rate_str[0]);
1195 free(rate_str[1]);
1196 free(iops_str[0]);
1197 free(iops_str[1]);
1198 }
1199
1200 if (eta_str[0]) {
1201 char *dst = output + strlen(output);
1202
1203 sprintf(dst, " - %s", eta_str);
1204 }
1205
Jens Axboe9988ca72012-03-09 15:14:06 +01001206 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001207 gdk_threads_leave();
1208}
1209
1210/*
1211 * Update ETA in main window for all clients
1212 */
1213static void gfio_update_all_eta(struct jobs_eta *je)
1214{
1215 struct gui *ui = &main_ui;
1216 static int eta_good;
1217 char eta_str[128];
1218 char output[256];
1219 double perc = 0.0;
1220 int i2p = 0;
1221
1222 gdk_threads_enter();
1223
1224 eta_str[0] = '\0';
1225 output[0] = '\0';
1226
1227 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1228 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1229 eta_to_str(eta_str, je->eta_sec);
1230 }
1231
1232#if 0
1233 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1234 if (je->m_rate || je->t_rate) {
1235 char *tr, *mr;
1236
1237 mr = num2str(je->m_rate, 4, 0, i2p);
1238 tr = num2str(je->t_rate, 4, 0, i2p);
1239 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1240 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1241 free(tr);
1242 free(mr);
1243 } else if (je->m_iops || je->t_iops)
1244 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1245
1246 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1247 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1248 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1249 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1250#endif
1251
1252 if (je->eta_sec != INT_MAX && je->nr_running) {
1253 char *iops_str[2];
1254 char *rate_str[2];
1255
1256 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1257 strcpy(output, "-.-% done");
1258 else {
1259 eta_good = 1;
1260 perc *= 100.0;
1261 sprintf(output, "%3.1f%% done", perc);
1262 }
1263
1264 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1265 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1266
1267 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1268 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1269
1270 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1271 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1272 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1273 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1274
1275 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1276 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1277 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1278 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001279
Jens Axboe3e47bd22012-02-29 13:45:02 +01001280 free(rate_str[0]);
1281 free(rate_str[1]);
1282 free(iops_str[0]);
1283 free(iops_str[1]);
1284 }
1285
1286 if (eta_str[0]) {
1287 char *dst = output + strlen(output);
1288
1289 sprintf(dst, " - %s", eta_str);
1290 }
1291
Jens Axboe9988ca72012-03-09 15:14:06 +01001292 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001293 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001294}
1295
Stephen M. Camerona1820202012-02-24 08:17:31 +01001296static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1297{
Jens Axboe843ad232012-02-29 11:44:53 +01001298 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001299 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001300 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001301 const char *os, *arch;
1302 char buf[64];
1303
1304 os = fio_get_os_string(probe->os);
1305 if (!os)
1306 os = "unknown";
1307
1308 arch = fio_get_arch_string(probe->arch);
1309 if (!arch)
1310 os = "unknown";
1311
1312 if (!client->name)
1313 client->name = strdup((char *) probe->hostname);
1314
Jens Axboe0050e5f2012-03-06 09:23:27 +01001315 gdk_threads_enter();
1316
Jens Axboe2f99deb2012-03-09 14:37:29 +01001317 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1318 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1319 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001320 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001321 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001322
Jens Axboe2f99deb2012-03-09 14:37:29 +01001323 gfio_set_connected(ge, 1);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001324
1325 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001326}
1327
Jens Axboe9988ca72012-03-09 15:14:06 +01001328static void gfio_update_thread_status(struct gui_entry *ge,
1329 char *status_message, double perc)
1330{
1331 static char message[100];
1332 const char *m = message;
1333
1334 strncpy(message, status_message, sizeof(message) - 1);
1335 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1336 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1337 gtk_widget_queue_draw(main_ui.window);
1338}
1339
1340static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001341{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001342 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001343 static char message[100];
1344 const char *m = message;
1345
1346 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001347 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1348 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1349 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001350}
1351
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001352static void gfio_quit_op(struct fio_client *client)
1353{
Jens Axboee0681f32012-03-06 12:14:42 +01001354 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001355
Jens Axboe0050e5f2012-03-06 09:23:27 +01001356 gdk_threads_enter();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001357 gfio_set_connected(gc->ge, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001358 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001359}
1360
Jens Axboe807f9972012-03-02 10:25:24 +01001361static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1362{
1363 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001364 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001365 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001366 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001367 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001368
Jens Axboedcaeb602012-03-08 19:45:37 +01001369 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001370
Jens Axboe0050e5f2012-03-06 09:23:27 +01001371 gdk_threads_enter();
1372
Jens Axboe2f99deb2012-03-09 14:37:29 +01001373 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1374
1375 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1376 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1377 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001378
Jens Axboedcaeb602012-03-08 19:45:37 +01001379 sprintf(tmp, "%u", o->iodepth);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001380 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001381
Jens Axboedcaeb602012-03-08 19:45:37 +01001382 gc->job_added++;
1383
Jens Axboe0050e5f2012-03-06 09:23:27 +01001384 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001385}
1386
Jens Axboeed727a42012-03-02 12:14:40 +01001387static void gfio_client_timed_out(struct fio_client *client)
1388{
Jens Axboee0681f32012-03-06 12:14:42 +01001389 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001390 GtkWidget *dialog, *label, *content;
1391 char buf[256];
1392
1393 gdk_threads_enter();
1394
Jens Axboe2f99deb2012-03-09 14:37:29 +01001395 gfio_set_connected(gc->ge, 0);
1396 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001397
1398 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1399
1400 dialog = gtk_dialog_new_with_buttons("Timed out!",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001401 GTK_WINDOW(main_ui.window),
Jens Axboeed727a42012-03-02 12:14:40 +01001402 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1403 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1404
Jens Axboef1299092012-03-07 20:00:02 +01001405 /* gtk_dialog_get_content_area() is 2.14 and newer */
1406 content = GTK_DIALOG(dialog)->vbox;
1407
Jens Axboeed727a42012-03-02 12:14:40 +01001408 label = gtk_label_new((const gchar *) buf);
1409 gtk_container_add(GTK_CONTAINER(content), label);
1410 gtk_widget_show_all(dialog);
1411 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1412
1413 gtk_dialog_run(GTK_DIALOG(dialog));
1414 gtk_widget_destroy(dialog);
1415
1416 gdk_threads_leave();
1417}
1418
Jens Axboe6b79c802012-03-08 10:51:36 +01001419static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1420{
1421 struct gfio_client *gc = client->client_data;
1422
1423 gdk_threads_enter();
1424
Jens Axboe2f99deb2012-03-09 14:37:29 +01001425 gfio_set_connected(gc->ge, 0);
Jens Axboe6b79c802012-03-08 10:51:36 +01001426
1427 if (gc->err_entry)
1428 entry_set_int_value(gc->err_entry, client->error);
1429
1430 gdk_threads_leave();
1431}
1432
Stephen M. Camerona1820202012-02-24 08:17:31 +01001433struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001434 .text_op = gfio_text_op,
1435 .disk_util = gfio_disk_util_op,
1436 .thread_status = gfio_thread_status_op,
1437 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001438 .jobs_eta = gfio_update_client_eta,
1439 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001440 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001441 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001442 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001443 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001444 .stop = gfio_client_stop,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001445 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001446};
1447
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001448static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1449 __attribute__((unused)) gpointer data)
1450{
1451 gtk_main_quit();
1452}
1453
Stephen M. Cameron25927252012-02-24 08:17:31 +01001454static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001455{
Jens Axboea9eccde2012-03-09 14:59:42 +01001456 struct gui *ui = arg;
1457
1458 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001459 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001460 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001461 return NULL;
1462}
1463
Jens Axboe2f99deb2012-03-09 14:37:29 +01001464static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001465{
Jens Axboe9988ca72012-03-09 15:14:06 +01001466 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001467 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001468
Jens Axboe2f99deb2012-03-09 14:37:29 +01001469 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001470 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001471 if (ret < 0) {
1472 GError *error;
1473
1474 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1475 report_error(error);
1476 g_error_free(error);
1477 break;
1478 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001479 break;
1480
Jens Axboe2f99deb2012-03-09 14:37:29 +01001481 free(ge->job_files[i]);
1482 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001483 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001484 while (i < ge->nr_job_files) {
1485 free(ge->job_files[i]);
1486 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001487 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001488 }
1489
Jens Axboe441013b2012-03-01 08:01:52 +01001490 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001491}
1492
Jens Axboe63a130b2012-03-06 20:08:59 +01001493static void *server_thread(void *arg)
1494{
1495 is_backend = 1;
1496 gfio_server_running = 1;
1497 fio_start_server(NULL);
1498 gfio_server_running = 0;
1499 return NULL;
1500}
1501
Jens Axboe2f99deb2012-03-09 14:37:29 +01001502static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001503{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001504 struct gui *ui = &main_ui;
1505
Jens Axboe63a130b2012-03-06 20:08:59 +01001506 if (!gfio_server_running) {
1507 gfio_server_running = 1;
1508 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001509 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001510 }
1511}
1512
Stephen M. Cameron25927252012-02-24 08:17:31 +01001513static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1514 gpointer data)
1515{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001516 struct gui_entry *ge = data;
1517 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001518
Jens Axboe2f99deb2012-03-09 14:37:29 +01001519 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1520 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001521}
1522
Jens Axboedf06f222012-03-02 13:32:04 +01001523static void file_open(GtkWidget *w, gpointer data);
1524
1525static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001526{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001527 struct gui_entry *ge = data;
1528 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001529
Jens Axboe2f99deb2012-03-09 14:37:29 +01001530 if (!ge->connected) {
Jens Axboec7249262012-03-09 17:11:04 +01001531 int ret;
1532
Jens Axboe2f99deb2012-03-09 14:37:29 +01001533 if (!ge->nr_job_files)
Jens Axboedf06f222012-03-02 13:32:04 +01001534 file_open(widget, data);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001535 if (!ge->nr_job_files)
1536 return;
1537
1538 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1539 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001540 ret = fio_client_connect(gc->client);
1541 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001542 if (!ge->ui->handler_running)
1543 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001544 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1545 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
Jens Axboec7249262012-03-09 17:11:04 +01001546 } else {
1547 GError *error;
1548
1549 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1550 report_error(error);
1551 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001552 }
Jens Axboedf06f222012-03-02 13:32:04 +01001553 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001554 fio_client_terminate(gc->client);
1555 gfio_set_connected(ge, 0);
1556 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001557 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001558}
1559
Jens Axboeb9d2f302012-03-08 20:36:28 +01001560static void send_clicked(GtkWidget *widget, gpointer data)
1561{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001562 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001563
Jens Axboe2f99deb2012-03-09 14:37:29 +01001564 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001565 GError *error;
1566
1567 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);
1568 report_error(error);
1569 g_error_free(error);
1570
Jens Axboe2f99deb2012-03-09 14:37:29 +01001571 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001572 }
1573
Jens Axboe2f99deb2012-03-09 14:37:29 +01001574 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1575 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001576}
1577
Jens Axboe2f99deb2012-03-09 14:37:29 +01001578static GtkWidget *add_button(GtkWidget *buttonbox,
1579 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001580{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001581 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1582
1583 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1584 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1585 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1586 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1587
1588 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001589}
1590
Jens Axboe2f99deb2012-03-09 14:37:29 +01001591static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1592 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001593{
1594 int i;
1595
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001596 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001597 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001598}
1599
Jens Axboe0420ba62012-02-29 11:16:52 +01001600static void on_info_bar_response(GtkWidget *widget, gint response,
1601 gpointer data)
1602{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001603 struct gui *ui = &main_ui;
1604
Jens Axboe0420ba62012-02-29 11:16:52 +01001605 if (response == GTK_RESPONSE_OK) {
1606 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001607 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001608 }
1609}
1610
Jens Axboedf06f222012-03-02 13:32:04 +01001611void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001612{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001613 struct gui *ui = &main_ui;
1614
1615 if (ui->error_info_bar == NULL) {
1616 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001617 GTK_RESPONSE_OK,
1618 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001619 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1620 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001621 GTK_MESSAGE_ERROR);
1622
Jens Axboe2f99deb2012-03-09 14:37:29 +01001623 ui->error_label = gtk_label_new(error->message);
1624 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1625 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001626
Jens Axboe2f99deb2012-03-09 14:37:29 +01001627 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1628 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001629 } else {
1630 char buffer[256];
1631 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001632 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001633 }
1634}
1635
Jens Axboe62bc9372012-03-07 11:45:07 +01001636struct connection_widgets
1637{
1638 GtkWidget *hentry;
1639 GtkWidget *combo;
1640 GtkWidget *button;
1641};
1642
1643static void hostname_cb(GtkEntry *entry, gpointer data)
1644{
1645 struct connection_widgets *cw = data;
1646 int uses_net = 0, is_localhost = 0;
1647 const gchar *text;
1648 gchar *ctext;
1649
1650 /*
1651 * Check whether to display the 'auto start backend' box
1652 * or not. Show it if we are a localhost and using network,
1653 * or using a socket.
1654 */
1655 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1656 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1657 uses_net = 1;
1658 g_free(ctext);
1659
1660 if (uses_net) {
1661 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1662 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1663 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1664 !strcmp(text, "ip6-loopback"))
1665 is_localhost = 1;
1666 }
1667
1668 if (!uses_net || is_localhost) {
1669 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1670 gtk_widget_set_sensitive(cw->button, 1);
1671 } else {
1672 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1673 gtk_widget_set_sensitive(cw->button, 0);
1674 }
1675}
1676
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001677static int get_connection_details(char **host, int *port, int *type,
1678 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001679{
Jens Axboe62bc9372012-03-07 11:45:07 +01001680 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1681 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001682 char *typeentry;
1683
1684 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001685 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001686 GTK_DIALOG_DESTROY_WITH_PARENT,
1687 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1688 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1689
1690 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001691 /* gtk_dialog_get_content_area() is 2.14 and newer */
1692 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001693 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1694
1695 box = gtk_vbox_new(FALSE, 6);
1696 gtk_container_add(GTK_CONTAINER(frame), box);
1697
1698 hbox = gtk_hbox_new(TRUE, 10);
1699 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01001700 cw.hentry = gtk_entry_new();
1701 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1702 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001703
1704 frame = gtk_frame_new("Port");
1705 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1706 box = gtk_vbox_new(FALSE, 10);
1707 gtk_container_add(GTK_CONTAINER(frame), box);
1708
1709 hbox = gtk_hbox_new(TRUE, 4);
1710 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1711 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1712
1713 frame = gtk_frame_new("Type");
1714 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1715 box = gtk_vbox_new(FALSE, 10);
1716 gtk_container_add(GTK_CONTAINER(frame), box);
1717
1718 hbox = gtk_hbox_new(TRUE, 4);
1719 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1720
Jens Axboe62bc9372012-03-07 11:45:07 +01001721 cw.combo = gtk_combo_box_new_text();
1722 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1723 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1724 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1725 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001726
Jens Axboe62bc9372012-03-07 11:45:07 +01001727 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001728
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001729 frame = gtk_frame_new("Options");
1730 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1731 box = gtk_vbox_new(FALSE, 10);
1732 gtk_container_add(GTK_CONTAINER(frame), box);
1733
1734 hbox = gtk_hbox_new(TRUE, 4);
1735 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1736
Jens Axboe62bc9372012-03-07 11:45:07 +01001737 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1738 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1739 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.");
1740 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1741
1742 /*
1743 * Connect edit signal, so we can show/not-show the auto start button
1744 */
1745 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1746 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001747
Jens Axboea7a42ce2012-03-02 13:12:04 +01001748 gtk_widget_show_all(dialog);
1749
1750 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1751 gtk_widget_destroy(dialog);
1752 return 1;
1753 }
1754
Jens Axboe62bc9372012-03-07 11:45:07 +01001755 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001756 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1757
Jens Axboe62bc9372012-03-07 11:45:07 +01001758 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001759 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1760 *type = Fio_client_ipv4;
1761 else if (!strncmp(typeentry, "IPv6", 4))
1762 *type = Fio_client_ipv6;
1763 else
1764 *type = Fio_client_socket;
1765 g_free(typeentry);
1766
Jens Axboe62bc9372012-03-07 11:45:07 +01001767 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001768
Jens Axboea7a42ce2012-03-02 13:12:04 +01001769 gtk_widget_destroy(dialog);
1770 return 0;
1771}
1772
Jens Axboe2f99deb2012-03-09 14:37:29 +01001773static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01001774{
1775 struct gfio_client *gc;
1776
1777 gc = malloc(sizeof(*gc));
1778 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001779 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01001780 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001781
Jens Axboe2f99deb2012-03-09 14:37:29 +01001782 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01001783
1784 client->client_data = gc;
1785}
1786
Jens Axboe2f99deb2012-03-09 14:37:29 +01001787static GtkWidget *new_client_page(struct gui_entry *ge);
1788
1789static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1790{
1791 struct gui_entry *ge;
1792
1793 ge = malloc(sizeof(*ge));
1794 memset(ge, 0, sizeof(*ge));
1795 INIT_FLIST_HEAD(&ge->list);
1796 flist_add_tail(&ge->list, &ui->list);
1797 ge->ui = ui;
1798 return ge;
1799}
1800
1801/*
1802 * FIXME: need more handling here
1803 */
1804static void ge_destroy(GtkWidget *w, gpointer data)
1805{
1806 struct gui_entry *ge = data;
Jens Axboe343cb4a2012-03-09 17:16:51 +01001807 struct gfio_client *gc = ge->client;
1808
1809 if (gc->client)
1810 fio_put_client(gc->client);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001811
1812 flist_del(&ge->list);
1813 free(ge);
1814}
1815
1816static struct gui_entry *get_new_ge_with_tab(const char *name)
1817{
1818 struct gui_entry *ge;
1819
1820 ge = alloc_new_gui_entry(&main_ui);
1821
1822 ge->vbox = new_client_page(ge);
1823 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1824
1825 ge->page_label = gtk_label_new(name);
1826 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1827
1828 gtk_widget_show_all(main_ui.window);
1829 return ge;
1830}
1831
1832static void file_new(GtkWidget *w, gpointer data)
1833{
1834 get_new_ge_with_tab("Untitled");
1835}
1836
1837/*
1838 * Return the 'ge' corresponding to the tab. If the active tab is the
1839 * main tab, open a new tab.
1840 */
1841static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1842{
1843 struct flist_head *entry;
1844 struct gui_entry *ge;
1845
1846 if (!cur_page)
1847 return get_new_ge_with_tab("Untitled");
1848
1849 flist_for_each(entry, &main_ui.list) {
1850 ge = flist_entry(entry, struct gui_entry, list);
1851 if (ge->page_num == cur_page)
1852 return ge;
1853 }
1854
1855 return NULL;
1856}
1857
Jens Axboe0420ba62012-02-29 11:16:52 +01001858static void file_open(GtkWidget *w, gpointer data)
1859{
Jens Axboe63a130b2012-03-06 20:08:59 +01001860 struct gui *ui = data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001861 GtkWidget *dialog;
Jens Axboe0420ba62012-02-29 11:16:52 +01001862 GSList *filenames, *fn_glist;
1863 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001864 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001865 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001866 struct gui_entry *ge;
1867 gint cur_page;
1868
1869 /*
1870 * Creates new tab if current tab is the main window, or the
1871 * current tab already has a client.
1872 */
1873 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1874 ge = get_ge_from_page(cur_page);
1875 if (ge->client)
1876 ge = get_new_ge_with_tab("Untitled");
1877
1878 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01001879
1880 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01001881 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01001882 GTK_FILE_CHOOSER_ACTION_OPEN,
1883 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1884 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1885 NULL);
1886 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1887
1888 filter = gtk_file_filter_new();
1889 gtk_file_filter_add_pattern(filter, "*.fio");
1890 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01001891 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe0420ba62012-02-29 11:16:52 +01001892 gtk_file_filter_add_mime_type(filter, "text/fio");
1893 gtk_file_filter_set_name(filter, "Fio job file");
1894 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1895
1896 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1897 gtk_widget_destroy(dialog);
1898 return;
1899 }
1900
1901 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001902
1903 gtk_widget_destroy(dialog);
1904
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001905 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01001906 goto err;
1907
Jens Axboe0420ba62012-02-29 11:16:52 +01001908 filenames = fn_glist;
1909 while (filenames != NULL) {
Jens Axboee0681f32012-03-06 12:14:42 +01001910 struct fio_client *client;
1911
Jens Axboe2f99deb2012-03-09 14:37:29 +01001912 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1913 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1914 ge->nr_job_files++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001915
Jens Axboee0681f32012-03-06 12:14:42 +01001916 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1917 if (!client) {
Jens Axboedf06f222012-03-02 13:32:04 +01001918 GError *error;
1919
1920 error = g_error_new(g_quark_from_string("fio"), 1,
1921 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01001922 report_error(error);
1923 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01001924 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001925 gfio_client_added(ge, client);
Jens Axboe0420ba62012-02-29 11:16:52 +01001926
1927 g_free(filenames->data);
1928 filenames = g_slist_next(filenames);
1929 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01001930 free(host);
Jens Axboe63a130b2012-03-06 20:08:59 +01001931
1932 if (server_start)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001933 gfio_start_server();
Jens Axboea7a42ce2012-03-02 13:12:04 +01001934err:
Jens Axboe0420ba62012-02-29 11:16:52 +01001935 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01001936}
1937
1938static void file_save(GtkWidget *w, gpointer data)
1939{
Jens Axboe63a130b2012-03-06 20:08:59 +01001940 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01001941 GtkWidget *dialog;
1942
1943 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01001944 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01001945 GTK_FILE_CHOOSER_ACTION_SAVE,
1946 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1947 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1948 NULL);
1949
1950 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1951 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1952
1953 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1954 char *filename;
1955
1956 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1957 // save_job_file(filename);
1958 g_free(filename);
1959 }
1960 gtk_widget_destroy(dialog);
1961}
1962
Jens Axboe9b260bd2012-03-06 11:02:52 +01001963static void view_log_destroy(GtkWidget *w, gpointer data)
1964{
1965 struct gui *ui = (struct gui *) data;
1966
1967 gtk_widget_ref(ui->log_tree);
1968 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1969 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01001970 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001971}
1972
1973static void view_log(GtkWidget *w, gpointer data)
1974{
Jens Axboe4cbe7212012-03-06 13:36:17 +01001975 GtkWidget *win, *scroll, *vbox, *box;
1976 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001977
Jens Axboe4cbe7212012-03-06 13:36:17 +01001978 if (ui->log_view)
1979 return;
1980
1981 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001982 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01001983 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001984
Jens Axboe4cbe7212012-03-06 13:36:17 +01001985 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001986
Jens Axboe4cbe7212012-03-06 13:36:17 +01001987 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1988
1989 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1990
1991 box = gtk_hbox_new(TRUE, 0);
1992 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1993 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1994 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1995
1996 vbox = gtk_vbox_new(TRUE, 5);
1997 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1998
1999 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002000 gtk_widget_show_all(win);
2001}
2002
Jens Axboe46974a72012-03-02 19:34:13 +01002003static void preferences(GtkWidget *w, gpointer data)
2004{
Jens Axboef3e84402012-03-07 13:14:32 +01002005 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe46974a72012-03-02 19:34:13 +01002006 int i;
2007
2008 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002009 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002010 GTK_DIALOG_DESTROY_WITH_PARENT,
2011 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2012 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2013 NULL);
2014
Jens Axboe0b8d11e2012-03-02 19:44:15 +01002015 frame = gtk_frame_new("Debug logging");
Jens Axboe46974a72012-03-02 19:34:13 +01002016 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002017
2018 vbox = gtk_vbox_new(FALSE, 6);
2019 gtk_container_add(GTK_CONTAINER(frame), vbox);
2020
Jens Axboe46974a72012-03-02 19:34:13 +01002021 box = gtk_hbox_new(FALSE, 6);
Jens Axboef3e84402012-03-07 13:14:32 +01002022 gtk_container_add(GTK_CONTAINER(vbox), box);
Jens Axboe46974a72012-03-02 19:34:13 +01002023
2024 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2025
2026 for (i = 0; i < FD_DEBUG_MAX; i++) {
Jens Axboef3e84402012-03-07 13:14:32 +01002027 if (i == 7) {
2028 box = gtk_hbox_new(FALSE, 6);
2029 gtk_container_add(GTK_CONTAINER(vbox), box);
2030 }
2031
2032
Jens Axboe46974a72012-03-02 19:34:13 +01002033 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
Jens Axboe0b8d11e2012-03-02 19:44:15 +01002034 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
Jens Axboe46974a72012-03-02 19:34:13 +01002035 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2036 }
2037
Jens Axboef3e84402012-03-07 13:14:32 +01002038 frame = gtk_frame_new("Graph font");
2039 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2040 vbox = gtk_vbox_new(FALSE, 6);
2041 gtk_container_add(GTK_CONTAINER(frame), vbox);
2042
2043 font = gtk_font_button_new();
2044 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2045
Jens Axboe46974a72012-03-02 19:34:13 +01002046 gtk_widget_show_all(dialog);
2047
2048 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2049 gtk_widget_destroy(dialog);
2050 return;
2051 }
2052
2053 for (i = 0; i < FD_DEBUG_MAX; i++) {
2054 int set;
2055
2056 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2057 if (set)
2058 fio_debug |= (1UL << i);
2059 }
2060
Jens Axboef3e84402012-03-07 13:14:32 +01002061 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe46974a72012-03-02 19:34:13 +01002062 gtk_widget_destroy(dialog);
2063}
2064
Jens Axboe0420ba62012-02-29 11:16:52 +01002065static void about_dialog(GtkWidget *w, gpointer data)
2066{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002067 const char *authors[] = {
2068 "Jens Axboe <axboe@kernel.dk>",
2069 "Stephen Carmeron <stephenmcameron@gmail.com>",
2070 NULL
2071 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002072 const char *license[] = {
2073 "Fio is free software; you can redistribute it and/or modify "
2074 "it under the terms of the GNU General Public License as published by "
2075 "the Free Software Foundation; either version 2 of the License, or "
2076 "(at your option) any later version.\n",
2077 "Fio is distributed in the hope that it will be useful, "
2078 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2079 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2080 "GNU General Public License for more details.\n",
2081 "You should have received a copy of the GNU General Public License "
2082 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2083 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2084 };
2085 char *license_trans;
2086
2087 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2088 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002089
Jens Axboe0420ba62012-02-29 11:16:52 +01002090 gtk_show_about_dialog(NULL,
2091 "program-name", "gfio",
2092 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002093 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002094 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2095 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002096 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002097 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002098 "logo-icon-name", "fio",
2099 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002100 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002101 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002102
Jens Axboe2f99deb2012-03-09 14:37:29 +01002103 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002104}
2105
2106static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002107 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002108 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002109 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002110 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe46974a72012-03-02 19:34:13 +01002111 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2112 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2113 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002114 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe46974a72012-03-02 19:34:13 +01002115 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2116 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002117};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002118static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002119
2120static const gchar *ui_string = " \
2121 <ui> \
2122 <menubar name=\"MainMenu\"> \
2123 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002124 <menuitem name=\"New\" action=\"NewFile\" /> \
2125 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002126 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2127 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002128 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002129 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2130 <separator name=\"Separator3\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002131 <menuitem name=\"Quit\" action=\"Quit\" /> \
2132 </menu> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002133 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2134 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2135 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002136 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2137 <menuitem name=\"About\" action=\"About\" /> \
2138 </menu> \
2139 </menubar> \
2140 </ui> \
2141";
2142
Jens Axboe4cbe7212012-03-06 13:36:17 +01002143static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2144 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002145{
2146 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2147 GError *error = 0;
2148
2149 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002150 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002151
2152 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2153 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2154
2155 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2156 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2157}
2158
2159void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2160 GtkWidget *vbox, GtkUIManager *ui_manager)
2161{
2162 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2163}
2164
Jens Axboe2f99deb2012-03-09 14:37:29 +01002165static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002166{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002167 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002168 GdkColor white;
Jens Axboe0420ba62012-02-29 11:16:52 +01002169
Jens Axboe2f99deb2012-03-09 14:37:29 +01002170 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002171
Jens Axboe2f99deb2012-03-09 14:37:29 +01002172 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2173 ge->topvbox = gtk_vbox_new(FALSE, 3);
2174 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2175 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002176
Jens Axboe3e47bd22012-02-29 13:45:02 +01002177 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002178 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002179 probe_frame = gtk_vbox_new(FALSE, 3);
2180 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2181
2182 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002183 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2184 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2185 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2186 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2187 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002188
Jens Axboe3e47bd22012-02-29 13:45:02 +01002189 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002190 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2191
2192 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2193 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2194 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2195 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2196 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2197 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2198
2199 probe_box = gtk_hbox_new(FALSE, 3);
2200 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2201 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2202 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2203 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2204 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2205
2206 /*
2207 * Only add this if we have a commit rate
2208 */
2209#if 0
2210 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002211 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002212
Jens Axboe2f99deb2012-03-09 14:37:29 +01002213 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2214 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2215
2216 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2217 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2218#endif
2219
2220 /*
2221 * Set up a drawing area and IOPS and bandwidth graphs
2222 */
2223 gdk_color_parse("white", &white);
2224 ge->graphs.drawing_area = gtk_drawing_area_new();
2225 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2226 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2227 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2228 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2229 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2230 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2231 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2232 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2233 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2234 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2235 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2236 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2237 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2238 ge->graphs.drawing_area);
2239 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2240 TRUE, TRUE, 0);
2241
2242 setup_graphs(&ge->graphs);
2243
2244 /*
2245 * Set up alignments for widgets at the bottom of ui,
2246 * align bottom left, expand horizontally but not vertically
2247 */
2248 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2249 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2250 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2251 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2252 FALSE, FALSE, 0);
2253
2254 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2255
2256 /*
2257 * Set up thread status progress bar
2258 */
2259 ge->thread_status_pb = gtk_progress_bar_new();
2260 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2261 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2262 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2263
2264
2265 return main_vbox;
2266}
2267
2268static GtkWidget *new_main_page(struct gui *ui)
2269{
2270 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2271 GdkColor white;
2272
2273 main_vbox = gtk_vbox_new(FALSE, 3);
2274
2275 /*
2276 * Set up alignments for widgets at the top of ui,
2277 * align top left, expand horizontally but not vertically
2278 */
2279 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2280 ui->topvbox = gtk_vbox_new(FALSE, 0);
2281 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2282 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2283
2284 probe = gtk_frame_new("Run statistics");
2285 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2286 probe_frame = gtk_vbox_new(FALSE, 3);
2287 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002288
2289 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002290 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboeca850992012-03-05 20:04:43 +01002291 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2292 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2293 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2294 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002295
2296 /*
2297 * Only add this if we have a commit rate
2298 */
2299#if 0
2300 probe_box = gtk_hbox_new(FALSE, 3);
2301 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2302
Jens Axboe3e47bd22012-02-29 13:45:02 +01002303 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2304 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2305
Jens Axboe3e47bd22012-02-29 13:45:02 +01002306 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2307 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002308#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002309
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002310 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002311 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002312 */
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002313 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002314 ui->graphs.drawing_area = gtk_drawing_area_new();
2315 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2316 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2317 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2318 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2319 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2320 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2321 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2322 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2323 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002324 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2325 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2326 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002327 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002328 ui->graphs.drawing_area);
2329 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002330 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002331
Jens Axboe2f99deb2012-03-09 14:37:29 +01002332 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002333
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002334 /*
2335 * Set up alignments for widgets at the bottom of ui,
2336 * align bottom left, expand horizontally but not vertically
2337 */
2338 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2339 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2340 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002341 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002342 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002343
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002344 /*
2345 * Set up thread status progress bar
2346 */
2347 ui->thread_status_pb = gtk_progress_bar_new();
2348 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002349 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002350 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2351
Jens Axboe2f99deb2012-03-09 14:37:29 +01002352 return main_vbox;
2353}
2354
2355static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2356 guint page, gpointer data)
2357
2358{
2359 return TRUE;
2360}
2361
2362static void init_ui(int *argc, char **argv[], struct gui *ui)
2363{
2364 GtkSettings *settings;
2365 GtkUIManager *uimanager;
2366 GtkWidget *menu, *vbox;
2367
2368 /* Magical g*thread incantation, you just need this thread stuff.
2369 * Without it, the update that happens in gfio_update_thread_status
2370 * doesn't really happen in a timely fashion, you need expose events
2371 */
2372 if (!g_thread_supported())
2373 g_thread_init(NULL);
2374 gdk_threads_init();
2375
2376 gtk_init(argc, argv);
2377 settings = gtk_settings_get_default();
2378 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2379 g_type_init();
2380
2381 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2382 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2383 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2384
2385 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2386 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2387
2388 ui->vbox = gtk_vbox_new(FALSE, 0);
2389 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2390
2391 uimanager = gtk_ui_manager_new();
2392 menu = get_menubar_menu(ui->window, uimanager, ui);
2393 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2394
2395 ui->notebook = gtk_notebook_new();
2396 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2397 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2398
2399 vbox = new_main_page(ui);
2400
2401 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2402
Jens Axboe9b260bd2012-03-06 11:02:52 +01002403 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002404
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002405 gtk_widget_show_all(ui->window);
2406}
2407
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002408int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002409{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002410 if (initialize_fio(envp))
2411 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01002412 if (fio_init_options())
2413 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01002414
Jens Axboe2f99deb2012-03-09 14:37:29 +01002415 memset(&main_ui, 0, sizeof(main_ui));
2416 INIT_FLIST_HEAD(&main_ui.list);
2417
2418 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01002419
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002420 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002421 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002422 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002423 return 0;
2424}