blob: 1555dcfb65cecc8b0ec270c137a9ec5a0a4f110c [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
122 struct flist_head list;
123} main_ui;
124
125/*
126 * Notebook entry
127 */
128struct gui_entry {
129 struct flist_head list;
130 struct gui *ui;
131
132 GtkWidget *vbox;
133 GtkWidget *topvbox;
134 GtkWidget *topalign;
135 GtkWidget *bottomalign;
136 GtkWidget *thread_status_pb;
137 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100138 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100139 GtkWidget *scrolled_window;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100140 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100141 GtkWidget *error_info_bar;
142 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100143 GtkWidget *results_notebook;
144 GtkWidget *results_window;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100145 GtkListStore *log_model;
146 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100147 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100148 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100149 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100150 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100151 GtkWidget *page_label;
152 gint page_num;
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100153 int connected;
Stephen M. Cameron25927252012-02-24 08:17:31 +0100154 pthread_t t;
Jens Axboe0420ba62012-02-29 11:16:52 +0100155
Jens Axboeb9d2f302012-03-08 20:36:28 +0100156 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100157 int nr_job_files;
158 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100159};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100160
Jens Axboee0681f32012-03-06 12:14:42 +0100161struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100162 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100163 struct fio_client *client;
Jens Axboee0681f32012-03-06 12:14:42 +0100164 GtkWidget *results_widget;
165 GtkWidget *disk_util_frame;
Jens Axboe6b79c802012-03-08 10:51:36 +0100166 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100167 unsigned int job_added;
168 struct thread_options o;
Jens Axboee0681f32012-03-06 12:14:42 +0100169};
170
Jens Axboe2f99deb2012-03-09 14:37:29 +0100171static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100172{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100173 struct graph *g;
174
175 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
176 graph_title(g, "IOPS");
177 graph_x_title(g, "Time (secs)");
178 graph_y_title(g, "IOs / sec");
179 graph_add_label(g, "Read IOPS");
180 graph_add_label(g, "Write IOPS");
181 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
182 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
183 line_graph_set_data_count_limit(g, 100);
184 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100185}
186
Jens Axboe2f99deb2012-03-09 14:37:29 +0100187static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100188{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100189 struct graph *g;
190
191 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
192 graph_title(g, "Bandwidth");
193 graph_x_title(g, "Time (secs)");
194 graph_y_title(g, "Kbytes / sec");
195 graph_add_label(g, "Read Bandwidth");
196 graph_add_label(g, "Write Bandwidth");
197 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
198 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
199 line_graph_set_data_count_limit(g, 100);
200 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100201}
202
Jens Axboe2f99deb2012-03-09 14:37:29 +0100203static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100204{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100205 g->iops_graph = setup_iops_graph();
206 g->bandwidth_graph = setup_bandwidth_graph();
207}
208
209static void clear_ge_ui_info(struct gui_entry *ge)
210{
211 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
212 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
213 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
214 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
215 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
216 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), "");
217 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), "");
218 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), "");
219 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
220 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
221 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
222 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
223 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
224 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100225}
226
Jens Axboe3650a3c2012-03-05 14:09:03 +0100227static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
228{
229 GtkWidget *entry, *frame;
230
231 frame = gtk_frame_new(label);
232 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100233 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100234 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
235 gtk_container_add(GTK_CONTAINER(frame), entry);
236
237 return entry;
238}
239
240static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
241{
242 GtkWidget *label_widget;
243 GtkWidget *frame;
244
245 frame = gtk_frame_new(label);
246 label_widget = gtk_label_new(NULL);
247 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
248 gtk_container_add(GTK_CONTAINER(frame), label_widget);
249
250 return label_widget;
251}
252
253static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
254{
255 GtkWidget *button, *box;
256
257 box = gtk_hbox_new(FALSE, 3);
258 gtk_container_add(GTK_CONTAINER(hbox), box);
259
260 button = gtk_spin_button_new_with_range(min, max, 1.0);
261 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
262
263 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
264 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
265
266 return button;
267}
268
Jens Axboe2f99deb2012-03-09 14:37:29 +0100269static void gfio_set_connected(struct gui_entry *ge, int connected)
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100270{
271 if (connected) {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100272 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
273 ge->connected = 1;
274 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
275 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100276 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100277 ge->connected = 0;
278 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
279 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
280 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
281 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100282 }
283}
284
Jens Axboe3650a3c2012-03-05 14:09:03 +0100285static void label_set_int_value(GtkWidget *entry, unsigned int val)
286{
287 char tmp[80];
288
289 sprintf(tmp, "%u", val);
290 gtk_label_set_text(GTK_LABEL(entry), tmp);
291}
292
293static void entry_set_int_value(GtkWidget *entry, unsigned int val)
294{
295 char tmp[80];
296
297 sprintf(tmp, "%u", val);
298 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
299}
300
Jens Axboea2697902012-03-05 16:43:49 +0100301#define ALIGN_LEFT 1
302#define ALIGN_RIGHT 2
303#define INVISIBLE 4
304#define UNSORTABLE 8
305
306GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
307{
308 GtkCellRenderer *renderer;
309 GtkTreeViewColumn *col;
310 double xalign = 0.0; /* left as default */
311 PangoAlignment align;
312 gboolean visible;
313
314 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
315 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
316 PANGO_ALIGN_CENTER;
317 visible = !(flags & INVISIBLE);
318
319 renderer = gtk_cell_renderer_text_new();
320 col = gtk_tree_view_column_new();
321
322 gtk_tree_view_column_set_title(col, title);
323 if (!(flags & UNSORTABLE))
324 gtk_tree_view_column_set_sort_column_id(col, index);
325 gtk_tree_view_column_set_resizable(col, TRUE);
326 gtk_tree_view_column_pack_start(col, renderer, TRUE);
327 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
328 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
329 switch (align) {
330 case PANGO_ALIGN_LEFT:
331 xalign = 0.0;
332 break;
333 case PANGO_ALIGN_CENTER:
334 xalign = 0.5;
335 break;
336 case PANGO_ALIGN_RIGHT:
337 xalign = 1.0;
338 break;
339 }
340 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
341 gtk_tree_view_column_set_visible(col, visible);
342 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
343 return col;
344}
345
Jens Axboe9b260bd2012-03-06 11:02:52 +0100346static void gfio_ui_setup_log(struct gui *ui)
347{
348 GtkTreeSelection *selection;
349 GtkListStore *model;
350 GtkWidget *tree_view;
351
352 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
353
354 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
355 gtk_widget_set_can_focus(tree_view, FALSE);
356
357 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
358 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100359 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
360 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100361
362 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
363 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
364 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100365 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100366
367 ui->log_model = model;
368 ui->log_tree = tree_view;
369}
370
Jens Axboea2697902012-03-05 16:43:49 +0100371static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
372 fio_fp64_t *plist,
373 unsigned int len,
374 const char *base,
375 unsigned int scale)
376{
377 GType types[FIO_IO_U_LIST_MAX_LEN];
378 GtkWidget *tree_view;
379 GtkTreeSelection *selection;
380 GtkListStore *model;
381 GtkTreeIter iter;
382 int i;
383
384 for (i = 0; i < len; i++)
385 types[i] = G_TYPE_INT;
386
387 model = gtk_list_store_newv(len, types);
388
389 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
390 gtk_widget_set_can_focus(tree_view, FALSE);
391
Jens Axboe661f7412012-03-06 13:55:45 +0100392 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
393 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
394
Jens Axboea2697902012-03-05 16:43:49 +0100395 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
396 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
397
398 for (i = 0; i < len; i++) {
399 char fbuf[8];
400
401 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
402 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
403 }
404
405 gtk_list_store_append(model, &iter);
406
Jens Axboee0681f32012-03-06 12:14:42 +0100407 for (i = 0; i < len; i++) {
408 if (scale)
409 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100410 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100411 }
Jens Axboea2697902012-03-05 16:43:49 +0100412
413 return tree_view;
414}
415
416static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
417 int ddir)
418{
419 unsigned int *io_u_plat = ts->io_u_plat[ddir];
420 unsigned long nr = ts->clat_stat[ddir].samples;
421 fio_fp64_t *plist = ts->percentile_list;
422 unsigned int *ovals, len, minv, maxv, scale_down;
423 const char *base;
424 GtkWidget *tree_view, *frame, *hbox;
425 char tmp[64];
426
427 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
428 if (!len)
429 goto out;
430
431 /*
432 * We default to usecs, but if the value range is such that we
433 * should scale down to msecs, do that.
434 */
435 if (minv > 2000 && maxv > 99999) {
436 scale_down = 1;
437 base = "msec";
438 } else {
439 scale_down = 0;
440 base = "usec";
441 }
442
443 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
444
445 sprintf(tmp, "Completion percentiles (%s)", base);
446 frame = gtk_frame_new(tmp);
447 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
448
449 hbox = gtk_hbox_new(FALSE, 3);
450 gtk_container_add(GTK_CONTAINER(frame), hbox);
451
452 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
453out:
454 if (ovals)
455 free(ovals);
456}
457
Jens Axboe3650a3c2012-03-05 14:09:03 +0100458static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
459 unsigned long max, double mean, double dev)
460{
461 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100462 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100463 char *minp, *maxp;
464 char tmp[64];
465
466 if (!usec_to_msec(&min, &max, &mean, &dev))
467 base = "(msec)";
468
469 minp = num2str(min, 6, 1, 0);
470 maxp = num2str(max, 6, 1, 0);
471
Jens Axboe3650a3c2012-03-05 14:09:03 +0100472 sprintf(tmp, "%s %s", name, base);
473 frame = gtk_frame_new(tmp);
474 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
475
Jens Axboe3650a3c2012-03-05 14:09:03 +0100476 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100477 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100478
479 label = new_info_label_in_frame(hbox, "Minimum");
480 gtk_label_set_text(GTK_LABEL(label), minp);
481 label = new_info_label_in_frame(hbox, "Maximum");
482 gtk_label_set_text(GTK_LABEL(label), maxp);
483 label = new_info_label_in_frame(hbox, "Average");
484 sprintf(tmp, "%5.02f", mean);
485 gtk_label_set_text(GTK_LABEL(label), tmp);
486 label = new_info_label_in_frame(hbox, "Standard deviation");
487 sprintf(tmp, "%5.02f", dev);
488 gtk_label_set_text(GTK_LABEL(label), tmp);
489
490 free(minp);
491 free(maxp);
492
493}
494
Jens Axboeca850992012-03-05 20:04:43 +0100495#define GFIO_CLAT 1
496#define GFIO_SLAT 2
497#define GFIO_LAT 4
498
Jens Axboe3650a3c2012-03-05 14:09:03 +0100499static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
500 struct thread_stat *ts, int ddir)
501{
502 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100503 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100504 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100505 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100506 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100507 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100508 char *io_p, *bw_p, *iops_p;
509 int i2p;
510
511 if (!ts->runtime[ddir])
512 return;
513
514 i2p = is_power_of_2(rs->kb_base);
515 runt = ts->runtime[ddir];
516
517 bw = (1000 * ts->io_bytes[ddir]) / runt;
518 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
519 bw_p = num2str(bw, 6, 1, i2p);
520
521 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
522 iops_p = num2str(iops, 6, 1, 0);
523
524 box = gtk_hbox_new(FALSE, 3);
525 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
526
527 frame = gtk_frame_new(ddir_label[ddir]);
528 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
529
Jens Axboe0b761302012-03-05 20:44:11 +0100530 main_vbox = gtk_vbox_new(FALSE, 3);
531 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100532
533 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100534 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100535
536 label = new_info_label_in_frame(box, "IO");
537 gtk_label_set_text(GTK_LABEL(label), io_p);
538 label = new_info_label_in_frame(box, "Bandwidth");
539 gtk_label_set_text(GTK_LABEL(label), bw_p);
540 label = new_info_label_in_frame(box, "IOPS");
541 gtk_label_set_text(GTK_LABEL(label), iops_p);
542 label = new_info_label_in_frame(box, "Runtime (msec)");
543 label_set_int_value(label, ts->runtime[ddir]);
544
Jens Axboee0681f32012-03-06 12:14:42 +0100545 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100546 double p_of_agg = 100.0;
547 const char *bw_str = "KB";
548 char tmp[32];
549
550 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100551 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100552 if (p_of_agg > 100.0)
553 p_of_agg = 100.0;
554 }
555
Jens Axboee0681f32012-03-06 12:14:42 +0100556 if (mean[0] > 999999.9) {
557 min[0] /= 1000.0;
558 max[0] /= 1000.0;
559 mean[0] /= 1000.0;
560 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100561 bw_str = "MB";
562 }
563
Jens Axboe0b761302012-03-05 20:44:11 +0100564 sprintf(tmp, "Bandwidth (%s)", bw_str);
565 frame = gtk_frame_new(tmp);
566 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100567
Jens Axboe0b761302012-03-05 20:44:11 +0100568 box = gtk_hbox_new(FALSE, 3);
569 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100570
Jens Axboe0b761302012-03-05 20:44:11 +0100571 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100572 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100573 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100574 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100575 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100576 sprintf(tmp, "%3.2f%%", p_of_agg);
577 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100578 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100579 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100580 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100581 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100582 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100583 gtk_label_set_text(GTK_LABEL(label), tmp);
584 }
585
Jens Axboee0681f32012-03-06 12:14:42 +0100586 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100587 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100588 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100589 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100590 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100591 flags |= GFIO_LAT;
592
593 if (flags) {
594 frame = gtk_frame_new("Latency");
595 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
596
597 vbox = gtk_vbox_new(FALSE, 3);
598 gtk_container_add(GTK_CONTAINER(frame), vbox);
599
600 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100601 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100602 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100603 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100604 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100605 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100606 }
607
608 if (ts->clat_percentiles)
609 gfio_show_clat_percentiles(main_vbox, ts, ddir);
610
611
Jens Axboe3650a3c2012-03-05 14:09:03 +0100612 free(io_p);
613 free(bw_p);
614 free(iops_p);
615}
616
Jens Axboee5bd1342012-03-05 21:38:12 +0100617static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
618 const char **labels)
619{
620 GtkWidget *tree_view;
621 GtkTreeSelection *selection;
622 GtkListStore *model;
623 GtkTreeIter iter;
624 GType *types;
625 int i, skipped;
626
627 /*
628 * Check if all are empty, in which case don't bother
629 */
630 for (i = 0, skipped = 0; i < num; i++)
631 if (lat[i] <= 0.0)
632 skipped++;
633
634 if (skipped == num)
635 return NULL;
636
637 types = malloc(num * sizeof(GType));
638
639 for (i = 0; i < num; i++)
640 types[i] = G_TYPE_STRING;
641
642 model = gtk_list_store_newv(num, types);
643 free(types);
644 types = NULL;
645
646 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
647 gtk_widget_set_can_focus(tree_view, FALSE);
648
Jens Axboe661f7412012-03-06 13:55:45 +0100649 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
650 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
651
Jens Axboee5bd1342012-03-05 21:38:12 +0100652 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
653 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
654
655 for (i = 0; i < num; i++)
656 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
657
658 gtk_list_store_append(model, &iter);
659
660 for (i = 0; i < num; i++) {
661 char fbuf[32];
662
663 if (lat[i] <= 0.0)
664 sprintf(fbuf, "0.00");
665 else
666 sprintf(fbuf, "%3.2f%%", lat[i]);
667
668 gtk_list_store_set(model, &iter, i, fbuf, -1);
669 }
670
671 return tree_view;
672}
673
674static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
675{
676 GtkWidget *box, *frame, *tree_view;
677 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
678 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
679 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
680 "250", "500", "750", "1000", };
681 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
682 "250", "500", "750", "1000", "2000",
683 ">= 2000", };
684
685 stat_calc_lat_u(ts, io_u_lat_u);
686 stat_calc_lat_m(ts, io_u_lat_m);
687
688 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
689 if (tree_view) {
690 frame = gtk_frame_new("Latency buckets (usec)");
691 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
692
693 box = gtk_hbox_new(FALSE, 3);
694 gtk_container_add(GTK_CONTAINER(frame), box);
695 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
696 }
697
698 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
699 if (tree_view) {
700 frame = gtk_frame_new("Latency buckets (msec)");
701 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
702
703 box = gtk_hbox_new(FALSE, 3);
704 gtk_container_add(GTK_CONTAINER(frame), box);
705 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
706 }
707}
708
Jens Axboe2e331012012-03-05 22:07:54 +0100709static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
710{
711 GtkWidget *box, *frame, *entry;
712 double usr_cpu, sys_cpu;
713 unsigned long runtime;
714 char tmp[32];
715
716 runtime = ts->total_run_time;
717 if (runtime) {
718 double runt = (double) runtime;
719
720 usr_cpu = (double) ts->usr_time * 100 / runt;
721 sys_cpu = (double) ts->sys_time * 100 / runt;
722 } else {
723 usr_cpu = 0;
724 sys_cpu = 0;
725 }
726
727 frame = gtk_frame_new("OS resources");
728 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
729
730 box = gtk_hbox_new(FALSE, 3);
731 gtk_container_add(GTK_CONTAINER(frame), box);
732
733 entry = new_info_entry_in_frame(box, "User CPU");
734 sprintf(tmp, "%3.2f%%", usr_cpu);
735 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
736 entry = new_info_entry_in_frame(box, "System CPU");
737 sprintf(tmp, "%3.2f%%", sys_cpu);
738 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
739 entry = new_info_entry_in_frame(box, "Context switches");
740 entry_set_int_value(entry, ts->ctx);
741 entry = new_info_entry_in_frame(box, "Major faults");
742 entry_set_int_value(entry, ts->majf);
743 entry = new_info_entry_in_frame(box, "Minor faults");
744 entry_set_int_value(entry, ts->minf);
745}
Jens Axboe19998db2012-03-06 09:17:59 +0100746static void gfio_add_sc_depths_tree(GtkListStore *model,
747 struct thread_stat *ts, unsigned int len,
748 int submit)
749{
750 double io_u_dist[FIO_IO_U_MAP_NR];
751 GtkTreeIter iter;
752 /* Bits 0, and 3-8 */
753 const int add_mask = 0x1f9;
754 int i, j;
755
756 if (submit)
757 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
758 else
759 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
760
761 gtk_list_store_append(model, &iter);
762
763 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
764
765 for (i = 1, j = 0; i < len; i++) {
766 char fbuf[32];
767
768 if (!(add_mask & (1UL << (i - 1))))
769 sprintf(fbuf, "0.0%%");
770 else {
771 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
772 j++;
773 }
774
775 gtk_list_store_set(model, &iter, i, fbuf, -1);
776 }
777
778}
779
780static void gfio_add_total_depths_tree(GtkListStore *model,
781 struct thread_stat *ts, unsigned int len)
782{
783 double io_u_dist[FIO_IO_U_MAP_NR];
784 GtkTreeIter iter;
785 /* Bits 1-6, and 8 */
786 const int add_mask = 0x17e;
787 int i, j;
788
789 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
790
791 gtk_list_store_append(model, &iter);
792
793 gtk_list_store_set(model, &iter, 0, "Total", -1);
794
795 for (i = 1, j = 0; i < len; i++) {
796 char fbuf[32];
797
798 if (!(add_mask & (1UL << (i - 1))))
799 sprintf(fbuf, "0.0%%");
800 else {
801 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
802 j++;
803 }
804
805 gtk_list_store_set(model, &iter, i, fbuf, -1);
806 }
807
808}
Jens Axboe2e331012012-03-05 22:07:54 +0100809
810static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
811{
Jens Axboe2e331012012-03-05 22:07:54 +0100812 GtkWidget *frame, *box, *tree_view;
813 GtkTreeSelection *selection;
814 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100815 GType types[FIO_IO_U_MAP_NR + 1];
816 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100817#define NR_LABELS 10
818 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100819
820 frame = gtk_frame_new("IO depths");
821 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
822
823 box = gtk_hbox_new(FALSE, 3);
824 gtk_container_add(GTK_CONTAINER(frame), box);
825
Jens Axboe19998db2012-03-06 09:17:59 +0100826 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100827 types[i] = G_TYPE_STRING;
828
Jens Axboe19998db2012-03-06 09:17:59 +0100829 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100830
831 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
832 gtk_widget_set_can_focus(tree_view, FALSE);
833
Jens Axboe661f7412012-03-06 13:55:45 +0100834 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
835 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
836
Jens Axboe2e331012012-03-05 22:07:54 +0100837 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
838 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
839
Jens Axboe19998db2012-03-06 09:17:59 +0100840 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100841 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
842
Jens Axboe19998db2012-03-06 09:17:59 +0100843 gfio_add_total_depths_tree(model, ts, NR_LABELS);
844 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
845 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +0100846
847 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
848}
849
Jens Axboef9d40b42012-03-06 09:52:49 +0100850static gboolean results_window_delete(GtkWidget *w, gpointer data)
851{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100852 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +0100853
854 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100855 ge->results_window = NULL;
856 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +0100857 return TRUE;
858}
859
Jens Axboe2f99deb2012-03-09 14:37:29 +0100860static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +0100861{
862 GtkWidget *win, *notebook;
863
Jens Axboe2f99deb2012-03-09 14:37:29 +0100864 if (ge->results_window)
865 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100866
867 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
868 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +0100869 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100870 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
871 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +0100872
873 notebook = gtk_notebook_new();
874 gtk_container_add(GTK_CONTAINER(win), notebook);
875
Jens Axboe2f99deb2012-03-09 14:37:29 +0100876 ge->results_window = win;
877 ge->results_notebook = notebook;
878 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100879}
880
Jens Axboe3650a3c2012-03-05 14:09:03 +0100881static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
882 struct group_run_stats *rs)
883{
Jens Axboeb01329d2012-03-07 20:31:28 +0100884 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
Jens Axboee0681f32012-03-06 12:14:42 +0100885 struct gfio_client *gc = client->client_data;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100886
887 gdk_threads_enter();
888
Jens Axboe2f99deb2012-03-09 14:37:29 +0100889 res_win = get_results_window(gc->ge);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100890
Jens Axboeb01329d2012-03-07 20:31:28 +0100891 scroll = gtk_scrolled_window_new(NULL, NULL);
892 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
893 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
894
Jens Axboe3650a3c2012-03-05 14:09:03 +0100895 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100896
Jens Axboeb01329d2012-03-07 20:31:28 +0100897 box = gtk_hbox_new(FALSE, 0);
898 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100899
Jens Axboeb01329d2012-03-07 20:31:28 +0100900 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
901
902 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +0100903
Jens Axboee0681f32012-03-06 12:14:42 +0100904 gc->results_widget = vbox;
905
Jens Axboe3650a3c2012-03-05 14:09:03 +0100906 entry = new_info_entry_in_frame(box, "Name");
907 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
908 if (strlen(ts->description)) {
909 entry = new_info_entry_in_frame(box, "Description");
910 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
911 }
912 entry = new_info_entry_in_frame(box, "Group ID");
913 entry_set_int_value(entry, ts->groupid);
914 entry = new_info_entry_in_frame(box, "Jobs");
915 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +0100916 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +0100917 entry_set_int_value(entry, ts->error);
918 entry = new_info_entry_in_frame(box, "PID");
919 entry_set_int_value(entry, ts->pid);
920
921 if (ts->io_bytes[DDIR_READ])
922 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
923 if (ts->io_bytes[DDIR_WRITE])
924 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
925
Jens Axboee5bd1342012-03-05 21:38:12 +0100926 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +0100927 gfio_show_cpu_usage(vbox, ts);
928 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +0100929
Jens Axboe2f99deb2012-03-09 14:37:29 +0100930 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100931 gdk_threads_leave();
932}
933
Jens Axboe084d1c62012-03-03 20:28:07 +0100934static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +0100935{
Jens Axboe9b260bd2012-03-06 11:02:52 +0100936 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100937 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100938 GtkTreeIter iter;
939 struct tm *tm;
940 time_t sec;
941 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100942
Jens Axboe9b260bd2012-03-06 11:02:52 +0100943 sec = p->log_sec;
944 tm = localtime(&sec);
945 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
946 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
947
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100948 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +0100949
Jens Axboe2f99deb2012-03-09 14:37:29 +0100950 gtk_list_store_append(ui->log_model, &iter);
951 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
952 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
953 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
954 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100955
Jens Axboe6b79c802012-03-08 10:51:36 +0100956 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +0100957 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +0100958
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100959 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +0100960}
961
962static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
963{
Jens Axboee0681f32012-03-06 12:14:42 +0100964 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
965 struct gfio_client *gc = client->client_data;
966 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +0100967 double util;
968 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +0100969
Jens Axboe0050e5f2012-03-06 09:23:27 +0100970 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +0100971
Jens Axboe45dcb2e2012-03-07 16:16:50 +0100972 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +0100973 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +0100974
975 if (!gc->disk_util_frame) {
976 gc->disk_util_frame = gtk_frame_new("Disk utilization");
977 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
978 }
979
980 vbox = gtk_vbox_new(FALSE, 3);
981 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
982
983 frame = gtk_frame_new((char *) p->dus.name);
984 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
985
986 box = gtk_vbox_new(FALSE, 3);
987 gtk_container_add(GTK_CONTAINER(frame), box);
988
989 frame = gtk_frame_new("Read");
990 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
991 vbox = gtk_hbox_new(TRUE, 3);
992 gtk_container_add(GTK_CONTAINER(frame), vbox);
993 entry = new_info_entry_in_frame(vbox, "IOs");
994 entry_set_int_value(entry, p->dus.ios[0]);
995 entry = new_info_entry_in_frame(vbox, "Merges");
996 entry_set_int_value(entry, p->dus.merges[0]);
997 entry = new_info_entry_in_frame(vbox, "Sectors");
998 entry_set_int_value(entry, p->dus.sectors[0]);
999 entry = new_info_entry_in_frame(vbox, "Ticks");
1000 entry_set_int_value(entry, p->dus.ticks[0]);
1001
1002 frame = gtk_frame_new("Write");
1003 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1004 vbox = gtk_hbox_new(TRUE, 3);
1005 gtk_container_add(GTK_CONTAINER(frame), vbox);
1006 entry = new_info_entry_in_frame(vbox, "IOs");
1007 entry_set_int_value(entry, p->dus.ios[1]);
1008 entry = new_info_entry_in_frame(vbox, "Merges");
1009 entry_set_int_value(entry, p->dus.merges[1]);
1010 entry = new_info_entry_in_frame(vbox, "Sectors");
1011 entry_set_int_value(entry, p->dus.sectors[1]);
1012 entry = new_info_entry_in_frame(vbox, "Ticks");
1013 entry_set_int_value(entry, p->dus.ticks[1]);
1014
1015 frame = gtk_frame_new("Shared");
1016 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1017 vbox = gtk_hbox_new(TRUE, 3);
1018 gtk_container_add(GTK_CONTAINER(frame), vbox);
1019 entry = new_info_entry_in_frame(vbox, "IO ticks");
1020 entry_set_int_value(entry, p->dus.io_ticks);
1021 entry = new_info_entry_in_frame(vbox, "Time in queue");
1022 entry_set_int_value(entry, p->dus.time_in_queue);
1023
Jens Axboe604cfe32012-03-07 19:51:36 +01001024 util = 0.0;
1025 if (p->dus.msec)
1026 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1027 if (util > 100.0)
1028 util = 100.0;
1029
1030 sprintf(tmp, "%3.2f%%", util);
1031 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1032 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1033
Jens Axboee0681f32012-03-06 12:14:42 +01001034 gtk_widget_show_all(gc->results_widget);
1035out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001036 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001037}
1038
Jens Axboe3650a3c2012-03-05 14:09:03 +01001039extern int sum_stat_clients;
1040extern struct thread_stat client_ts;
1041extern struct group_run_stats client_gs;
1042
1043static int sum_stat_nr;
1044
Jens Axboe89e5fad2012-03-05 09:21:12 +01001045static void gfio_thread_status_op(struct fio_client *client,
1046 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001047{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001048 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1049
1050 gfio_display_ts(client, &p->ts, &p->rs);
1051
1052 if (sum_stat_clients == 1)
1053 return;
1054
1055 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1056 sum_group_stats(&client_gs, &p->rs);
1057
1058 client_ts.members++;
1059 client_ts.groupid = p->ts.groupid;
1060
1061 if (++sum_stat_nr == sum_stat_clients) {
1062 strcpy(client_ts.name, "All clients");
1063 gfio_display_ts(client, &client_ts, &client_gs);
1064 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001065}
1066
Jens Axboe89e5fad2012-03-05 09:21:12 +01001067static void gfio_group_stats_op(struct fio_client *client,
1068 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001069{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001070 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001071}
1072
Jens Axboe2f99deb2012-03-09 14:37:29 +01001073static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1074 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001075{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001076 struct gfio_graphs *g = data;
1077
1078 g->drawing_area_xdim = w->allocation.width;
1079 g->drawing_area_ydim = w->allocation.height;
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001080 return TRUE;
1081}
1082
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001083static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1084{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001085 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001086 cairo_t *cr;
1087
Jens Axboe2f99deb2012-03-09 14:37:29 +01001088 graph_set_size(g->iops_graph, g->drawing_area_xdim / 2.0,
1089 g->drawing_area_ydim);
1090 graph_set_size(g->bandwidth_graph, g->drawing_area_xdim / 2.0,
1091 g->drawing_area_ydim);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001092 cr = gdk_cairo_create(w->window);
1093
1094 cairo_set_source_rgb(cr, 0, 0, 0);
1095
1096 cairo_save(cr);
1097 cairo_translate(cr, 0, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001098 line_graph_draw(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001099 cairo_stroke(cr);
1100 cairo_restore(cr);
1101
1102 cairo_save(cr);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001103 cairo_translate(cr, g->drawing_area_xdim / 2.0, 0);
1104 line_graph_draw(g->iops_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001105 cairo_stroke(cr);
1106 cairo_restore(cr);
1107 cairo_destroy(cr);
1108
1109 return FALSE;
1110}
1111
Jens Axboe2f99deb2012-03-09 14:37:29 +01001112/*
1113 * Client specific ETA
1114 */
1115static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001116{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001117 struct gfio_client *gc = client->client_data;
1118 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001119 static int eta_good;
1120 char eta_str[128];
1121 char output[256];
1122 char tmp[32];
1123 double perc = 0.0;
1124 int i2p = 0;
1125
Jens Axboe0050e5f2012-03-06 09:23:27 +01001126 gdk_threads_enter();
1127
Jens Axboe3e47bd22012-02-29 13:45:02 +01001128 eta_str[0] = '\0';
1129 output[0] = '\0';
1130
1131 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1132 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1133 eta_to_str(eta_str, je->eta_sec);
1134 }
1135
1136 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001137 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001138 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001139 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001140
1141#if 0
1142 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1143 if (je->m_rate || je->t_rate) {
1144 char *tr, *mr;
1145
1146 mr = num2str(je->m_rate, 4, 0, i2p);
1147 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001148 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001149 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1150 free(tr);
1151 free(mr);
1152 } else if (je->m_iops || je->t_iops)
1153 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001154
Jens Axboe2f99deb2012-03-09 14:37:29 +01001155 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1156 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1157 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1158 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001159#endif
1160
1161 if (je->eta_sec != INT_MAX && je->nr_running) {
1162 char *iops_str[2];
1163 char *rate_str[2];
1164
1165 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1166 strcpy(output, "-.-% done");
1167 else {
1168 eta_good = 1;
1169 perc *= 100.0;
1170 sprintf(output, "%3.1f%% done", perc);
1171 }
1172
1173 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1174 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1175
1176 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1177 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1178
Jens Axboe2f99deb2012-03-09 14:37:29 +01001179 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1180 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1181 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1182 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001183
Jens Axboe2f99deb2012-03-09 14:37:29 +01001184 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1185 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1186 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1187 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1188
1189 free(rate_str[0]);
1190 free(rate_str[1]);
1191 free(iops_str[0]);
1192 free(iops_str[1]);
1193 }
1194
1195 if (eta_str[0]) {
1196 char *dst = output + strlen(output);
1197
1198 sprintf(dst, " - %s", eta_str);
1199 }
1200
1201 gfio_update_thread_status(output, perc);
1202 gdk_threads_leave();
1203}
1204
1205/*
1206 * Update ETA in main window for all clients
1207 */
1208static void gfio_update_all_eta(struct jobs_eta *je)
1209{
1210 struct gui *ui = &main_ui;
1211 static int eta_good;
1212 char eta_str[128];
1213 char output[256];
1214 double perc = 0.0;
1215 int i2p = 0;
1216
1217 gdk_threads_enter();
1218
1219 eta_str[0] = '\0';
1220 output[0] = '\0';
1221
1222 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1223 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1224 eta_to_str(eta_str, je->eta_sec);
1225 }
1226
1227#if 0
1228 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1229 if (je->m_rate || je->t_rate) {
1230 char *tr, *mr;
1231
1232 mr = num2str(je->m_rate, 4, 0, i2p);
1233 tr = num2str(je->t_rate, 4, 0, i2p);
1234 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1235 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1236 free(tr);
1237 free(mr);
1238 } else if (je->m_iops || je->t_iops)
1239 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1240
1241 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1242 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1243 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1244 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1245#endif
1246
1247 if (je->eta_sec != INT_MAX && je->nr_running) {
1248 char *iops_str[2];
1249 char *rate_str[2];
1250
1251 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1252 strcpy(output, "-.-% done");
1253 else {
1254 eta_good = 1;
1255 perc *= 100.0;
1256 sprintf(output, "%3.1f%% done", perc);
1257 }
1258
1259 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1260 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1261
1262 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1263 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1264
1265 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1266 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1267 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1268 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1269
1270 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1271 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1272 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1273 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001274
Jens Axboe3e47bd22012-02-29 13:45:02 +01001275 free(rate_str[0]);
1276 free(rate_str[1]);
1277 free(iops_str[0]);
1278 free(iops_str[1]);
1279 }
1280
1281 if (eta_str[0]) {
1282 char *dst = output + strlen(output);
1283
1284 sprintf(dst, " - %s", eta_str);
1285 }
1286
1287 gfio_update_thread_status(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001288 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001289}
1290
Stephen M. Camerona1820202012-02-24 08:17:31 +01001291static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1292{
Jens Axboe843ad232012-02-29 11:44:53 +01001293 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001294 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001295 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001296 const char *os, *arch;
1297 char buf[64];
1298
1299 os = fio_get_os_string(probe->os);
1300 if (!os)
1301 os = "unknown";
1302
1303 arch = fio_get_arch_string(probe->arch);
1304 if (!arch)
1305 os = "unknown";
1306
1307 if (!client->name)
1308 client->name = strdup((char *) probe->hostname);
1309
Jens Axboe0050e5f2012-03-06 09:23:27 +01001310 gdk_threads_enter();
1311
Jens Axboe2f99deb2012-03-09 14:37:29 +01001312 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1313 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1314 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001315 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001316 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001317
Jens Axboe2f99deb2012-03-09 14:37:29 +01001318 gfio_set_connected(ge, 1);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001319
1320 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001321}
1322
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +01001323static void gfio_update_thread_status(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001324{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001325 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001326 static char message[100];
1327 const char *m = message;
1328
1329 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001330 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1331 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1332 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001333}
1334
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001335static void gfio_quit_op(struct fio_client *client)
1336{
Jens Axboee0681f32012-03-06 12:14:42 +01001337 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001338
Jens Axboe0050e5f2012-03-06 09:23:27 +01001339 gdk_threads_enter();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001340 gfio_set_connected(gc->ge, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001341 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001342}
1343
Jens Axboe807f9972012-03-02 10:25:24 +01001344static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1345{
1346 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001347 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001348 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001349 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001350 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001351
Jens Axboedcaeb602012-03-08 19:45:37 +01001352 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001353
Jens Axboe0050e5f2012-03-06 09:23:27 +01001354 gdk_threads_enter();
1355
Jens Axboe2f99deb2012-03-09 14:37:29 +01001356 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1357
1358 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), (gchar *) o->name);
1359 gtk_entry_set_text(GTK_ENTRY(ge->eta.iotype), ddir_str(o->td_ddir));
1360 gtk_entry_set_text(GTK_ENTRY(ge->eta.ioengine), (gchar *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001361
Jens Axboedcaeb602012-03-08 19:45:37 +01001362 sprintf(tmp, "%u", o->iodepth);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001363 gtk_entry_set_text(GTK_ENTRY(ge->eta.iodepth), tmp);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001364
Jens Axboedcaeb602012-03-08 19:45:37 +01001365 gc->job_added++;
1366
Jens Axboe0050e5f2012-03-06 09:23:27 +01001367 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001368}
1369
Jens Axboeed727a42012-03-02 12:14:40 +01001370static void gfio_client_timed_out(struct fio_client *client)
1371{
Jens Axboee0681f32012-03-06 12:14:42 +01001372 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001373 GtkWidget *dialog, *label, *content;
1374 char buf[256];
1375
1376 gdk_threads_enter();
1377
Jens Axboe2f99deb2012-03-09 14:37:29 +01001378 gfio_set_connected(gc->ge, 0);
1379 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001380
1381 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
1382
1383 dialog = gtk_dialog_new_with_buttons("Timed out!",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001384 GTK_WINDOW(main_ui.window),
Jens Axboeed727a42012-03-02 12:14:40 +01001385 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1386 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1387
Jens Axboef1299092012-03-07 20:00:02 +01001388 /* gtk_dialog_get_content_area() is 2.14 and newer */
1389 content = GTK_DIALOG(dialog)->vbox;
1390
Jens Axboeed727a42012-03-02 12:14:40 +01001391 label = gtk_label_new((const gchar *) buf);
1392 gtk_container_add(GTK_CONTAINER(content), label);
1393 gtk_widget_show_all(dialog);
1394 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1395
1396 gtk_dialog_run(GTK_DIALOG(dialog));
1397 gtk_widget_destroy(dialog);
1398
1399 gdk_threads_leave();
1400}
1401
Jens Axboe6b79c802012-03-08 10:51:36 +01001402static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1403{
1404 struct gfio_client *gc = client->client_data;
1405
1406 gdk_threads_enter();
1407
Jens Axboe2f99deb2012-03-09 14:37:29 +01001408 gfio_set_connected(gc->ge, 0);
Jens Axboe6b79c802012-03-08 10:51:36 +01001409
1410 if (gc->err_entry)
1411 entry_set_int_value(gc->err_entry, client->error);
1412
1413 gdk_threads_leave();
1414}
1415
Stephen M. Camerona1820202012-02-24 08:17:31 +01001416struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001417 .text_op = gfio_text_op,
1418 .disk_util = gfio_disk_util_op,
1419 .thread_status = gfio_thread_status_op,
1420 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001421 .jobs_eta = gfio_update_client_eta,
1422 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001423 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001424 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001425 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001426 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001427 .stop = gfio_client_stop,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001428 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001429};
1430
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001431static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1432 __attribute__((unused)) gpointer data)
1433{
1434 gtk_main_quit();
1435}
1436
Stephen M. Cameron25927252012-02-24 08:17:31 +01001437static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001438{
Stephen M. Cameron25927252012-02-24 08:17:31 +01001439 fio_handle_clients(&gfio_client_ops);
Stephen M. Cameron25927252012-02-24 08:17:31 +01001440 return NULL;
1441}
1442
Jens Axboe2f99deb2012-03-09 14:37:29 +01001443static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001444{
Jens Axboe441013b2012-03-01 08:01:52 +01001445 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001446
Jens Axboe2f99deb2012-03-09 14:37:29 +01001447 for (i = 0; i < ge->nr_job_files; i++) {
1448 ret = fio_clients_send_ini(ge->job_files[i]);
Jens Axboe441013b2012-03-01 08:01:52 +01001449 if (ret)
1450 break;
1451
Jens Axboe2f99deb2012-03-09 14:37:29 +01001452 free(ge->job_files[i]);
1453 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001454 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001455 while (i < ge->nr_job_files) {
1456 free(ge->job_files[i]);
1457 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001458 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001459 }
1460
Jens Axboe441013b2012-03-01 08:01:52 +01001461 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001462}
1463
Jens Axboe63a130b2012-03-06 20:08:59 +01001464static void *server_thread(void *arg)
1465{
1466 is_backend = 1;
1467 gfio_server_running = 1;
1468 fio_start_server(NULL);
1469 gfio_server_running = 0;
1470 return NULL;
1471}
1472
Jens Axboe2f99deb2012-03-09 14:37:29 +01001473static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001474{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001475 struct gui *ui = &main_ui;
1476
Jens Axboe63a130b2012-03-06 20:08:59 +01001477 if (!gfio_server_running) {
1478 gfio_server_running = 1;
1479 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001480 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001481 }
1482}
1483
Stephen M. Cameron25927252012-02-24 08:17:31 +01001484static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1485 gpointer data)
1486{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001487 struct gui_entry *ge = data;
1488 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001489
Jens Axboe2f99deb2012-03-09 14:37:29 +01001490 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1491 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001492}
1493
Jens Axboedf06f222012-03-02 13:32:04 +01001494static void file_open(GtkWidget *w, gpointer data);
1495
1496static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001497{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001498 struct gui_entry *ge = data;
1499 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001500
Jens Axboe2f99deb2012-03-09 14:37:29 +01001501 if (!ge->connected) {
1502 if (!ge->nr_job_files)
Jens Axboedf06f222012-03-02 13:32:04 +01001503 file_open(widget, data);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001504 if (!ge->nr_job_files)
1505 return;
1506
1507 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1508 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
1509 if (!fio_client_connect(gc->client)) {
1510 pthread_create(&ge->t, NULL, job_thread, NULL);
1511 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1512 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
Jens Axboe69406b92012-03-06 14:00:42 +01001513 }
Jens Axboedf06f222012-03-02 13:32:04 +01001514 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001515 fio_client_terminate(gc->client);
1516 gfio_set_connected(ge, 0);
1517 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001518 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001519}
1520
Jens Axboeb9d2f302012-03-08 20:36:28 +01001521static void send_clicked(GtkWidget *widget, gpointer data)
1522{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001523 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001524
Jens Axboe2f99deb2012-03-09 14:37:29 +01001525 if (send_job_files(ge)) {
Jens Axboeb9d2f302012-03-08 20:36:28 +01001526 printf("Yeah, I didn't really like those options too much.\n");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001527 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001528 }
1529
Jens Axboe2f99deb2012-03-09 14:37:29 +01001530 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1531 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001532}
1533
Jens Axboe2f99deb2012-03-09 14:37:29 +01001534static GtkWidget *add_button(GtkWidget *buttonbox,
1535 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001536{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001537 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1538
1539 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1540 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1541 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1542 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1543
1544 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001545}
1546
Jens Axboe2f99deb2012-03-09 14:37:29 +01001547static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1548 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001549{
1550 int i;
1551
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001552 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001553 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001554}
1555
Jens Axboe0420ba62012-02-29 11:16:52 +01001556static void on_info_bar_response(GtkWidget *widget, gint response,
1557 gpointer data)
1558{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001559 struct gui *ui = &main_ui;
1560
Jens Axboe0420ba62012-02-29 11:16:52 +01001561 if (response == GTK_RESPONSE_OK) {
1562 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001563 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001564 }
1565}
1566
Jens Axboedf06f222012-03-02 13:32:04 +01001567void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001568{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001569 struct gui *ui = &main_ui;
1570
1571 if (ui->error_info_bar == NULL) {
1572 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001573 GTK_RESPONSE_OK,
1574 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001575 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1576 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001577 GTK_MESSAGE_ERROR);
1578
Jens Axboe2f99deb2012-03-09 14:37:29 +01001579 ui->error_label = gtk_label_new(error->message);
1580 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1581 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001582
Jens Axboe2f99deb2012-03-09 14:37:29 +01001583 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1584 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001585 } else {
1586 char buffer[256];
1587 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001588 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001589 }
1590}
1591
Jens Axboe62bc9372012-03-07 11:45:07 +01001592struct connection_widgets
1593{
1594 GtkWidget *hentry;
1595 GtkWidget *combo;
1596 GtkWidget *button;
1597};
1598
1599static void hostname_cb(GtkEntry *entry, gpointer data)
1600{
1601 struct connection_widgets *cw = data;
1602 int uses_net = 0, is_localhost = 0;
1603 const gchar *text;
1604 gchar *ctext;
1605
1606 /*
1607 * Check whether to display the 'auto start backend' box
1608 * or not. Show it if we are a localhost and using network,
1609 * or using a socket.
1610 */
1611 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1612 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1613 uses_net = 1;
1614 g_free(ctext);
1615
1616 if (uses_net) {
1617 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1618 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1619 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1620 !strcmp(text, "ip6-loopback"))
1621 is_localhost = 1;
1622 }
1623
1624 if (!uses_net || is_localhost) {
1625 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1626 gtk_widget_set_sensitive(cw->button, 1);
1627 } else {
1628 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1629 gtk_widget_set_sensitive(cw->button, 0);
1630 }
1631}
1632
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001633static int get_connection_details(char **host, int *port, int *type,
1634 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001635{
Jens Axboe62bc9372012-03-07 11:45:07 +01001636 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1637 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001638 char *typeentry;
1639
1640 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001641 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001642 GTK_DIALOG_DESTROY_WITH_PARENT,
1643 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1644 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1645
1646 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001647 /* gtk_dialog_get_content_area() is 2.14 and newer */
1648 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001649 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1650
1651 box = gtk_vbox_new(FALSE, 6);
1652 gtk_container_add(GTK_CONTAINER(frame), box);
1653
1654 hbox = gtk_hbox_new(TRUE, 10);
1655 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01001656 cw.hentry = gtk_entry_new();
1657 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1658 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001659
1660 frame = gtk_frame_new("Port");
1661 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1662 box = gtk_vbox_new(FALSE, 10);
1663 gtk_container_add(GTK_CONTAINER(frame), box);
1664
1665 hbox = gtk_hbox_new(TRUE, 4);
1666 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1667 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1668
1669 frame = gtk_frame_new("Type");
1670 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1671 box = gtk_vbox_new(FALSE, 10);
1672 gtk_container_add(GTK_CONTAINER(frame), box);
1673
1674 hbox = gtk_hbox_new(TRUE, 4);
1675 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1676
Jens Axboe62bc9372012-03-07 11:45:07 +01001677 cw.combo = gtk_combo_box_new_text();
1678 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1679 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1680 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1681 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001682
Jens Axboe62bc9372012-03-07 11:45:07 +01001683 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001684
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001685 frame = gtk_frame_new("Options");
1686 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1687 box = gtk_vbox_new(FALSE, 10);
1688 gtk_container_add(GTK_CONTAINER(frame), box);
1689
1690 hbox = gtk_hbox_new(TRUE, 4);
1691 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1692
Jens Axboe62bc9372012-03-07 11:45:07 +01001693 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1694 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1695 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.");
1696 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1697
1698 /*
1699 * Connect edit signal, so we can show/not-show the auto start button
1700 */
1701 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1702 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001703
Jens Axboea7a42ce2012-03-02 13:12:04 +01001704 gtk_widget_show_all(dialog);
1705
1706 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1707 gtk_widget_destroy(dialog);
1708 return 1;
1709 }
1710
Jens Axboe62bc9372012-03-07 11:45:07 +01001711 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001712 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1713
Jens Axboe62bc9372012-03-07 11:45:07 +01001714 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001715 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1716 *type = Fio_client_ipv4;
1717 else if (!strncmp(typeentry, "IPv6", 4))
1718 *type = Fio_client_ipv6;
1719 else
1720 *type = Fio_client_socket;
1721 g_free(typeentry);
1722
Jens Axboe62bc9372012-03-07 11:45:07 +01001723 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001724
Jens Axboea7a42ce2012-03-02 13:12:04 +01001725 gtk_widget_destroy(dialog);
1726 return 0;
1727}
1728
Jens Axboe2f99deb2012-03-09 14:37:29 +01001729static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01001730{
1731 struct gfio_client *gc;
1732
1733 gc = malloc(sizeof(*gc));
1734 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001735 gc->ge = ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001736 gc->client = client;
1737
Jens Axboe2f99deb2012-03-09 14:37:29 +01001738 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01001739
1740 client->client_data = gc;
1741}
1742
Jens Axboe2f99deb2012-03-09 14:37:29 +01001743static GtkWidget *new_client_page(struct gui_entry *ge);
1744
1745static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1746{
1747 struct gui_entry *ge;
1748
1749 ge = malloc(sizeof(*ge));
1750 memset(ge, 0, sizeof(*ge));
1751 INIT_FLIST_HEAD(&ge->list);
1752 flist_add_tail(&ge->list, &ui->list);
1753 ge->ui = ui;
1754 return ge;
1755}
1756
1757/*
1758 * FIXME: need more handling here
1759 */
1760static void ge_destroy(GtkWidget *w, gpointer data)
1761{
1762 struct gui_entry *ge = data;
1763
1764 flist_del(&ge->list);
1765 free(ge);
1766}
1767
1768static struct gui_entry *get_new_ge_with_tab(const char *name)
1769{
1770 struct gui_entry *ge;
1771
1772 ge = alloc_new_gui_entry(&main_ui);
1773
1774 ge->vbox = new_client_page(ge);
1775 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1776
1777 ge->page_label = gtk_label_new(name);
1778 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1779
1780 gtk_widget_show_all(main_ui.window);
1781 return ge;
1782}
1783
1784static void file_new(GtkWidget *w, gpointer data)
1785{
1786 get_new_ge_with_tab("Untitled");
1787}
1788
1789/*
1790 * Return the 'ge' corresponding to the tab. If the active tab is the
1791 * main tab, open a new tab.
1792 */
1793static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1794{
1795 struct flist_head *entry;
1796 struct gui_entry *ge;
1797
1798 if (!cur_page)
1799 return get_new_ge_with_tab("Untitled");
1800
1801 flist_for_each(entry, &main_ui.list) {
1802 ge = flist_entry(entry, struct gui_entry, list);
1803 if (ge->page_num == cur_page)
1804 return ge;
1805 }
1806
1807 return NULL;
1808}
1809
Jens Axboe0420ba62012-02-29 11:16:52 +01001810static void file_open(GtkWidget *w, gpointer data)
1811{
Jens Axboe63a130b2012-03-06 20:08:59 +01001812 struct gui *ui = data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001813 GtkWidget *dialog;
Jens Axboe0420ba62012-02-29 11:16:52 +01001814 GSList *filenames, *fn_glist;
1815 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001816 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001817 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001818 struct gui_entry *ge;
1819 gint cur_page;
1820
1821 /*
1822 * Creates new tab if current tab is the main window, or the
1823 * current tab already has a client.
1824 */
1825 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1826 ge = get_ge_from_page(cur_page);
1827 if (ge->client)
1828 ge = get_new_ge_with_tab("Untitled");
1829
1830 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01001831
1832 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01001833 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01001834 GTK_FILE_CHOOSER_ACTION_OPEN,
1835 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1836 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1837 NULL);
1838 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
1839
1840 filter = gtk_file_filter_new();
1841 gtk_file_filter_add_pattern(filter, "*.fio");
1842 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01001843 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe0420ba62012-02-29 11:16:52 +01001844 gtk_file_filter_add_mime_type(filter, "text/fio");
1845 gtk_file_filter_set_name(filter, "Fio job file");
1846 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
1847
1848 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1849 gtk_widget_destroy(dialog);
1850 return;
1851 }
1852
1853 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001854
1855 gtk_widget_destroy(dialog);
1856
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001857 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01001858 goto err;
1859
Jens Axboe0420ba62012-02-29 11:16:52 +01001860 filenames = fn_glist;
1861 while (filenames != NULL) {
Jens Axboee0681f32012-03-06 12:14:42 +01001862 struct fio_client *client;
1863
Jens Axboe2f99deb2012-03-09 14:37:29 +01001864 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
1865 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
1866 ge->nr_job_files++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001867
Jens Axboee0681f32012-03-06 12:14:42 +01001868 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
1869 if (!client) {
Jens Axboedf06f222012-03-02 13:32:04 +01001870 GError *error;
1871
1872 error = g_error_new(g_quark_from_string("fio"), 1,
1873 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01001874 report_error(error);
1875 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01001876 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001877 gfio_client_added(ge, client);
Jens Axboe0420ba62012-02-29 11:16:52 +01001878
1879 g_free(filenames->data);
1880 filenames = g_slist_next(filenames);
1881 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01001882 free(host);
Jens Axboe63a130b2012-03-06 20:08:59 +01001883
1884 if (server_start)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001885 gfio_start_server();
Jens Axboea7a42ce2012-03-02 13:12:04 +01001886err:
Jens Axboe0420ba62012-02-29 11:16:52 +01001887 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01001888}
1889
1890static void file_save(GtkWidget *w, gpointer data)
1891{
Jens Axboe63a130b2012-03-06 20:08:59 +01001892 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01001893 GtkWidget *dialog;
1894
1895 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01001896 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01001897 GTK_FILE_CHOOSER_ACTION_SAVE,
1898 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1899 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1900 NULL);
1901
1902 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
1903 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
1904
1905 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
1906 char *filename;
1907
1908 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1909 // save_job_file(filename);
1910 g_free(filename);
1911 }
1912 gtk_widget_destroy(dialog);
1913}
1914
Jens Axboe9b260bd2012-03-06 11:02:52 +01001915static void view_log_destroy(GtkWidget *w, gpointer data)
1916{
1917 struct gui *ui = (struct gui *) data;
1918
1919 gtk_widget_ref(ui->log_tree);
1920 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
1921 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01001922 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001923}
1924
1925static void view_log(GtkWidget *w, gpointer data)
1926{
Jens Axboe4cbe7212012-03-06 13:36:17 +01001927 GtkWidget *win, *scroll, *vbox, *box;
1928 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001929
Jens Axboe4cbe7212012-03-06 13:36:17 +01001930 if (ui->log_view)
1931 return;
1932
1933 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001934 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01001935 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001936
Jens Axboe4cbe7212012-03-06 13:36:17 +01001937 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001938
Jens Axboe4cbe7212012-03-06 13:36:17 +01001939 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1940
1941 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1942
1943 box = gtk_hbox_new(TRUE, 0);
1944 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
1945 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
1946 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
1947
1948 vbox = gtk_vbox_new(TRUE, 5);
1949 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
1950
1951 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001952 gtk_widget_show_all(win);
1953}
1954
Jens Axboe46974a72012-03-02 19:34:13 +01001955static void preferences(GtkWidget *w, gpointer data)
1956{
Jens Axboef3e84402012-03-07 13:14:32 +01001957 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe46974a72012-03-02 19:34:13 +01001958 int i;
1959
1960 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001961 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01001962 GTK_DIALOG_DESTROY_WITH_PARENT,
1963 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1964 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1965 NULL);
1966
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001967 frame = gtk_frame_new("Debug logging");
Jens Axboe46974a72012-03-02 19:34:13 +01001968 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01001969
1970 vbox = gtk_vbox_new(FALSE, 6);
1971 gtk_container_add(GTK_CONTAINER(frame), vbox);
1972
Jens Axboe46974a72012-03-02 19:34:13 +01001973 box = gtk_hbox_new(FALSE, 6);
Jens Axboef3e84402012-03-07 13:14:32 +01001974 gtk_container_add(GTK_CONTAINER(vbox), box);
Jens Axboe46974a72012-03-02 19:34:13 +01001975
1976 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
1977
1978 for (i = 0; i < FD_DEBUG_MAX; i++) {
Jens Axboef3e84402012-03-07 13:14:32 +01001979 if (i == 7) {
1980 box = gtk_hbox_new(FALSE, 6);
1981 gtk_container_add(GTK_CONTAINER(vbox), box);
1982 }
1983
1984
Jens Axboe46974a72012-03-02 19:34:13 +01001985 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
Jens Axboe0b8d11e2012-03-02 19:44:15 +01001986 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
Jens Axboe46974a72012-03-02 19:34:13 +01001987 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
1988 }
1989
Jens Axboef3e84402012-03-07 13:14:32 +01001990 frame = gtk_frame_new("Graph font");
1991 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
1992 vbox = gtk_vbox_new(FALSE, 6);
1993 gtk_container_add(GTK_CONTAINER(frame), vbox);
1994
1995 font = gtk_font_button_new();
1996 gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
1997
Jens Axboe46974a72012-03-02 19:34:13 +01001998 gtk_widget_show_all(dialog);
1999
2000 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2001 gtk_widget_destroy(dialog);
2002 return;
2003 }
2004
2005 for (i = 0; i < FD_DEBUG_MAX; i++) {
2006 int set;
2007
2008 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2009 if (set)
2010 fio_debug |= (1UL << i);
2011 }
2012
Jens Axboef3e84402012-03-07 13:14:32 +01002013 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe46974a72012-03-02 19:34:13 +01002014 gtk_widget_destroy(dialog);
2015}
2016
Jens Axboe0420ba62012-02-29 11:16:52 +01002017static void about_dialog(GtkWidget *w, gpointer data)
2018{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002019 const char *authors[] = {
2020 "Jens Axboe <axboe@kernel.dk>",
2021 "Stephen Carmeron <stephenmcameron@gmail.com>",
2022 NULL
2023 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002024 const char *license[] = {
2025 "Fio is free software; you can redistribute it and/or modify "
2026 "it under the terms of the GNU General Public License as published by "
2027 "the Free Software Foundation; either version 2 of the License, or "
2028 "(at your option) any later version.\n",
2029 "Fio is distributed in the hope that it will be useful, "
2030 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2031 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2032 "GNU General Public License for more details.\n",
2033 "You should have received a copy of the GNU General Public License "
2034 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2035 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2036 };
2037 char *license_trans;
2038
2039 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2040 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002041
Jens Axboe0420ba62012-02-29 11:16:52 +01002042 gtk_show_about_dialog(NULL,
2043 "program-name", "gfio",
2044 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002045 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002046 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2047 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002048 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002049 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002050 "logo-icon-name", "fio",
2051 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002052 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002053 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002054
Jens Axboe2f99deb2012-03-09 14:37:29 +01002055 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002056}
2057
2058static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002059 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002060 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002061 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002062 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe46974a72012-03-02 19:34:13 +01002063 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2064 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2065 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002066 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe46974a72012-03-02 19:34:13 +01002067 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2068 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002069};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002070static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002071
2072static const gchar *ui_string = " \
2073 <ui> \
2074 <menubar name=\"MainMenu\"> \
2075 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002076 <menuitem name=\"New\" action=\"NewFile\" /> \
2077 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002078 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2079 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002080 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002081 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2082 <separator name=\"Separator3\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002083 <menuitem name=\"Quit\" action=\"Quit\" /> \
2084 </menu> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002085 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2086 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2087 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002088 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2089 <menuitem name=\"About\" action=\"About\" /> \
2090 </menu> \
2091 </menubar> \
2092 </ui> \
2093";
2094
Jens Axboe4cbe7212012-03-06 13:36:17 +01002095static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2096 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002097{
2098 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2099 GError *error = 0;
2100
2101 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002102 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002103
2104 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2105 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2106
2107 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
2108 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2109}
2110
2111void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2112 GtkWidget *vbox, GtkUIManager *ui_manager)
2113{
2114 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2115}
2116
Jens Axboe2f99deb2012-03-09 14:37:29 +01002117static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002118{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002119 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002120 GdkColor white;
Jens Axboe0420ba62012-02-29 11:16:52 +01002121
Jens Axboe2f99deb2012-03-09 14:37:29 +01002122 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002123
Jens Axboe2f99deb2012-03-09 14:37:29 +01002124 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2125 ge->topvbox = gtk_vbox_new(FALSE, 3);
2126 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2127 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002128
Jens Axboe3e47bd22012-02-29 13:45:02 +01002129 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002130 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002131 probe_frame = gtk_vbox_new(FALSE, 3);
2132 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2133
2134 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002135 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2136 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2137 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2138 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2139 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002140
Jens Axboe3e47bd22012-02-29 13:45:02 +01002141 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
2144 ge->eta.name = new_info_entry_in_frame(probe_box, "Name");
2145 ge->eta.iotype = new_info_entry_in_frame(probe_box, "IO");
2146 ge->eta.ioengine = new_info_entry_in_frame(probe_box, "IO Engine");
2147 ge->eta.iodepth = new_info_entry_in_frame(probe_box, "IO Depth");
2148 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2149 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2150
2151 probe_box = gtk_hbox_new(FALSE, 3);
2152 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2153 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2154 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2155 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2156 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2157
2158 /*
2159 * Only add this if we have a commit rate
2160 */
2161#if 0
2162 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002163 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002164
Jens Axboe2f99deb2012-03-09 14:37:29 +01002165 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2166 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2167
2168 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2169 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2170#endif
2171
2172 /*
2173 * Set up a drawing area and IOPS and bandwidth graphs
2174 */
2175 gdk_color_parse("white", &white);
2176 ge->graphs.drawing_area = gtk_drawing_area_new();
2177 ge->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2178 ge->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2179 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
2180 ge->graphs.drawing_area_xdim, ge->graphs.drawing_area_ydim);
2181 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2182 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2183 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2184 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2185 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2186 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2187 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2188 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2189 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2190 ge->graphs.drawing_area);
2191 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2192 TRUE, TRUE, 0);
2193
2194 setup_graphs(&ge->graphs);
2195
2196 /*
2197 * Set up alignments for widgets at the bottom of ui,
2198 * align bottom left, expand horizontally but not vertically
2199 */
2200 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2201 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2202 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2203 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2204 FALSE, FALSE, 0);
2205
2206 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2207
2208 /*
2209 * Set up thread status progress bar
2210 */
2211 ge->thread_status_pb = gtk_progress_bar_new();
2212 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2213 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2214 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2215
2216
2217 return main_vbox;
2218}
2219
2220static GtkWidget *new_main_page(struct gui *ui)
2221{
2222 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2223 GdkColor white;
2224
2225 main_vbox = gtk_vbox_new(FALSE, 3);
2226
2227 /*
2228 * Set up alignments for widgets at the top of ui,
2229 * align top left, expand horizontally but not vertically
2230 */
2231 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2232 ui->topvbox = gtk_vbox_new(FALSE, 0);
2233 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2234 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2235
2236 probe = gtk_frame_new("Run statistics");
2237 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2238 probe_frame = gtk_vbox_new(FALSE, 3);
2239 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002240
2241 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002242 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboeca850992012-03-05 20:04:43 +01002243 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2244 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2245 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2246 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002247
2248 /*
2249 * Only add this if we have a commit rate
2250 */
2251#if 0
2252 probe_box = gtk_hbox_new(FALSE, 3);
2253 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2254
Jens Axboe3e47bd22012-02-29 13:45:02 +01002255 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2256 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2257
Jens Axboe3e47bd22012-02-29 13:45:02 +01002258 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2259 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002260#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002261
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002262 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002263 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002264 */
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002265 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002266 ui->graphs.drawing_area = gtk_drawing_area_new();
2267 ui->graphs.drawing_area_xdim = DRAWING_AREA_XDIM;
2268 ui->graphs.drawing_area_ydim = DRAWING_AREA_YDIM;
2269 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
2270 ui->graphs.drawing_area_xdim, ui->graphs.drawing_area_ydim);
2271 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2272 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2273 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2274 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2275 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002276 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2277 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2278 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002279 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002280 ui->graphs.drawing_area);
2281 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002282 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002283
Jens Axboe2f99deb2012-03-09 14:37:29 +01002284 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002285
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002286 /*
2287 * Set up alignments for widgets at the bottom of ui,
2288 * align bottom left, expand horizontally but not vertically
2289 */
2290 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2291 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2292 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002293 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002294 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002295
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002296 /*
2297 * Set up thread status progress bar
2298 */
2299 ui->thread_status_pb = gtk_progress_bar_new();
2300 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002301 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002302 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2303
Jens Axboe2f99deb2012-03-09 14:37:29 +01002304 return main_vbox;
2305}
2306
2307static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2308 guint page, gpointer data)
2309
2310{
2311 return TRUE;
2312}
2313
2314static void init_ui(int *argc, char **argv[], struct gui *ui)
2315{
2316 GtkSettings *settings;
2317 GtkUIManager *uimanager;
2318 GtkWidget *menu, *vbox;
2319
2320 /* Magical g*thread incantation, you just need this thread stuff.
2321 * Without it, the update that happens in gfio_update_thread_status
2322 * doesn't really happen in a timely fashion, you need expose events
2323 */
2324 if (!g_thread_supported())
2325 g_thread_init(NULL);
2326 gdk_threads_init();
2327
2328 gtk_init(argc, argv);
2329 settings = gtk_settings_get_default();
2330 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2331 g_type_init();
2332
2333 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2334 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2335 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2336
2337 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2338 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2339
2340 ui->vbox = gtk_vbox_new(FALSE, 0);
2341 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2342
2343 uimanager = gtk_ui_manager_new();
2344 menu = get_menubar_menu(ui->window, uimanager, ui);
2345 gfio_ui_setup(settings, menu, ui->vbox, uimanager);
2346
2347 ui->notebook = gtk_notebook_new();
2348 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
2349 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2350
2351 vbox = new_main_page(ui);
2352
2353 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2354
Jens Axboe9b260bd2012-03-06 11:02:52 +01002355 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002356
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002357 gtk_widget_show_all(ui->window);
2358}
2359
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002360int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002361{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002362 if (initialize_fio(envp))
2363 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01002364 if (fio_init_options())
2365 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01002366
Jens Axboe2f99deb2012-03-09 14:37:29 +01002367 memset(&main_ui, 0, sizeof(main_ui));
2368 INIT_FLIST_HEAD(&main_ui.list);
2369
2370 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01002371
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002372 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002373 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002374 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002375 return 0;
2376}