blob: 1f85a491e199628ff0c00294bdec5808380f71b6 [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 Axboe3e47bd22012-02-29 13:45:02 +010038static void gfio_update_thread_status(char *status_message, double perc);
Jens Axboe6b79c802012-03-08 10:51:36 +010039static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010040
Stephen M. Cameronf3074002012-02-24 08:17:30 +010041#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
42
43typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
44
Jens Axboe3e47bd22012-02-29 13:45:02 +010045static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010046static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010047static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010048
49static struct button_spec {
50 const char *buttontext;
51 clickfunction f;
52 const char *tooltiptext;
Jens Axboe3e47bd22012-02-29 13:45:02 +010053 const int start_insensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010054} buttonspeclist[] = {
Jens Axboe3e47bd22012-02-29 13:45:02 +010055#define CONNECT_BUTTON 0
Jens Axboeb9d2f302012-03-08 20:36:28 +010056#define SEND_BUTTON 1
57#define START_JOB_BUTTON 2
Jens Axboe3e47bd22012-02-29 13:45:02 +010058 { "Connect", connect_clicked, "Connect to host", 0 },
Jens Axboeb9d2f302012-03-08 20:36:28 +010059 { "Send", send_clicked, "Send job description to host", 1 },
60 { "Start Job", start_job_clicked,
Jens Axboe2f99deb2012-03-09 14:37:29 +010061 "Start the current job on the server", 1 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010062};
63
Jens Axboe843ad232012-02-29 11:44:53 +010064struct probe_widget {
65 GtkWidget *hostname;
66 GtkWidget *os;
67 GtkWidget *arch;
68 GtkWidget *fio_ver;
69};
70
Jens Axboe3e47bd22012-02-29 13:45:02 +010071struct eta_widget {
Jens Axboe807f9972012-03-02 10:25:24 +010072 GtkWidget *name;
73 GtkWidget *iotype;
74 GtkWidget *ioengine;
75 GtkWidget *iodepth;
Jens Axboe3e47bd22012-02-29 13:45:02 +010076 GtkWidget *jobs;
77 GtkWidget *files;
78 GtkWidget *read_bw;
79 GtkWidget *read_iops;
80 GtkWidget *cr_bw;
81 GtkWidget *cr_iops;
82 GtkWidget *write_bw;
83 GtkWidget *write_iops;
84 GtkWidget *cw_bw;
85 GtkWidget *cw_iops;
86};
87
Jens Axboe2f99deb2012-03-09 14:37:29 +010088struct gfio_graphs {
89#define DRAWING_AREA_XDIM 1000
90#define DRAWING_AREA_YDIM 400
91 GtkWidget *drawing_area;
92 int drawing_area_xdim;
93 int drawing_area_ydim;
94
95 struct graph *iops_graph;
96 struct graph *bandwidth_graph;
97};
98
99/*
100 * Main window widgets and data
101 */
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100102struct gui {
103 GtkWidget *window;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100104 GtkWidget *vbox;
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +0100105 GtkWidget *topvbox;
106 GtkWidget *topalign;
107 GtkWidget *bottomalign;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100108 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100109 GtkWidget *buttonbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100110 GtkWidget *scrolled_window;
111 GtkWidget *notebook;
112 GtkWidget *error_info_bar;
113 GtkWidget *error_label;
114 GtkListStore *log_model;
115 GtkWidget *log_tree;
116 GtkWidget *log_view;
117 struct gfio_graphs graphs;
118 struct probe_widget probe;
119 struct eta_widget eta;
120 pthread_t server_t;
121
Jens Axboea9eccde2012-03-09 14:59:42 +0100122 pthread_t t;
123 int handler_running;
124
Jens Axboe2f99deb2012-03-09 14:37:29 +0100125 struct flist_head list;
126} main_ui;
127
128/*
129 * Notebook entry
130 */
131struct gui_entry {
132 struct flist_head list;
133 struct gui *ui;
134
135 GtkWidget *vbox;
136 GtkWidget *topvbox;
137 GtkWidget *topalign;
138 GtkWidget *bottomalign;
139 GtkWidget *thread_status_pb;
140 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100141 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100142 GtkWidget *scrolled_window;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100143 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100144 GtkWidget *error_info_bar;
145 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100146 GtkWidget *results_notebook;
147 GtkWidget *results_window;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100148 GtkListStore *log_model;
149 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100150 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100151 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100152 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100153 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100154 GtkWidget *page_label;
155 gint page_num;
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100156 int connected;
Jens Axboe0420ba62012-02-29 11:16:52 +0100157
Jens Axboeb9d2f302012-03-08 20:36:28 +0100158 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100159 int nr_job_files;
160 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100161};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100162
Jens Axboee0681f32012-03-06 12:14:42 +0100163struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100164 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100165 struct fio_client *client;
Jens Axboee0681f32012-03-06 12:14:42 +0100166 GtkWidget *results_widget;
167 GtkWidget *disk_util_frame;
Jens Axboe6b79c802012-03-08 10:51:36 +0100168 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100169 unsigned int job_added;
170 struct thread_options o;
Jens Axboee0681f32012-03-06 12:14:42 +0100171};
172
Jens Axboe2f99deb2012-03-09 14:37:29 +0100173static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100174{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100175 struct graph *g;
176
177 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
178 graph_title(g, "IOPS");
179 graph_x_title(g, "Time (secs)");
180 graph_y_title(g, "IOs / sec");
181 graph_add_label(g, "Read IOPS");
182 graph_add_label(g, "Write IOPS");
183 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
184 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
185 line_graph_set_data_count_limit(g, 100);
186 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100187}
188
Jens Axboe2f99deb2012-03-09 14:37:29 +0100189static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100190{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100191 struct graph *g;
192
193 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
194 graph_title(g, "Bandwidth");
195 graph_x_title(g, "Time (secs)");
196 graph_y_title(g, "Kbytes / sec");
197 graph_add_label(g, "Read Bandwidth");
198 graph_add_label(g, "Write Bandwidth");
199 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
200 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
201 line_graph_set_data_count_limit(g, 100);
202 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100203}
204
Jens Axboe2f99deb2012-03-09 14:37:29 +0100205static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100206{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100207 g->iops_graph = setup_iops_graph();
208 g->bandwidth_graph = setup_bandwidth_graph();
209}
210
211static void clear_ge_ui_info(struct gui_entry *ge)
212{
213 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
214 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
215 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
216 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
217 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
218 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
219 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
220 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
221 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
225 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
226 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100227}
228
Jens Axboe3650a3c2012-03-05 14:09:03 +0100229static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
230{
231 GtkWidget *entry, *frame;
232
233 frame = gtk_frame_new(label);
234 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100235 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100236 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
237 gtk_container_add(GTK_CONTAINER(frame), entry);
238
239 return entry;
240}
241
242static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
243{
244 GtkWidget *label_widget;
245 GtkWidget *frame;
246
247 frame = gtk_frame_new(label);
248 label_widget = gtk_label_new(NULL);
249 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
250 gtk_container_add(GTK_CONTAINER(frame), label_widget);
251
252 return label_widget;
253}
254
255static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
256{
257 GtkWidget *button, *box;
258
259 box = gtk_hbox_new(FALSE, 3);
260 gtk_container_add(GTK_CONTAINER(hbox), box);
261
262 button = gtk_spin_button_new_with_range(min, max, 1.0);
263 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
264
265 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
266 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
267
268 return button;
269}
270
Jens Axboe2f99deb2012-03-09 14:37:29 +0100271static void gfio_set_connected(struct gui_entry *ge, int connected)
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100272{
273 if (connected) {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100274 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
275 ge->connected = 1;
276 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
277 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100278 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100279 ge->connected = 0;
280 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
281 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
282 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
283 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100284 }
285}
286
Jens Axboe3650a3c2012-03-05 14:09:03 +0100287static void label_set_int_value(GtkWidget *entry, unsigned int val)
288{
289 char tmp[80];
290
291 sprintf(tmp, "%u", val);
292 gtk_label_set_text(GTK_LABEL(entry), tmp);
293}
294
295static void entry_set_int_value(GtkWidget *entry, unsigned int val)
296{
297 char tmp[80];
298
299 sprintf(tmp, "%u", val);
300 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
301}
302
Jens Axboea2697902012-03-05 16:43:49 +0100303#define ALIGN_LEFT 1
304#define ALIGN_RIGHT 2
305#define INVISIBLE 4
306#define UNSORTABLE 8
307
308GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
309{
310 GtkCellRenderer *renderer;
311 GtkTreeViewColumn *col;
312 double xalign = 0.0; /* left as default */
313 PangoAlignment align;
314 gboolean visible;
315
316 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
317 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
318 PANGO_ALIGN_CENTER;
319 visible = !(flags & INVISIBLE);
320
321 renderer = gtk_cell_renderer_text_new();
322 col = gtk_tree_view_column_new();
323
324 gtk_tree_view_column_set_title(col, title);
325 if (!(flags & UNSORTABLE))
326 gtk_tree_view_column_set_sort_column_id(col, index);
327 gtk_tree_view_column_set_resizable(col, TRUE);
328 gtk_tree_view_column_pack_start(col, renderer, TRUE);
329 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
330 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
331 switch (align) {
332 case PANGO_ALIGN_LEFT:
333 xalign = 0.0;
334 break;
335 case PANGO_ALIGN_CENTER:
336 xalign = 0.5;
337 break;
338 case PANGO_ALIGN_RIGHT:
339 xalign = 1.0;
340 break;
341 }
342 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
343 gtk_tree_view_column_set_visible(col, visible);
344 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
345 return col;
346}
347
Jens Axboe9b260bd2012-03-06 11:02:52 +0100348static void gfio_ui_setup_log(struct gui *ui)
349{
350 GtkTreeSelection *selection;
351 GtkListStore *model;
352 GtkWidget *tree_view;
353
354 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
355
356 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
357 gtk_widget_set_can_focus(tree_view, FALSE);
358
359 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
360 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100361 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
362 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100363
364 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
365 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
366 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100367 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100368
369 ui->log_model = model;
370 ui->log_tree = tree_view;
371}
372
Jens Axboea2697902012-03-05 16:43:49 +0100373static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
374 fio_fp64_t *plist,
375 unsigned int len,
376 const char *base,
377 unsigned int scale)
378{
379 GType types[FIO_IO_U_LIST_MAX_LEN];
380 GtkWidget *tree_view;
381 GtkTreeSelection *selection;
382 GtkListStore *model;
383 GtkTreeIter iter;
384 int i;
385
386 for (i = 0; i < len; i++)
387 types[i] = G_TYPE_INT;
388
389 model = gtk_list_store_newv(len, types);
390
391 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
392 gtk_widget_set_can_focus(tree_view, FALSE);
393
Jens Axboe661f7412012-03-06 13:55:45 +0100394 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
395 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
396
Jens Axboea2697902012-03-05 16:43:49 +0100397 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
398 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
399
400 for (i = 0; i < len; i++) {
401 char fbuf[8];
402
403 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
404 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
405 }
406
407 gtk_list_store_append(model, &iter);
408
Jens Axboee0681f32012-03-06 12:14:42 +0100409 for (i = 0; i < len; i++) {
410 if (scale)
411 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100412 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100413 }
Jens Axboea2697902012-03-05 16:43:49 +0100414
415 return tree_view;
416}
417
418static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
419 int ddir)
420{
421 unsigned int *io_u_plat = ts->io_u_plat[ddir];
422 unsigned long nr = ts->clat_stat[ddir].samples;
423 fio_fp64_t *plist = ts->percentile_list;
424 unsigned int *ovals, len, minv, maxv, scale_down;
425 const char *base;
426 GtkWidget *tree_view, *frame, *hbox;
427 char tmp[64];
428
429 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
430 if (!len)
431 goto out;
432
433 /*
434 * We default to usecs, but if the value range is such that we
435 * should scale down to msecs, do that.
436 */
437 if (minv > 2000 && maxv > 99999) {
438 scale_down = 1;
439 base = "msec";
440 } else {
441 scale_down = 0;
442 base = "usec";
443 }
444
445 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
446
447 sprintf(tmp, "Completion percentiles (%s)", base);
448 frame = gtk_frame_new(tmp);
449 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
450
451 hbox = gtk_hbox_new(FALSE, 3);
452 gtk_container_add(GTK_CONTAINER(frame), hbox);
453
454 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
455out:
456 if (ovals)
457 free(ovals);
458}
459
Jens Axboe3650a3c2012-03-05 14:09:03 +0100460static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
461 unsigned long max, double mean, double dev)
462{
463 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100464 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100465 char *minp, *maxp;
466 char tmp[64];
467
468 if (!usec_to_msec(&min, &max, &mean, &dev))
469 base = "(msec)";
470
471 minp = num2str(min, 6, 1, 0);
472 maxp = num2str(max, 6, 1, 0);
473
Jens Axboe3650a3c2012-03-05 14:09:03 +0100474 sprintf(tmp, "%s %s", name, base);
475 frame = gtk_frame_new(tmp);
476 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
477
Jens Axboe3650a3c2012-03-05 14:09:03 +0100478 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100479 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100480
481 label = new_info_label_in_frame(hbox, "Minimum");
482 gtk_label_set_text(GTK_LABEL(label), minp);
483 label = new_info_label_in_frame(hbox, "Maximum");
484 gtk_label_set_text(GTK_LABEL(label), maxp);
485 label = new_info_label_in_frame(hbox, "Average");
486 sprintf(tmp, "%5.02f", mean);
487 gtk_label_set_text(GTK_LABEL(label), tmp);
488 label = new_info_label_in_frame(hbox, "Standard deviation");
489 sprintf(tmp, "%5.02f", dev);
490 gtk_label_set_text(GTK_LABEL(label), tmp);
491
492 free(minp);
493 free(maxp);
494
495}
496
Jens Axboeca850992012-03-05 20:04:43 +0100497#define GFIO_CLAT 1
498#define GFIO_SLAT 2
499#define GFIO_LAT 4
500
Jens Axboe3650a3c2012-03-05 14:09:03 +0100501static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
502 struct thread_stat *ts, int ddir)
503{
504 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100505 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100506 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100507 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100508 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100509 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100510 char *io_p, *bw_p, *iops_p;
511 int i2p;
512
513 if (!ts->runtime[ddir])
514 return;
515
516 i2p = is_power_of_2(rs->kb_base);
517 runt = ts->runtime[ddir];
518
519 bw = (1000 * ts->io_bytes[ddir]) / runt;
520 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
521 bw_p = num2str(bw, 6, 1, i2p);
522
523 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
524 iops_p = num2str(iops, 6, 1, 0);
525
526 box = gtk_hbox_new(FALSE, 3);
527 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
528
529 frame = gtk_frame_new(ddir_label[ddir]);
530 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
531
Jens Axboe0b761302012-03-05 20:44:11 +0100532 main_vbox = gtk_vbox_new(FALSE, 3);
533 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100534
535 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100536 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100537
538 label = new_info_label_in_frame(box, "IO");
539 gtk_label_set_text(GTK_LABEL(label), io_p);
540 label = new_info_label_in_frame(box, "Bandwidth");
541 gtk_label_set_text(GTK_LABEL(label), bw_p);
542 label = new_info_label_in_frame(box, "IOPS");
543 gtk_label_set_text(GTK_LABEL(label), iops_p);
544 label = new_info_label_in_frame(box, "Runtime (msec)");
545 label_set_int_value(label, ts->runtime[ddir]);
546
Jens Axboee0681f32012-03-06 12:14:42 +0100547 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100548 double p_of_agg = 100.0;
549 const char *bw_str = "KB";
550 char tmp[32];
551
552 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100553 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100554 if (p_of_agg > 100.0)
555 p_of_agg = 100.0;
556 }
557
Jens Axboee0681f32012-03-06 12:14:42 +0100558 if (mean[0] > 999999.9) {
559 min[0] /= 1000.0;
560 max[0] /= 1000.0;
561 mean[0] /= 1000.0;
562 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100563 bw_str = "MB";
564 }
565
Jens Axboe0b761302012-03-05 20:44:11 +0100566 sprintf(tmp, "Bandwidth (%s)", bw_str);
567 frame = gtk_frame_new(tmp);
568 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100569
Jens Axboe0b761302012-03-05 20:44:11 +0100570 box = gtk_hbox_new(FALSE, 3);
571 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100572
Jens Axboe0b761302012-03-05 20:44:11 +0100573 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100574 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100575 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100576 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100577 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100578 sprintf(tmp, "%3.2f%%", p_of_agg);
579 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100580 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100581 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100582 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100583 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100584 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100585 gtk_label_set_text(GTK_LABEL(label), tmp);
586 }
587
Jens Axboee0681f32012-03-06 12:14:42 +0100588 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100589 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100590 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100591 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100592 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100593 flags |= GFIO_LAT;
594
595 if (flags) {
596 frame = gtk_frame_new("Latency");
597 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
598
599 vbox = gtk_vbox_new(FALSE, 3);
600 gtk_container_add(GTK_CONTAINER(frame), vbox);
601
602 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100603 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100604 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100605 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100606 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100607 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100608 }
609
610 if (ts->clat_percentiles)
611 gfio_show_clat_percentiles(main_vbox, ts, ddir);
612
613
Jens Axboe3650a3c2012-03-05 14:09:03 +0100614 free(io_p);
615 free(bw_p);
616 free(iops_p);
617}
618
Jens Axboee5bd1342012-03-05 21:38:12 +0100619static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
620 const char **labels)
621{
622 GtkWidget *tree_view;
623 GtkTreeSelection *selection;
624 GtkListStore *model;
625 GtkTreeIter iter;
626 GType *types;
627 int i, skipped;
628
629 /*
630 * Check if all are empty, in which case don't bother
631 */
632 for (i = 0, skipped = 0; i < num; i++)
633 if (lat[i] <= 0.0)
634 skipped++;
635
636 if (skipped == num)
637 return NULL;
638
639 types = malloc(num * sizeof(GType));
640
641 for (i = 0; i < num; i++)
642 types[i] = G_TYPE_STRING;
643
644 model = gtk_list_store_newv(num, types);
645 free(types);
646 types = NULL;
647
648 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
649 gtk_widget_set_can_focus(tree_view, FALSE);
650
Jens Axboe661f7412012-03-06 13:55:45 +0100651 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
652 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
653
Jens Axboee5bd1342012-03-05 21:38:12 +0100654 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
655 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
656
657 for (i = 0; i < num; i++)
658 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
659
660 gtk_list_store_append(model, &iter);
661
662 for (i = 0; i < num; i++) {
663 char fbuf[32];
664
665 if (lat[i] <= 0.0)
666 sprintf(fbuf, "0.00");
667 else
668 sprintf(fbuf, "%3.2f%%", lat[i]);
669
670 gtk_list_store_set(model, &iter, i, fbuf, -1);
671 }
672
673 return tree_view;
674}
675
676static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
677{
678 GtkWidget *box, *frame, *tree_view;
679 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
680 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
681 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
682 "250", "500", "750", "1000", };
683 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
684 "250", "500", "750", "1000", "2000",
685 ">= 2000", };
686
687 stat_calc_lat_u(ts, io_u_lat_u);
688 stat_calc_lat_m(ts, io_u_lat_m);
689
690 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
691 if (tree_view) {
692 frame = gtk_frame_new("Latency buckets (usec)");
693 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
694
695 box = gtk_hbox_new(FALSE, 3);
696 gtk_container_add(GTK_CONTAINER(frame), box);
697 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
698 }
699
700 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
701 if (tree_view) {
702 frame = gtk_frame_new("Latency buckets (msec)");
703 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
704
705 box = gtk_hbox_new(FALSE, 3);
706 gtk_container_add(GTK_CONTAINER(frame), box);
707 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
708 }
709}
710
Jens Axboe2e331012012-03-05 22:07:54 +0100711static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
712{
713 GtkWidget *box, *frame, *entry;
714 double usr_cpu, sys_cpu;
715 unsigned long runtime;
716 char tmp[32];
717
718 runtime = ts->total_run_time;
719 if (runtime) {
720 double runt = (double) runtime;
721
722 usr_cpu = (double) ts->usr_time * 100 / runt;
723 sys_cpu = (double) ts->sys_time * 100 / runt;
724 } else {
725 usr_cpu = 0;
726 sys_cpu = 0;
727 }
728
729 frame = gtk_frame_new("OS resources");
730 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
731
732 box = gtk_hbox_new(FALSE, 3);
733 gtk_container_add(GTK_CONTAINER(frame), box);
734
735 entry = new_info_entry_in_frame(box, "User CPU");
736 sprintf(tmp, "%3.2f%%", usr_cpu);
737 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
738 entry = new_info_entry_in_frame(box, "System CPU");
739 sprintf(tmp, "%3.2f%%", sys_cpu);
740 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
741 entry = new_info_entry_in_frame(box, "Context switches");
742 entry_set_int_value(entry, ts->ctx);
743 entry = new_info_entry_in_frame(box, "Major faults");
744 entry_set_int_value(entry, ts->majf);
745 entry = new_info_entry_in_frame(box, "Minor faults");
746 entry_set_int_value(entry, ts->minf);
747}
Jens Axboe19998db2012-03-06 09:17:59 +0100748static void gfio_add_sc_depths_tree(GtkListStore *model,
749 struct thread_stat *ts, unsigned int len,
750 int submit)
751{
752 double io_u_dist[FIO_IO_U_MAP_NR];
753 GtkTreeIter iter;
754 /* Bits 0, and 3-8 */
755 const int add_mask = 0x1f9;
756 int i, j;
757
758 if (submit)
759 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
760 else
761 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
762
763 gtk_list_store_append(model, &iter);
764
765 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
766
767 for (i = 1, j = 0; i < len; i++) {
768 char fbuf[32];
769
770 if (!(add_mask & (1UL << (i - 1))))
771 sprintf(fbuf, "0.0%%");
772 else {
773 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
774 j++;
775 }
776
777 gtk_list_store_set(model, &iter, i, fbuf, -1);
778 }
779
780}
781
782static void gfio_add_total_depths_tree(GtkListStore *model,
783 struct thread_stat *ts, unsigned int len)
784{
785 double io_u_dist[FIO_IO_U_MAP_NR];
786 GtkTreeIter iter;
787 /* Bits 1-6, and 8 */
788 const int add_mask = 0x17e;
789 int i, j;
790
791 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
792
793 gtk_list_store_append(model, &iter);
794
795 gtk_list_store_set(model, &iter, 0, "Total", -1);
796
797 for (i = 1, j = 0; i < len; i++) {
798 char fbuf[32];
799
800 if (!(add_mask & (1UL << (i - 1))))
801 sprintf(fbuf, "0.0%%");
802 else {
803 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
804 j++;
805 }
806
807 gtk_list_store_set(model, &iter, i, fbuf, -1);
808 }
809
810}
Jens Axboe2e331012012-03-05 22:07:54 +0100811
812static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
813{
Jens Axboe2e331012012-03-05 22:07:54 +0100814 GtkWidget *frame, *box, *tree_view;
815 GtkTreeSelection *selection;
816 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100817 GType types[FIO_IO_U_MAP_NR + 1];
818 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100819#define NR_LABELS 10
820 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100821
822 frame = gtk_frame_new("IO depths");
823 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
824
825 box = gtk_hbox_new(FALSE, 3);
826 gtk_container_add(GTK_CONTAINER(frame), box);
827
Jens Axboe19998db2012-03-06 09:17:59 +0100828 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100829 types[i] = G_TYPE_STRING;
830
Jens Axboe19998db2012-03-06 09:17:59 +0100831 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100832
833 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
834 gtk_widget_set_can_focus(tree_view, FALSE);
835
Jens Axboe661f7412012-03-06 13:55:45 +0100836 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
837 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
838
Jens Axboe2e331012012-03-05 22:07:54 +0100839 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
840 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
841
Jens Axboe19998db2012-03-06 09:17:59 +0100842 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100843 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
844
Jens Axboe19998db2012-03-06 09:17:59 +0100845 gfio_add_total_depths_tree(model, ts, NR_LABELS);
846 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
847 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +0100848
849 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
850}
851
Jens Axboef9d40b42012-03-06 09:52:49 +0100852static gboolean results_window_delete(GtkWidget *w, gpointer data)
853{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100854 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +0100855
856 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100857 ge->results_window = NULL;
858 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +0100859 return TRUE;
860}
861
Jens Axboe2f99deb2012-03-09 14:37:29 +0100862static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +0100863{
864 GtkWidget *win, *notebook;
865
Jens Axboe2f99deb2012-03-09 14:37:29 +0100866 if (ge->results_window)
867 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100868
869 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
870 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +0100871 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100872 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
873 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +0100874
875 notebook = gtk_notebook_new();
876 gtk_container_add(GTK_CONTAINER(win), notebook);
877
Jens Axboe2f99deb2012-03-09 14:37:29 +0100878 ge->results_window = win;
879 ge->results_notebook = notebook;
880 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100881}
882
Jens Axboe3650a3c2012-03-05 14:09:03 +0100883static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
884 struct group_run_stats *rs)
885{
Jens Axboeb01329d2012-03-07 20:31:28 +0100886 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
Jens Axboee0681f32012-03-06 12:14:42 +0100887 struct gfio_client *gc = client->client_data;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100888
889 gdk_threads_enter();
890
Jens Axboe2f99deb2012-03-09 14:37:29 +0100891 res_win = get_results_window(gc->ge);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100892
Jens Axboeb01329d2012-03-07 20:31:28 +0100893 scroll = gtk_scrolled_window_new(NULL, NULL);
894 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
895 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
896
Jens Axboe3650a3c2012-03-05 14:09:03 +0100897 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100898
Jens Axboeb01329d2012-03-07 20:31:28 +0100899 box = gtk_hbox_new(FALSE, 0);
900 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100901
Jens Axboeb01329d2012-03-07 20:31:28 +0100902 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
903
904 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +0100905
Jens Axboee0681f32012-03-06 12:14:42 +0100906 gc->results_widget = vbox;
907
Jens Axboe3650a3c2012-03-05 14:09:03 +0100908 entry = new_info_entry_in_frame(box, "Name");
909 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
910 if (strlen(ts->description)) {
911 entry = new_info_entry_in_frame(box, "Description");
912 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
913 }
914 entry = new_info_entry_in_frame(box, "Group ID");
915 entry_set_int_value(entry, ts->groupid);
916 entry = new_info_entry_in_frame(box, "Jobs");
917 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +0100918 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +0100919 entry_set_int_value(entry, ts->error);
920 entry = new_info_entry_in_frame(box, "PID");
921 entry_set_int_value(entry, ts->pid);
922
923 if (ts->io_bytes[DDIR_READ])
924 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
925 if (ts->io_bytes[DDIR_WRITE])
926 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
927
Jens Axboee5bd1342012-03-05 21:38:12 +0100928 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +0100929 gfio_show_cpu_usage(vbox, ts);
930 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +0100931
Jens Axboe2f99deb2012-03-09 14:37:29 +0100932 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100933 gdk_threads_leave();
934}
935
Jens Axboe084d1c62012-03-03 20:28:07 +0100936static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100937{
Jens Axboe9b260bd2012-03-06 11:02:52 +0100938 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100939 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100940 GtkTreeIter iter;
941 struct tm *tm;
942 time_t sec;
943 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100944
Jens Axboe9b260bd2012-03-06 11:02:52 +0100945 sec = p->log_sec;
946 tm = localtime(&sec);
947 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
948 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
949
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100950 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +0100951
Jens Axboe2f99deb2012-03-09 14:37:29 +0100952 gtk_list_store_append(ui->log_model, &iter);
953 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
954 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
955 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
956 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100957
Jens Axboe6b79c802012-03-08 10:51:36 +0100958 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +0100959 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +0100960
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100961 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100962}
963
964static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
965{
Jens Axboee0681f32012-03-06 12:14:42 +0100966 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
967 struct gfio_client *gc = client->client_data;
968 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +0100969 double util;
970 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +0100971
Jens Axboe0050e5f2012-03-06 09:23:27 +0100972 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +0100973
Jens Axboe45dcb2e2012-03-07 16:16:50 +0100974 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +0100975 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +0100976
977 if (!gc->disk_util_frame) {
978 gc->disk_util_frame = gtk_frame_new("Disk utilization");
979 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
980 }
981
982 vbox = gtk_vbox_new(FALSE, 3);
983 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
984
985 frame = gtk_frame_new((char *) p->dus.name);
986 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
987
988 box = gtk_vbox_new(FALSE, 3);
989 gtk_container_add(GTK_CONTAINER(frame), box);
990
991 frame = gtk_frame_new("Read");
992 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
993 vbox = gtk_hbox_new(TRUE, 3);
994 gtk_container_add(GTK_CONTAINER(frame), vbox);
995 entry = new_info_entry_in_frame(vbox, "IOs");
996 entry_set_int_value(entry, p->dus.ios[0]);
997 entry = new_info_entry_in_frame(vbox, "Merges");
998 entry_set_int_value(entry, p->dus.merges[0]);
999 entry = new_info_entry_in_frame(vbox, "Sectors");
1000 entry_set_int_value(entry, p->dus.sectors[0]);
1001 entry = new_info_entry_in_frame(vbox, "Ticks");
1002 entry_set_int_value(entry, p->dus.ticks[0]);
1003
1004 frame = gtk_frame_new("Write");
1005 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1006 vbox = gtk_hbox_new(TRUE, 3);
1007 gtk_container_add(GTK_CONTAINER(frame), vbox);
1008 entry = new_info_entry_in_frame(vbox, "IOs");
1009 entry_set_int_value(entry, p->dus.ios[1]);
1010 entry = new_info_entry_in_frame(vbox, "Merges");
1011 entry_set_int_value(entry, p->dus.merges[1]);
1012 entry = new_info_entry_in_frame(vbox, "Sectors");
1013 entry_set_int_value(entry, p->dus.sectors[1]);
1014 entry = new_info_entry_in_frame(vbox, "Ticks");
1015 entry_set_int_value(entry, p->dus.ticks[1]);
1016
1017 frame = gtk_frame_new("Shared");
1018 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1019 vbox = gtk_hbox_new(TRUE, 3);
1020 gtk_container_add(GTK_CONTAINER(frame), vbox);
1021 entry = new_info_entry_in_frame(vbox, "IO ticks");
1022 entry_set_int_value(entry, p->dus.io_ticks);
1023 entry = new_info_entry_in_frame(vbox, "Time in queue");
1024 entry_set_int_value(entry, p->dus.time_in_queue);
1025
Jens Axboe604cfe32012-03-07 19:51:36 +01001026 util = 0.0;
1027 if (p->dus.msec)
1028 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1029 if (util > 100.0)
1030 util = 100.0;
1031
1032 sprintf(tmp, "%3.2f%%", util);
1033 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1034 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1035
Jens Axboee0681f32012-03-06 12:14:42 +01001036 gtk_widget_show_all(gc->results_widget);
1037out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001038 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001039}
1040
Jens Axboe3650a3c2012-03-05 14:09:03 +01001041extern int sum_stat_clients;
1042extern struct thread_stat client_ts;
1043extern struct group_run_stats client_gs;
1044
1045static int sum_stat_nr;
1046
Jens Axboe89e5fad2012-03-05 09:21:12 +01001047static void gfio_thread_status_op(struct fio_client *client,
1048 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001049{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001050 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1051
1052 gfio_display_ts(client, &p->ts, &p->rs);
1053
1054 if (sum_stat_clients == 1)
1055 return;
1056
1057 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1058 sum_group_stats(&client_gs, &p->rs);
1059
1060 client_ts.members++;
1061 client_ts.groupid = p->ts.groupid;
1062
1063 if (++sum_stat_nr == sum_stat_clients) {
1064 strcpy(client_ts.name, "All clients");
1065 gfio_display_ts(client, &client_ts, &client_gs);
1066 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001067}
1068
Jens Axboe89e5fad2012-03-05 09:21:12 +01001069static void gfio_group_stats_op(struct fio_client *client,
1070 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001071{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001072 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001073}
1074
Jens Axboe2f99deb2012-03-09 14:37:29 +01001075static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1076 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001077{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001078 struct gfio_graphs *g = data;
1079
1080 g->drawing_area_xdim = w->allocation.width;
1081 g->drawing_area_ydim = w->allocation.height;
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001082 return TRUE;
1083}
1084
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001085static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1086{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001087 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001088 cairo_t *cr;
1089
Jens Axboe2f99deb2012-03-09 14:37:29 +01001090 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1091 g->drawing_area_ydim);
1092 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1093 g->drawing_area_ydim);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001094 cr = gdk_cairo_create(w->window);
1095
1096 cairo_set_source_rgb(cr, 0, 0, 0);
1097
1098 cairo_save(cr);
1099 cairo_translate(cr, 0, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001100 line_graph_draw(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001101 cairo_stroke(cr);
1102 cairo_restore(cr);
1103
1104 cairo_save(cr);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001105 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1106 line_graph_draw(g->iops_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001107 cairo_stroke(cr);
1108 cairo_restore(cr);
1109 cairo_destroy(cr);
1110
1111 return FALSE;
1112}
1113
Jens Axboe2f99deb2012-03-09 14:37:29 +01001114/*
1115 * Client specific ETA
1116 */
1117static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001118{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001119 struct gfio_client *gc = client->client_data;
1120 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001121 static int eta_good;
1122 char eta_str[128];
1123 char output[256];
1124 char tmp[32];
1125 double perc = 0.0;
1126 int i2p = 0;
1127
Jens Axboe0050e5f2012-03-06 09:23:27 +01001128 gdk_threads_enter();
1129
Jens Axboe3e47bd22012-02-29 13:45:02 +01001130 eta_str[0] = '\0';
1131 output[0] = '\0';
1132
1133 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1134 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1135 eta_to_str(eta_str, je->eta_sec);
1136 }
1137
1138 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001139 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001140 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001141 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001142
1143#if 0
1144 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1145 if (je->m_rate || je->t_rate) {
1146 char *tr, *mr;
1147
1148 mr = num2str(je->m_rate, 4, 0, i2p);
1149 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001150 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001151 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1152 free(tr);
1153 free(mr);
1154 } else if (je->m_iops || je->t_iops)
1155 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001156
Jens Axboe2f99deb2012-03-09 14:37:29 +01001157 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1158 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1159 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1160 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001161#endif
1162
1163 if (je->eta_sec != INT_MAX && je->nr_running) {
1164 char *iops_str[2];
1165 char *rate_str[2];
1166
1167 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1168 strcpy(output, "-.-% done");
1169 else {
1170 eta_good = 1;
1171 perc *= 100.0;
1172 sprintf(output, "%3.1f%% done", perc);
1173 }
1174
1175 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1176 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1177
1178 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1179 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1180
Jens Axboe2f99deb2012-03-09 14:37:29 +01001181 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1182 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1183 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1184 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001185
Jens Axboe2f99deb2012-03-09 14:37:29 +01001186 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1187 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1188 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1189 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1190
1191 free(rate_str[0]);
1192 free(rate_str[1]);
1193 free(iops_str[0]);
1194 free(iops_str[1]);
1195 }
1196
1197 if (eta_str[0]) {
1198 char *dst = output + strlen(output);
1199
1200 sprintf(dst, " - %s", eta_str);
1201 }
1202
1203 gfio_update_thread_status(output, perc);
1204 gdk_threads_leave();
1205}
1206
1207/*
1208 * Update ETA in main window for all clients
1209 */
1210static void gfio_update_all_eta(struct jobs_eta *je)
1211{
1212 struct gui *ui = &main_ui;
1213 static int eta_good;
1214 char eta_str[128];
1215 char output[256];
1216 double perc = 0.0;
1217 int i2p = 0;
1218
1219 gdk_threads_enter();
1220
1221 eta_str[0] = '\0';
1222 output[0] = '\0';
1223
1224 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1225 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1226 eta_to_str(eta_str, je->eta_sec);
1227 }
1228
1229#if 0
1230 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1231 if (je->m_rate || je->t_rate) {
1232 char *tr, *mr;
1233
1234 mr = num2str(je->m_rate, 4, 0, i2p);
1235 tr = num2str(je->t_rate, 4, 0, i2p);
1236 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1237 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1238 free(tr);
1239 free(mr);
1240 } else if (je->m_iops || je->t_iops)
1241 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1242
1243 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1244 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1245 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1246 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1247#endif
1248
1249 if (je->eta_sec != INT_MAX && je->nr_running) {
1250 char *iops_str[2];
1251 char *rate_str[2];
1252
1253 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1254 strcpy(output, "-.-% done");
1255 else {
1256 eta_good = 1;
1257 perc *= 100.0;
1258 sprintf(output, "%3.1f%% done", perc);
1259 }
1260
1261 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1262 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1263
1264 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1265 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1266
1267 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1268 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1269 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1270 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1271
1272 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1273 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1274 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1275 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001276
Jens Axboe3e47bd22012-02-29 13:45:02 +01001277 free(rate_str[0]);
1278 free(rate_str[1]);
1279 free(iops_str[0]);
1280 free(iops_str[1]);
1281 }
1282
1283 if (eta_str[0]) {
1284 char *dst = output + strlen(output);
1285
1286 sprintf(dst, " - %s", eta_str);
1287 }
1288
1289 gfio_update_thread_status(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001290 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001291}
1292
Stephen M. Camerona1820202012-02-24 08:17:31 +01001293static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1294{
Jens Axboe843ad232012-02-29 11:44:53 +01001295 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001296 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001297 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001298 const char *os, *arch;
1299 char buf[64];
1300
1301 os = fio_get_os_string(probe->os);
1302 if (!os)
1303 os = "unknown";
1304
1305 arch = fio_get_arch_string(probe->arch);
1306 if (!arch)
1307 os = "unknown";
1308
1309 if (!client->name)
1310 client->name = strdup((char *) probe->hostname);
1311
Jens Axboe0050e5f2012-03-06 09:23:27 +01001312 gdk_threads_enter();
1313
Jens Axboe2f99deb2012-03-09 14:37:29 +01001314 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1315 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1316 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001317 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001318 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001319
Jens Axboe2f99deb2012-03-09 14:37:29 +01001320 gfio_set_connected(ge, 1);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001321
1322 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001323}
1324
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001325static void gfio_update_thread_status(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001326{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001327 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001328 static char message[100];
1329 const char *m = message;
1330
1331 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001332 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1333 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1334 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001335}
1336
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001337static void gfio_quit_op(struct fio_client *client)
1338{
Jens Axboee0681f32012-03-06 12:14:42 +01001339 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001340
Jens Axboe0050e5f2012-03-06 09:23:27 +01001341 gdk_threads_enter();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001342 gfio_set_connected(gc->ge, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001343 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001344}
1345
Jens Axboe807f9972012-03-02 10:25:24 +01001346static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1347{
1348 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001349 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001350 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001351 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001352 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001353
Jens Axboedcaeb602012-03-08 19:45:37 +01001354 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001355
Jens Axboe0050e5f2012-03-06 09:23:27 +01001356 gdk_threads_enter();
1357
Jens Axboe2f99deb2012-03-09 14:37:29 +01001358 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1359
1360 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1361 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1362 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001363
Jens Axboedcaeb602012-03-08 19:45:37 +01001364 sprintf(tmp, "%u", o->iodepth);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001365 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001366
Jens Axboedcaeb602012-03-08 19:45:37 +01001367 gc->job_added++;
1368
Jens Axboe0050e5f2012-03-06 09:23:27 +01001369 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001370}
1371
Jens Axboeed727a42012-03-02 12:14:40 +01001372static void gfio_client_timed_out(struct fio_client *client)
1373{
Jens Axboee0681f32012-03-06 12:14:42 +01001374 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001375 GtkWidget *dialog, *label, *content;
1376 char buf[256];
1377
1378 gdk_threads_enter();
1379
Jens Axboe2f99deb2012-03-09 14:37:29 +01001380 gfio_set_connected(gc->ge, 0);
1381 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001382
1383 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1384
1385 dialog = gtk_dialog_new_with_buttons("Timed out!",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001386 GTK_WINDOW(main_ui.window),
Jens Axboeed727a42012-03-02 12:14:40 +01001387 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1388 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1389
Jens Axboef1299092012-03-07 20:00:02 +01001390 /* gtk_dialog_get_content_area() is 2.14 and newer */
1391 content = GTK_DIALOG(dialog)->vbox;
1392
Jens Axboeed727a42012-03-02 12:14:40 +01001393 label = gtk_label_new((const gchar *) buf);
1394 gtk_container_add(GTK_CONTAINER(content), label);
1395 gtk_widget_show_all(dialog);
1396 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1397
1398 gtk_dialog_run(GTK_DIALOG(dialog));
1399 gtk_widget_destroy(dialog);
1400
1401 gdk_threads_leave();
1402}
1403
Jens Axboe6b79c802012-03-08 10:51:36 +01001404static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1405{
1406 struct gfio_client *gc = client->client_data;
1407
1408 gdk_threads_enter();
1409
Jens Axboe2f99deb2012-03-09 14:37:29 +01001410 gfio_set_connected(gc->ge, 0);
Jens Axboe6b79c802012-03-08 10:51:36 +01001411
1412 if (gc->err_entry)
1413 entry_set_int_value(gc->err_entry, client->error);
1414
1415 gdk_threads_leave();
1416}
1417
Stephen M. Camerona1820202012-02-24 08:17:31 +01001418struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001419 .text_op = gfio_text_op,
1420 .disk_util = gfio_disk_util_op,
1421 .thread_status = gfio_thread_status_op,
1422 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001423 .jobs_eta = gfio_update_client_eta,
1424 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001425 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001426 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001427 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001428 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001429 .stop = gfio_client_stop,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001430 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001431};
1432
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001433static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1434 __attribute__((unused)) gpointer data)
1435{
1436 gtk_main_quit();
1437}
1438
Stephen M. Cameron25927252012-02-24 08:17:31 +01001439static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001440{
Jens Axboea9eccde2012-03-09 14:59:42 +01001441 struct gui *ui = arg;
1442
1443 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001444 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001445 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001446 return NULL;
1447}
1448
Jens Axboe2f99deb2012-03-09 14:37:29 +01001449static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001450{
Jens Axboe441013b2012-03-01 08:01:52 +01001451 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001452
Jens Axboe2f99deb2012-03-09 14:37:29 +01001453 for (i = 0; i < ge->nr_job_files; i++) {
1454 ret = fio_clients_send_ini(ge->job_files[i]);
Jens Axboe441013b2012-03-01 08:01:52 +01001455 if (ret)
1456 break;
1457
Jens Axboe2f99deb2012-03-09 14:37:29 +01001458 free(ge->job_files[i]);
1459 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001460 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001461 while (i < ge->nr_job_files) {
1462 free(ge->job_files[i]);
1463 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001464 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001465 }
1466
Jens Axboe441013b2012-03-01 08:01:52 +01001467 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001468}
1469
Jens Axboe63a130b2012-03-06 20:08:59 +01001470static void *server_thread(void *arg)
1471{
1472 is_backend = 1;
1473 gfio_server_running = 1;
1474 fio_start_server(NULL);
1475 gfio_server_running = 0;
1476 return NULL;
1477}
1478
Jens Axboe2f99deb2012-03-09 14:37:29 +01001479static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001480{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001481 struct gui *ui = &main_ui;
1482
Jens Axboe63a130b2012-03-06 20:08:59 +01001483 if (!gfio_server_running) {
1484 gfio_server_running = 1;
1485 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001486 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001487 }
1488}
1489
Stephen M. Cameron25927252012-02-24 08:17:31 +01001490static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1491 gpointer data)
1492{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001493 struct gui_entry *ge = data;
1494 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001495
Jens Axboe2f99deb2012-03-09 14:37:29 +01001496 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1497 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001498}
1499
Jens Axboedf06f222012-03-02 13:32:04 +01001500static void file_open(GtkWidget *w, gpointer data);
1501
1502static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001503{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001504 struct gui_entry *ge = data;
1505 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001506
Jens Axboe2f99deb2012-03-09 14:37:29 +01001507 if (!ge->connected) {
1508 if (!ge->nr_job_files)
Jens Axboedf06f222012-03-02 13:32:04 +01001509 file_open(widget, data);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001510 if (!ge->nr_job_files)
1511 return;
1512
1513 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1514 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1515 if (!fio_client_connect(gc->client)) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001516 if (!ge->ui->handler_running)
1517 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001518 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1519 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
Jens Axboe69406b92012-03-06 14:00:42 +01001520 }
Jens Axboedf06f222012-03-02 13:32:04 +01001521 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001522 fio_client_terminate(gc->client);
1523 gfio_set_connected(ge, 0);
1524 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001525 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001526}
1527
Jens Axboeb9d2f302012-03-08 20:36:28 +01001528static void send_clicked(GtkWidget *widget, gpointer data)
1529{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001530 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001531
Jens Axboe2f99deb2012-03-09 14:37:29 +01001532 if (send_job_files(ge)) {
Jens Axboeb9d2f302012-03-08 20:36:28 +01001533 printf("Yeah, I didn't really like those options too much.\n");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001534 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001535 }
1536
Jens Axboe2f99deb2012-03-09 14:37:29 +01001537 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1538 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001539}
1540
Jens Axboe2f99deb2012-03-09 14:37:29 +01001541static GtkWidget *add_button(GtkWidget *buttonbox,
1542 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001543{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001544 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1545
1546 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1547 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1548 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1549 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1550
1551 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001552}
1553
Jens Axboe2f99deb2012-03-09 14:37:29 +01001554static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1555 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001556{
1557 int i;
1558
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001559 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001560 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001561}
1562
Jens Axboe0420ba62012-02-29 11:16:52 +01001563static void on_info_bar_response(GtkWidget *widget, gint response,
1564 gpointer data)
1565{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001566 struct gui *ui = &main_ui;
1567
Jens Axboe0420ba62012-02-29 11:16:52 +01001568 if (response == GTK_RESPONSE_OK) {
1569 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001570 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001571 }
1572}
1573
Jens Axboedf06f222012-03-02 13:32:04 +01001574void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001575{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001576 struct gui *ui = &main_ui;
1577
1578 if (ui->error_info_bar == NULL) {
1579 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001580 GTK_RESPONSE_OK,
1581 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001582 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1583 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001584 GTK_MESSAGE_ERROR);
1585
Jens Axboe2f99deb2012-03-09 14:37:29 +01001586 ui->error_label = gtk_label_new(error->message);
1587 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1588 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001589
Jens Axboe2f99deb2012-03-09 14:37:29 +01001590 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1591 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001592 } else {
1593 char buffer[256];
1594 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001595 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001596 }
1597}
1598
Jens Axboe62bc9372012-03-07 11:45:07 +01001599struct connection_widgets
1600{
1601 GtkWidget *hentry;
1602 GtkWidget *combo;
1603 GtkWidget *button;
1604};
1605
1606static void hostname_cb(GtkEntry *entry, gpointer data)
1607{
1608 struct connection_widgets *cw = data;
1609 int uses_net = 0, is_localhost = 0;
1610 const gchar *text;
1611 gchar *ctext;
1612
1613 /*
1614 * Check whether to display the 'auto start backend' box
1615 * or not. Show it if we are a localhost and using network,
1616 * or using a socket.
1617 */
1618 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1619 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1620 uses_net = 1;
1621 g_free(ctext);
1622
1623 if (uses_net) {
1624 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1625 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1626 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1627 !strcmp(text, "ip6-loopback"))
1628 is_localhost = 1;
1629 }
1630
1631 if (!uses_net || is_localhost) {
1632 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1633 gtk_widget_set_sensitive(cw->button, 1);
1634 } else {
1635 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1636 gtk_widget_set_sensitive(cw->button, 0);
1637 }
1638}
1639
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001640static int get_connection_details(char **host, int *port, int *type,
1641 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001642{
Jens Axboe62bc9372012-03-07 11:45:07 +01001643 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1644 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001645 char *typeentry;
1646
1647 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001648 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001649 GTK_DIALOG_DESTROY_WITH_PARENT,
1650 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1651 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1652
1653 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001654 /* gtk_dialog_get_content_area() is 2.14 and newer */
1655 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001656 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1657
1658 box = gtk_vbox_new(FALSE, 6);
1659 gtk_container_add(GTK_CONTAINER(frame), box);
1660
1661 hbox = gtk_hbox_new(TRUE, 10);
1662 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01001663 cw.hentry = gtk_entry_new();
1664 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1665 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001666
1667 frame = gtk_frame_new("Port");
1668 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1669 box = gtk_vbox_new(FALSE, 10);
1670 gtk_container_add(GTK_CONTAINER(frame), box);
1671
1672 hbox = gtk_hbox_new(TRUE, 4);
1673 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1674 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1675
1676 frame = gtk_frame_new("Type");
1677 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1678 box = gtk_vbox_new(FALSE, 10);
1679 gtk_container_add(GTK_CONTAINER(frame), box);
1680
1681 hbox = gtk_hbox_new(TRUE, 4);
1682 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1683
Jens Axboe62bc9372012-03-07 11:45:07 +01001684 cw.combo = gtk_combo_box_new_text();
1685 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1686 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1687 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1688 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001689
Jens Axboe62bc9372012-03-07 11:45:07 +01001690 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001691
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001692 frame = gtk_frame_new("Options");
1693 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1694 box = gtk_vbox_new(FALSE, 10);
1695 gtk_container_add(GTK_CONTAINER(frame), box);
1696
1697 hbox = gtk_hbox_new(TRUE, 4);
1698 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1699
Jens Axboe62bc9372012-03-07 11:45:07 +01001700 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1701 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1702 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.");
1703 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1704
1705 /*
1706 * Connect edit signal, so we can show/not-show the auto start button
1707 */
1708 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1709 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001710
Jens Axboea7a42ce2012-03-02 13:12:04 +01001711 gtk_widget_show_all(dialog);
1712
1713 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1714 gtk_widget_destroy(dialog);
1715 return 1;
1716 }
1717
Jens Axboe62bc9372012-03-07 11:45:07 +01001718 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001719 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1720
Jens Axboe62bc9372012-03-07 11:45:07 +01001721 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001722 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1723 *type = Fio_client_ipv4;
1724 else if (!strncmp(typeentry, "IPv6", 4))
1725 *type = Fio_client_ipv6;
1726 else
1727 *type = Fio_client_socket;
1728 g_free(typeentry);
1729
Jens Axboe62bc9372012-03-07 11:45:07 +01001730 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001731
Jens Axboea7a42ce2012-03-02 13:12:04 +01001732 gtk_widget_destroy(dialog);
1733 return 0;
1734}
1735
Jens Axboe2f99deb2012-03-09 14:37:29 +01001736static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01001737{
1738 struct gfio_client *gc;
1739
1740 gc = malloc(sizeof(*gc));
1741 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001742 gc->ge = ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001743 gc->client = client;
1744
Jens Axboe2f99deb2012-03-09 14:37:29 +01001745 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01001746
1747 client->client_data = gc;
1748}
1749
Jens Axboe2f99deb2012-03-09 14:37:29 +01001750static GtkWidget *new_client_page(struct gui_entry *ge);
1751
1752static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1753{
1754 struct gui_entry *ge;
1755
1756 ge = malloc(sizeof(*ge));
1757 memset(ge, 0, sizeof(*ge));
1758 INIT_FLIST_HEAD(&ge->list);
1759 flist_add_tail(&ge->list, &ui->list);
1760 ge->ui = ui;
1761 return ge;
1762}
1763
1764/*
1765 * FIXME: need more handling here
1766 */
1767static void ge_destroy(GtkWidget *w, gpointer data)
1768{
1769 struct gui_entry *ge = data;
1770
1771 flist_del(&ge->list);
1772 free(ge);
1773}
1774
1775static struct gui_entry *get_new_ge_with_tab(const char *name)
1776{
1777 struct gui_entry *ge;
1778
1779 ge = alloc_new_gui_entry(&main_ui);
1780
1781 ge->vbox = new_client_page(ge);
1782 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1783
1784 ge->page_label = gtk_label_new(name);
1785 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1786
1787 gtk_widget_show_all(main_ui.window);
1788 return ge;
1789}
1790
1791static void file_new(GtkWidget *w, gpointer data)
1792{
1793 get_new_ge_with_tab("Untitled");
1794}
1795
1796/*
1797 * Return the 'ge' corresponding to the tab. If the active tab is the
1798 * main tab, open a new tab.
1799 */
1800static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1801{
1802 struct flist_head *entry;
1803 struct gui_entry *ge;
1804
1805 if (!cur_page)
1806 return get_new_ge_with_tab("Untitled");
1807
1808 flist_for_each(entry, &main_ui.list) {
1809 ge = flist_entry(entry, struct gui_entry, list);
1810 if (ge->page_num == cur_page)
1811 return ge;
1812 }
1813
1814 return NULL;
1815}
1816
Jens Axboe0420ba62012-02-29 11:16:52 +01001817static void file_open(GtkWidget *w, gpointer data)
1818{
Jens Axboe63a130b2012-03-06 20:08:59 +01001819 struct gui *ui = data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001820 GtkWidget *dialog;
Jens Axboe0420ba62012-02-29 11:16:52 +01001821 GSList *filenames, *fn_glist;
1822 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001823 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001824 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001825 struct gui_entry *ge;
1826 gint cur_page;
1827
1828 /*
1829 * Creates new tab if current tab is the main window, or the
1830 * current tab already has a client.
1831 */
1832 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1833 ge = get_ge_from_page(cur_page);
1834 if (ge->client)
1835 ge = get_new_ge_with_tab("Untitled");
1836
1837 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01001838
1839 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01001840 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01001841 GTK_FILE_CHOOSER_ACTION_OPEN,
1842 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1843 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1844 NULL);
1845 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1846
1847 filter = gtk_file_filter_new();
1848 gtk_file_filter_add_pattern(filter, "*.fio");
1849 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01001850 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe0420ba62012-02-29 11:16:52 +01001851 gtk_file_filter_add_mime_type(filter, "text/fio");
1852 gtk_file_filter_set_name(filter, "Fio job file");
1853 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1854
1855 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1856 gtk_widget_destroy(dialog);
1857 return;
1858 }
1859
1860 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001861
1862 gtk_widget_destroy(dialog);
1863
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001864 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01001865 goto err;
1866
Jens Axboe0420ba62012-02-29 11:16:52 +01001867 filenames = fn_glist;
1868 while (filenames != NULL) {
Jens Axboee0681f32012-03-06 12:14:42 +01001869 struct fio_client *client;
1870
Jens Axboe2f99deb2012-03-09 14:37:29 +01001871 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1872 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1873 ge->nr_job_files++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001874
Jens Axboee0681f32012-03-06 12:14:42 +01001875 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1876 if (!client) {
Jens Axboedf06f222012-03-02 13:32:04 +01001877 GError *error;
1878
1879 error = g_error_new(g_quark_from_string("fio"), 1,
1880 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01001881 report_error(error);
1882 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01001883 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001884 gfio_client_added(ge, client);
Jens Axboe0420ba62012-02-29 11:16:52 +01001885
1886 g_free(filenames->data);
1887 filenames = g_slist_next(filenames);
1888 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01001889 free(host);
Jens Axboe63a130b2012-03-06 20:08:59 +01001890
1891 if (server_start)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001892 gfio_start_server();
Jens Axboea7a42ce2012-03-02 13:12:04 +01001893err:
Jens Axboe0420ba62012-02-29 11:16:52 +01001894 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01001895}
1896
1897static void file_save(GtkWidget *w, gpointer data)
1898{
Jens Axboe63a130b2012-03-06 20:08:59 +01001899 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01001900 GtkWidget *dialog;
1901
1902 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01001903 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01001904 GTK_FILE_CHOOSER_ACTION_SAVE,
1905 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1906 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1907 NULL);
1908
1909 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1910 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1911
1912 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1913 char *filename;
1914
1915 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1916 // save_job_file(filename);
1917 g_free(filename);
1918 }
1919 gtk_widget_destroy(dialog);
1920}
1921
Jens Axboe9b260bd2012-03-06 11:02:52 +01001922static void view_log_destroy(GtkWidget *w, gpointer data)
1923{
1924 struct gui *ui = (struct gui *) data;
1925
1926 gtk_widget_ref(ui->log_tree);
1927 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1928 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01001929 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001930}
1931
1932static void view_log(GtkWidget *w, gpointer data)
1933{
Jens Axboe4cbe7212012-03-06 13:36:17 +01001934 GtkWidget *win, *scroll, *vbox, *box;
1935 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001936
Jens Axboe4cbe7212012-03-06 13:36:17 +01001937 if (ui->log_view)
1938 return;
1939
1940 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001941 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01001942 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001943
Jens Axboe4cbe7212012-03-06 13:36:17 +01001944 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001945
Jens Axboe4cbe7212012-03-06 13:36:17 +01001946 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1947
1948 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1949
1950 box = gtk_hbox_new(TRUE, 0);
1951 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1952 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1953 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1954
1955 vbox = gtk_vbox_new(TRUE, 5);
1956 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1957
1958 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001959 gtk_widget_show_all(win);
1960}
1961
Jens Axboe46974a72012-03-02 19:34:13 +01001962static void preferences(GtkWidget *w, gpointer data)
1963{
Jens Axboef3e84402012-03-07 13:14:32 +01001964 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe46974a72012-03-02 19:34:13 +01001965 int i;
1966
1967 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001968 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01001969 GTK_DIALOG_DESTROY_WITH_PARENT,
1970 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1971 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1972 NULL);
1973
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001974 frame = gtk_frame_new("Debug logging");
Jens Axboe46974a72012-03-02 19:34:13 +01001975 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001976
1977 vbox = gtk_vbox_new(FALSE, 6);
1978 gtk_container_add(GTK_CONTAINER(frame), vbox);
1979
Jens Axboe46974a72012-03-02 19:34:13 +01001980 box = gtk_hbox_new(FALSE, 6);
Jens Axboef3e84402012-03-07 13:14:32 +01001981 gtk_container_add(GTK_CONTAINER(vbox), box);
Jens Axboe46974a72012-03-02 19:34:13 +01001982
1983 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1984
1985 for (i = 0; i < FD_DEBUG_MAX; i++) {
Jens Axboef3e84402012-03-07 13:14:32 +01001986 if (i == 7) {
1987 box = gtk_hbox_new(FALSE, 6);
1988 gtk_container_add(GTK_CONTAINER(vbox), box);
1989 }
1990
1991
Jens Axboe46974a72012-03-02 19:34:13 +01001992 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001993 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
Jens Axboe46974a72012-03-02 19:34:13 +01001994 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1995 }
1996
Jens Axboef3e84402012-03-07 13:14:32 +01001997 frame = gtk_frame_new("Graph font");
1998 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1999 vbox = gtk_vbox_new(FALSE, 6);
2000 gtk_container_add(GTK_CONTAINER(frame), vbox);
2001
2002 font = gtk_font_button_new();
2003 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
2004
Jens Axboe46974a72012-03-02 19:34:13 +01002005 gtk_widget_show_all(dialog);
2006
2007 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2008 gtk_widget_destroy(dialog);
2009 return;
2010 }
2011
2012 for (i = 0; i < FD_DEBUG_MAX; i++) {
2013 int set;
2014
2015 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2016 if (set)
2017 fio_debug |= (1UL << i);
2018 }
2019
Jens Axboef3e84402012-03-07 13:14:32 +01002020 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe46974a72012-03-02 19:34:13 +01002021 gtk_widget_destroy(dialog);
2022}
2023
Jens Axboe0420ba62012-02-29 11:16:52 +01002024static void about_dialog(GtkWidget *w, gpointer data)
2025{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002026 const char *authors[] = {
2027 "Jens Axboe <axboe@kernel.dk>",
2028 "Stephen Carmeron <stephenmcameron@gmail.com>",
2029 NULL
2030 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002031 const char *license[] = {
2032 "Fio is free software; you can redistribute it and/or modify "
2033 "it under the terms of the GNU General Public License as published by "
2034 "the Free Software Foundation; either version 2 of the License, or "
2035 "(at your option) any later version.\n",
2036 "Fio is distributed in the hope that it will be useful, "
2037 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2038 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2039 "GNU General Public License for more details.\n",
2040 "You should have received a copy of the GNU General Public License "
2041 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2042 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2043 };
2044 char *license_trans;
2045
2046 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2047 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002048
Jens Axboe0420ba62012-02-29 11:16:52 +01002049 gtk_show_about_dialog(NULL,
2050 "program-name", "gfio",
2051 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002052 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002053 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2054 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002055 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002056 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002057 "logo-icon-name", "fio",
2058 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002059 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002060 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002061
Jens Axboe2f99deb2012-03-09 14:37:29 +01002062 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002063}
2064
2065static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002066 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002067 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002068 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002069 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe46974a72012-03-02 19:34:13 +01002070 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2071 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2072 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002073 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe46974a72012-03-02 19:34:13 +01002074 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2075 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002076};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002077static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002078
2079static const gchar *ui_string = " \
2080 <ui> \
2081 <menubar name=\"MainMenu\"> \
2082 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002083 <menuitem name=\"New\" action=\"NewFile\" /> \
2084 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002085 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2086 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002087 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002088 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2089 <separator name=\"Separator3\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002090 <menuitem name=\"Quit\" action=\"Quit\" /> \
2091 </menu> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002092 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2093 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2094 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002095 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2096 <menuitem name=\"About\" action=\"About\" /> \
2097 </menu> \
2098 </menubar> \
2099 </ui> \
2100";
2101
Jens Axboe4cbe7212012-03-06 13:36:17 +01002102static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2103 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002104{
2105 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2106 GError *error = 0;
2107
2108 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002109 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002110
2111 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2112 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2113
2114 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2115 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2116}
2117
2118void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2119 GtkWidget *vbox, GtkUIManager *ui_manager)
2120{
2121 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2122}
2123
Jens Axboe2f99deb2012-03-09 14:37:29 +01002124static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002125{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002126 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002127 GdkColor white;
Jens Axboe0420ba62012-02-29 11:16:52 +01002128
Jens Axboe2f99deb2012-03-09 14:37:29 +01002129 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002130
Jens Axboe2f99deb2012-03-09 14:37:29 +01002131 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2132 ge->topvbox = gtk_vbox_new(FALSE, 3);
2133 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2134 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002135
Jens Axboe3e47bd22012-02-29 13:45:02 +01002136 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002137 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002138 probe_frame = gtk_vbox_new(FALSE, 3);
2139 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2140
2141 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002142 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2143 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2144 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2145 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2146 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002147
Jens Axboe3e47bd22012-02-29 13:45:02 +01002148 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002149 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2150
2151 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2152 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2153 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2154 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2155 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2156 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2157
2158 probe_box = gtk_hbox_new(FALSE, 3);
2159 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2160 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2161 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2162 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2163 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2164
2165 /*
2166 * Only add this if we have a commit rate
2167 */
2168#if 0
2169 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002170 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002171
Jens Axboe2f99deb2012-03-09 14:37:29 +01002172 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2173 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2174
2175 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2176 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2177#endif
2178
2179 /*
2180 * Set up a drawing area and IOPS and bandwidth graphs
2181 */
2182 gdk_color_parse("white", &white);
2183 ge->graphs.drawing_area = gtk_drawing_area_new();
2184 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2185 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2186 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2187 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2188 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2189 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2190 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2191 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2192 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2193 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2194 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2195 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2196 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2197 ge->graphs.drawing_area);
2198 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2199 TRUE, TRUE, 0);
2200
2201 setup_graphs(&ge->graphs);
2202
2203 /*
2204 * Set up alignments for widgets at the bottom of ui,
2205 * align bottom left, expand horizontally but not vertically
2206 */
2207 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2208 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2209 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2210 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2211 FALSE, FALSE, 0);
2212
2213 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2214
2215 /*
2216 * Set up thread status progress bar
2217 */
2218 ge->thread_status_pb = gtk_progress_bar_new();
2219 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2220 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2221 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2222
2223
2224 return main_vbox;
2225}
2226
2227static GtkWidget *new_main_page(struct gui *ui)
2228{
2229 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2230 GdkColor white;
2231
2232 main_vbox = gtk_vbox_new(FALSE, 3);
2233
2234 /*
2235 * Set up alignments for widgets at the top of ui,
2236 * align top left, expand horizontally but not vertically
2237 */
2238 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2239 ui->topvbox = gtk_vbox_new(FALSE, 0);
2240 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2241 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2242
2243 probe = gtk_frame_new("Run statistics");
2244 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2245 probe_frame = gtk_vbox_new(FALSE, 3);
2246 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002247
2248 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002249 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboeca850992012-03-05 20:04:43 +01002250 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2251 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2252 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2253 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002254
2255 /*
2256 * Only add this if we have a commit rate
2257 */
2258#if 0
2259 probe_box = gtk_hbox_new(FALSE, 3);
2260 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2261
Jens Axboe3e47bd22012-02-29 13:45:02 +01002262 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2263 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2264
Jens Axboe3e47bd22012-02-29 13:45:02 +01002265 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2266 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002267#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002268
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002269 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002270 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002271 */
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002272 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002273 ui->graphs.drawing_area = gtk_drawing_area_new();
2274 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2275 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2276 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2277 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2278 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2279 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2280 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2281 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2282 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002283 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2284 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2285 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002286 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002287 ui->graphs.drawing_area);
2288 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002289 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002290
Jens Axboe2f99deb2012-03-09 14:37:29 +01002291 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002292
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002293 /*
2294 * Set up alignments for widgets at the bottom of ui,
2295 * align bottom left, expand horizontally but not vertically
2296 */
2297 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2298 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2299 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002300 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002301 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002302
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002303 /*
2304 * Set up thread status progress bar
2305 */
2306 ui->thread_status_pb = gtk_progress_bar_new();
2307 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002308 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002309 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2310
Jens Axboe2f99deb2012-03-09 14:37:29 +01002311 return main_vbox;
2312}
2313
2314static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2315 guint page, gpointer data)
2316
2317{
2318 return TRUE;
2319}
2320
2321static void init_ui(int *argc, char **argv[], struct gui *ui)
2322{
2323 GtkSettings *settings;
2324 GtkUIManager *uimanager;
2325 GtkWidget *menu, *vbox;
2326
2327 /* Magical g*thread incantation, you just need this thread stuff.
2328 * Without it, the update that happens in gfio_update_thread_status
2329 * doesn't really happen in a timely fashion, you need expose events
2330 */
2331 if (!g_thread_supported())
2332 g_thread_init(NULL);
2333 gdk_threads_init();
2334
2335 gtk_init(argc, argv);
2336 settings = gtk_settings_get_default();
2337 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2338 g_type_init();
2339
2340 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2341 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2342 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2343
2344 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2345 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2346
2347 ui->vbox = gtk_vbox_new(FALSE, 0);
2348 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2349
2350 uimanager = gtk_ui_manager_new();
2351 menu = get_menubar_menu(ui->window, uimanager, ui);
2352 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2353
2354 ui->notebook = gtk_notebook_new();
2355 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2356 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2357
2358 vbox = new_main_page(ui);
2359
2360 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2361
Jens Axboe9b260bd2012-03-06 11:02:52 +01002362 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002363
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002364 gtk_widget_show_all(ui->window);
2365}
2366
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002367int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002368{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002369 if (initialize_fio(envp))
2370 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01002371 if (fio_init_options())
2372 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01002373
Jens Axboe2f99deb2012-03-09 14:37:29 +01002374 memset(&main_ui, 0, sizeof(main_ui));
2375 INIT_FLIST_HEAD(&main_ui.list);
2376
2377 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01002378
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002379 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002380 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002381 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002382 return 0;
2383}