blob: 14ec1d5203489a1643b2d3f0ed9463d38d751555 [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 Axboe38634cb2012-03-13 12:26:41 +010035#define GFIO_MIME "text/fio"
36
Jens Axboe63a130b2012-03-06 20:08:59 +010037static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010038static const char *gfio_graph_font;
Jens Axboe8577f4f2012-03-09 19:28:27 +010039static unsigned int gfio_graph_limit = 100;
Stephen M. Cameron814479d2012-03-15 07:58:14 +010040static GdkColor white;
Jens Axboe63a130b2012-03-06 20:08:59 +010041
Jens Axboe6b79c802012-03-08 10:51:36 +010042static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010043
Stephen M. Cameronf3074002012-02-24 08:17:30 +010044#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
45
46typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
47
Jens Axboe3e47bd22012-02-29 13:45:02 +010048static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010049static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010050static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010051
52static struct button_spec {
53 const char *buttontext;
54 clickfunction f;
Jens Axboe014f4022012-03-15 14:03:01 +010055 const char *tooltiptext[2];
56 const int start_sensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010057} buttonspeclist[] = {
Jens Axboe3e47bd22012-02-29 13:45:02 +010058#define CONNECT_BUTTON 0
Jens Axboeb9d2f302012-03-08 20:36:28 +010059#define SEND_BUTTON 1
60#define START_JOB_BUTTON 2
Jens Axboe014f4022012-03-15 14:03:01 +010061 { "Connect", connect_clicked, { "Disconnect from host", "Connect to host" }, 1 },
62 { "Send", send_clicked, { "Send job description to host", NULL }, 0 },
Jens Axboeb9d2f302012-03-08 20:36:28 +010063 { "Start Job", start_job_clicked,
Jens Axboe014f4022012-03-15 14:03:01 +010064 { "Start the current job on the server", NULL }, 0 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010065};
66
Jens Axboe843ad232012-02-29 11:44:53 +010067struct probe_widget {
68 GtkWidget *hostname;
69 GtkWidget *os;
70 GtkWidget *arch;
71 GtkWidget *fio_ver;
72};
73
Jens Axboec80b74b2012-03-12 10:23:28 +010074struct multitext_widget {
75 GtkWidget *entry;
76 char **text;
77 unsigned int cur_text;
78 unsigned int max_text;
79};
80
Jens Axboe3e47bd22012-02-29 13:45:02 +010081struct eta_widget {
Jens Axboe3863d1a2012-03-09 17:39:05 +010082 GtkWidget *names;
Jens Axboec80b74b2012-03-12 10:23:28 +010083 struct multitext_widget iotype;
Jens Axboe99d633a2012-03-15 15:55:04 +010084 struct multitext_widget bs;
Jens Axboec80b74b2012-03-12 10:23:28 +010085 struct multitext_widget ioengine;
86 struct multitext_widget iodepth;
Jens Axboe3e47bd22012-02-29 13:45:02 +010087 GtkWidget *jobs;
88 GtkWidget *files;
89 GtkWidget *read_bw;
90 GtkWidget *read_iops;
91 GtkWidget *cr_bw;
92 GtkWidget *cr_iops;
93 GtkWidget *write_bw;
94 GtkWidget *write_iops;
95 GtkWidget *cw_bw;
96 GtkWidget *cw_iops;
97};
98
Jens Axboe2f99deb2012-03-09 14:37:29 +010099struct gfio_graphs {
100#define DRAWING_AREA_XDIM 1000
101#define DRAWING_AREA_YDIM 400
102 GtkWidget *drawing_area;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100103 struct graph *iops_graph;
104 struct graph *bandwidth_graph;
105};
106
107/*
108 * Main window widgets and data
109 */
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100110struct gui {
Jens Axboe02421e62012-03-12 12:05:50 +0100111 GtkUIManager *uimanager;
Jens Axboe38634cb2012-03-13 12:26:41 +0100112 GtkRecentManager *recentmanager;
113 GtkActionGroup *actiongroup;
114 guint recent_ui_id;
Jens Axboe02421e62012-03-12 12:05:50 +0100115 GtkWidget *menu;
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100116 GtkWidget *window;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100117 GtkWidget *vbox;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100118 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100119 GtkWidget *buttonbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100120 GtkWidget *notebook;
121 GtkWidget *error_info_bar;
122 GtkWidget *error_label;
123 GtkListStore *log_model;
124 GtkWidget *log_tree;
125 GtkWidget *log_view;
126 struct gfio_graphs graphs;
127 struct probe_widget probe;
128 struct eta_widget eta;
129 pthread_t server_t;
130
Jens Axboea9eccde2012-03-09 14:59:42 +0100131 pthread_t t;
132 int handler_running;
133
Jens Axboe2f99deb2012-03-09 14:37:29 +0100134 struct flist_head list;
135} main_ui;
136
Jens Axboe85dd01e2012-03-12 14:33:16 +0100137enum {
138 GE_STATE_NEW = 1,
139 GE_STATE_CONNECTED,
140 GE_STATE_JOB_SENT,
141 GE_STATE_JOB_STARTED,
142 GE_STATE_JOB_RUNNING,
143 GE_STATE_JOB_DONE,
144};
145
Jens Axboe2f99deb2012-03-09 14:37:29 +0100146/*
147 * Notebook entry
148 */
149struct gui_entry {
150 struct flist_head list;
151 struct gui *ui;
152
153 GtkWidget *vbox;
Jens Axboec80b74b2012-03-12 10:23:28 +0100154 GtkWidget *job_notebook;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100155 GtkWidget *thread_status_pb;
156 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100157 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Jens Axboe2f99deb2012-03-09 14:37:29 +0100158 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100159 GtkWidget *error_info_bar;
160 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100161 GtkWidget *results_window;
Jens Axboe7da23b42012-03-15 13:45:02 +0100162 GtkWidget *results_notebook;
Jens Axboe17b97212012-03-14 20:17:57 +0100163 GtkUIManager *results_uimanager;
Jens Axboe17b97212012-03-14 20:17:57 +0100164 GtkWidget *results_menu;
Jens Axboe7da23b42012-03-15 13:45:02 +0100165 GtkWidget *disk_util_vbox;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100166 GtkListStore *log_model;
167 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100168 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100169 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100170 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100171 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100172 GtkWidget *page_label;
173 gint page_num;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100174 unsigned int state;
Jens Axboe0420ba62012-02-29 11:16:52 +0100175
Jens Axboe09d574e2012-03-15 10:45:48 +0100176 struct graph *clat_graph;
177 struct graph *lat_bucket_graph;
178
Jens Axboeb9d2f302012-03-08 20:36:28 +0100179 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100180 int nr_job_files;
181 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100182};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100183
Jens Axboe781ccba2012-03-15 09:44:42 +0100184struct end_results {
185 struct group_run_stats gs;
186 struct thread_stat ts;
187};
188
Jens Axboee0681f32012-03-06 12:14:42 +0100189struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100190 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100191 struct fio_client *client;
Jens Axboe6b79c802012-03-08 10:51:36 +0100192 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100193 struct thread_options o;
Jens Axboe781ccba2012-03-15 09:44:42 +0100194
195 struct end_results *results;
196 unsigned int nr_results;
Jens Axboe7da23b42012-03-15 13:45:02 +0100197
198 struct cmd_du_pdu *du;
199 unsigned int nr_du;
Jens Axboee0681f32012-03-06 12:14:42 +0100200};
201
Jens Axboe9988ca72012-03-09 15:14:06 +0100202static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
203static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +0100204void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +0100205
Jens Axboe2f99deb2012-03-09 14:37:29 +0100206static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100207{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100208 struct graph *g;
209
210 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100211 graph_title(g, "IOPS (IOs/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100212 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100213 graph_add_label(g, "Read IOPS");
214 graph_add_label(g, "Write IOPS");
215 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
216 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +0100217 line_graph_set_data_count_limit(g, gfio_graph_limit);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100218 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100219 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100220}
221
Jens Axboe2f99deb2012-03-09 14:37:29 +0100222static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100223{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100224 struct graph *g;
225
226 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100227 graph_title(g, "Bandwidth (bytes/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100228 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100229 graph_add_label(g, "Read Bandwidth");
230 graph_add_label(g, "Write Bandwidth");
231 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
232 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100233 graph_set_base_offset(g, 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100234 line_graph_set_data_count_limit(g, 100);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100235 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100236 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100237}
238
Jens Axboe2f99deb2012-03-09 14:37:29 +0100239static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100240{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100241 g->iops_graph = setup_iops_graph();
242 g->bandwidth_graph = setup_bandwidth_graph();
243}
244
Jens Axboec80b74b2012-03-12 10:23:28 +0100245static void multitext_add_entry(struct multitext_widget *mt, const char *text)
246{
247 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
248 mt->text[mt->max_text] = strdup(text);
249 mt->max_text++;
250}
251
252static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
253{
254 if (index >= mt->max_text)
255 return;
Jens Axboeda185432012-03-12 11:05:46 +0100256 if (!mt->text || !mt->text[index])
Jens Axboec80b74b2012-03-12 10:23:28 +0100257 return;
258
259 mt->cur_text = index;
260 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
261}
262
263static void multitext_update_entry(struct multitext_widget *mt,
264 unsigned int index, const char *text)
265{
Jens Axboeda185432012-03-12 11:05:46 +0100266 if (!mt->text)
267 return;
268
Jens Axboec80b74b2012-03-12 10:23:28 +0100269 if (mt->text[index])
270 free(mt->text[index]);
271
272 mt->text[index] = strdup(text);
273 if (mt->cur_text == index)
274 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
275}
276
277static void multitext_free(struct multitext_widget *mt)
278{
279 int i;
280
281 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
282
283 for (i = 0; i < mt->max_text; i++) {
284 if (mt->text[i])
285 free(mt->text[i]);
286 }
287
288 free(mt->text);
289 mt->cur_text = -1;
290 mt->max_text = 0;
291}
292
Jens Axboe2f99deb2012-03-09 14:37:29 +0100293static void clear_ge_ui_info(struct gui_entry *ge)
294{
295 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
296 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
297 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
298 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100299#if 0
300 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100301 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100302#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100303 multitext_update_entry(&ge->eta.iotype, 0, "");
Jens Axboe99d633a2012-03-15 15:55:04 +0100304 multitext_update_entry(&ge->eta.bs, 0, "");
Jens Axboec80b74b2012-03-12 10:23:28 +0100305 multitext_update_entry(&ge->eta.ioengine, 0, "");
306 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100307 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
309 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
310 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
311 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
312 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100313}
314
Jens Axboe3863d1a2012-03-09 17:39:05 +0100315static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
316{
317 GtkWidget *entry, *frame;
318
319 frame = gtk_frame_new(label);
320 entry = gtk_combo_box_new_text();
321 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
322 gtk_container_add(GTK_CONTAINER(frame), entry);
323
324 return entry;
325}
326
Jens Axboe3650a3c2012-03-05 14:09:03 +0100327static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
328{
329 GtkWidget *entry, *frame;
330
331 frame = gtk_frame_new(label);
332 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100333 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100334 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
335 gtk_container_add(GTK_CONTAINER(frame), entry);
336
337 return entry;
338}
339
340static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
341{
342 GtkWidget *label_widget;
343 GtkWidget *frame;
344
345 frame = gtk_frame_new(label);
346 label_widget = gtk_label_new(NULL);
347 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
348 gtk_container_add(GTK_CONTAINER(frame), label_widget);
349
350 return label_widget;
351}
352
353static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
354{
355 GtkWidget *button, *box;
356
357 box = gtk_hbox_new(FALSE, 3);
358 gtk_container_add(GTK_CONTAINER(hbox), box);
359
360 button = gtk_spin_button_new_with_range(min, max, 1.0);
361 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
362
363 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
364 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
365
366 return button;
367}
368
Jens Axboe3650a3c2012-03-05 14:09:03 +0100369static void label_set_int_value(GtkWidget *entry, unsigned int val)
370{
371 char tmp[80];
372
373 sprintf(tmp, "%u", val);
374 gtk_label_set_text(GTK_LABEL(entry), tmp);
375}
376
377static void entry_set_int_value(GtkWidget *entry, unsigned int val)
378{
379 char tmp[80];
380
381 sprintf(tmp, "%u", val);
382 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
383}
384
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100385static void show_info_dialog(struct gui *ui, const char *title,
386 const char *message)
387{
388 GtkWidget *dialog, *content, *label;
389
390 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
391 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
392 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
393
394 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
395 label = gtk_label_new(message);
396 gtk_container_add(GTK_CONTAINER(content), label);
397 gtk_widget_show_all(dialog);
398 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
399 gtk_dialog_run(GTK_DIALOG(dialog));
400 gtk_widget_destroy(dialog);
401}
402
Jens Axboe781ccba2012-03-15 09:44:42 +0100403static void set_menu_entry_text(struct gui *ui, const char *path,
404 const char *text)
405{
406 GtkWidget *w;
407
408 w = gtk_ui_manager_get_widget(ui->uimanager, path);
409 if (w)
410 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
411 else
412 fprintf(stderr, "gfio: can't find path %s\n", path);
413}
414
415
416static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
417{
418 GtkWidget *w;
419
420 w = gtk_ui_manager_get_widget(ui->uimanager, path);
421 if (w)
422 gtk_widget_set_sensitive(w, show);
423 else
424 fprintf(stderr, "gfio: can't find path %s\n", path);
425}
426
427static void set_job_menu_visible(struct gui *ui, int visible)
428{
429 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
430}
431
432static void set_view_results_visible(struct gui *ui, int visible)
433{
434 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
435}
436
Jens Axboe014f4022012-03-15 14:03:01 +0100437static const char *get_button_tooltip(struct button_spec *s, int sensitive)
438{
439 if (s->tooltiptext[sensitive])
440 return s->tooltiptext[sensitive];
441
442 return s->tooltiptext[0];
443}
444
445static GtkWidget *add_button(GtkWidget *buttonbox,
446 struct button_spec *buttonspec, gpointer data)
447{
448 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
449 gboolean sens = buttonspec->start_sensitive;
450
451 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
452 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
453
454 sens = buttonspec->start_sensitive;
455 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
456 gtk_widget_set_sensitive(button, sens);
457
458 return button;
459}
460
461static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
462 int nbuttons)
463{
464 int i;
465
466 for (i = 0; i < nbuttons; i++)
467 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
468}
469
Jens Axboe85dd01e2012-03-12 14:33:16 +0100470/*
471 * Update sensitivity of job buttons and job menu items, based on the
472 * state of the client.
473 */
474static void update_button_states(struct gui *ui, struct gui_entry *ge)
475{
476 unsigned int connect_state, send_state, start_state, edit_state;
477 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100478
479 switch (ge->state) {
480 default: {
481 char tmp[80];
482
483 sprintf(tmp, "Bad client state: %u\n", ge->state);
484 show_info_dialog(ui, "Error", tmp);
485 /* fall through to new state */
486 }
487
488 case GE_STATE_NEW:
489 connect_state = 1;
490 edit_state = 0;
491 connect_str = "Connect";
492 send_state = 0;
493 start_state = 0;
494 break;
495 case GE_STATE_CONNECTED:
496 connect_state = 1;
497 edit_state = 0;
498 connect_str = "Disconnect";
499 send_state = 1;
500 start_state = 0;
501 break;
502 case GE_STATE_JOB_SENT:
503 connect_state = 1;
504 edit_state = 0;
505 connect_str = "Disconnect";
506 send_state = 0;
507 start_state = 1;
508 break;
509 case GE_STATE_JOB_STARTED:
510 connect_state = 1;
511 edit_state = 1;
512 connect_str = "Disconnect";
513 send_state = 0;
514 start_state = 1;
515 break;
516 case GE_STATE_JOB_RUNNING:
517 connect_state = 1;
518 edit_state = 0;
519 connect_str = "Disconnect";
520 send_state = 0;
521 start_state = 0;
522 break;
523 case GE_STATE_JOB_DONE:
524 connect_state = 1;
525 edit_state = 0;
526 connect_str = "Connect";
527 send_state = 0;
528 start_state = 0;
529 break;
530 }
531
532 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
533 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
534 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
535 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
Jens Axboe014f4022012-03-15 14:03:01 +0100536 gtk_widget_set_tooltip_text(ge->button[CONNECT_BUTTON], get_button_tooltip(&buttonspeclist[CONNECT_BUTTON], connect_state));
Jens Axboe85dd01e2012-03-12 14:33:16 +0100537
Jens Axboe781ccba2012-03-15 09:44:42 +0100538 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
539 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100540
Jens Axboe781ccba2012-03-15 09:44:42 +0100541 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
542 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
543 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100544
Jens Axboe781ccba2012-03-15 09:44:42 +0100545 if (ge->client && ge->client->nr_results)
546 set_view_results_visible(ui, 1);
547 else
548 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100549}
550
551static void gfio_set_state(struct gui_entry *ge, unsigned int state)
552{
553 ge->state = state;
554 update_button_states(ge->ui, ge);
555}
556
Jens Axboea2697902012-03-05 16:43:49 +0100557#define ALIGN_LEFT 1
558#define ALIGN_RIGHT 2
559#define INVISIBLE 4
560#define UNSORTABLE 8
561
562GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
563{
564 GtkCellRenderer *renderer;
565 GtkTreeViewColumn *col;
566 double xalign = 0.0; /* left as default */
567 PangoAlignment align;
568 gboolean visible;
569
570 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
571 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
572 PANGO_ALIGN_CENTER;
573 visible = !(flags & INVISIBLE);
574
575 renderer = gtk_cell_renderer_text_new();
576 col = gtk_tree_view_column_new();
577
578 gtk_tree_view_column_set_title(col, title);
579 if (!(flags & UNSORTABLE))
580 gtk_tree_view_column_set_sort_column_id(col, index);
581 gtk_tree_view_column_set_resizable(col, TRUE);
582 gtk_tree_view_column_pack_start(col, renderer, TRUE);
583 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
584 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
585 switch (align) {
586 case PANGO_ALIGN_LEFT:
587 xalign = 0.0;
588 break;
589 case PANGO_ALIGN_CENTER:
590 xalign = 0.5;
591 break;
592 case PANGO_ALIGN_RIGHT:
593 xalign = 1.0;
594 break;
595 }
596 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
597 gtk_tree_view_column_set_visible(col, visible);
598 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
599 return col;
600}
601
Jens Axboe9b260bd2012-03-06 11:02:52 +0100602static void gfio_ui_setup_log(struct gui *ui)
603{
604 GtkTreeSelection *selection;
605 GtkListStore *model;
606 GtkWidget *tree_view;
607
608 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
609
610 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
611 gtk_widget_set_can_focus(tree_view, FALSE);
612
613 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
614 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100615 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
616 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100617
618 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
619 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
620 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100621 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100622
623 ui->log_model = model;
624 ui->log_tree = tree_view;
625}
626
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100627static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
628 fio_fp64_t *plist,
629 unsigned int len,
630 double xdim, double ydim)
631{
632 struct graph *g;
633 int i;
634
635 g = graph_new(xdim, ydim, gfio_graph_font);
636 graph_title(g, title);
637 graph_x_title(g, "Percentile");
638
639 for (i = 0; i < len; i++) {
640 char fbuf[8];
641
642 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
643 graph_add_label(g, fbuf);
644 graph_add_data(g, fbuf, (double) ovals[i]);
645 }
646
647 return g;
648}
649
Jens Axboea2697902012-03-05 16:43:49 +0100650static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
651 fio_fp64_t *plist,
652 unsigned int len,
653 const char *base,
654 unsigned int scale)
655{
656 GType types[FIO_IO_U_LIST_MAX_LEN];
657 GtkWidget *tree_view;
658 GtkTreeSelection *selection;
659 GtkListStore *model;
660 GtkTreeIter iter;
661 int i;
662
663 for (i = 0; i < len; i++)
664 types[i] = G_TYPE_INT;
665
666 model = gtk_list_store_newv(len, types);
667
668 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
669 gtk_widget_set_can_focus(tree_view, FALSE);
670
Jens Axboe661f7412012-03-06 13:55:45 +0100671 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
672 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
673
Jens Axboea2697902012-03-05 16:43:49 +0100674 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
675 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
676
677 for (i = 0; i < len; i++) {
678 char fbuf[8];
679
680 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
681 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
682 }
683
684 gtk_list_store_append(model, &iter);
685
Jens Axboee0681f32012-03-06 12:14:42 +0100686 for (i = 0; i < len; i++) {
687 if (scale)
688 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100689 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100690 }
Jens Axboea2697902012-03-05 16:43:49 +0100691
692 return tree_view;
693}
694
Jens Axboe09d574e2012-03-15 10:45:48 +0100695static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100696{
697 struct graph *g = p;
698 cairo_t *cr;
699
700 cr = gdk_cairo_create(w->window);
701#if 0
702 if (graph_has_tooltips(g)) {
703 g_object_set(w, "has-tooltip", TRUE, NULL);
704 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
705 }
706#endif
707 cairo_set_source_rgb(cr, 0, 0, 0);
708 bar_graph_draw(g, cr);
709 cairo_destroy(cr);
710
711 return FALSE;
712}
713
Jens Axboe09d574e2012-03-15 10:45:48 +0100714static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
715 gpointer data)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100716{
717 struct graph *g = data;
718
719 graph_set_size(g, w->allocation.width, w->allocation.height);
720 graph_set_size(g, w->allocation.width, w->allocation.height);
721 graph_set_position(g, 0, 0);
722 return TRUE;
723}
724
725static void gfio_show_clat_percentiles(struct gfio_client *gc,
726 GtkWidget *vbox, struct thread_stat *ts,
Jens Axboea2697902012-03-05 16:43:49 +0100727 int ddir)
728{
729 unsigned int *io_u_plat = ts->io_u_plat[ddir];
730 unsigned long nr = ts->clat_stat[ddir].samples;
731 fio_fp64_t *plist = ts->percentile_list;
732 unsigned int *ovals, len, minv, maxv, scale_down;
733 const char *base;
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100734 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
Jens Axboe09d574e2012-03-15 10:45:48 +0100735 struct gui_entry *ge = gc->ge;
Jens Axboea2697902012-03-05 16:43:49 +0100736 char tmp[64];
737
738 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
739 if (!len)
740 goto out;
741
742 /*
743 * We default to usecs, but if the value range is such that we
744 * should scale down to msecs, do that.
745 */
746 if (minv > 2000 && maxv > 99999) {
747 scale_down = 1;
748 base = "msec";
749 } else {
750 scale_down = 0;
751 base = "usec";
752 }
753
Jens Axboea2697902012-03-05 16:43:49 +0100754 sprintf(tmp, "Completion percentiles (%s)", base);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100755 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
Jens Axboe09d574e2012-03-15 10:45:48 +0100756 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100757
Jens Axboea2697902012-03-05 16:43:49 +0100758 frame = gtk_frame_new(tmp);
759 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
760
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100761 completion_vbox = gtk_vbox_new(FALSE, 3);
762 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
Jens Axboea2697902012-03-05 16:43:49 +0100763 hbox = gtk_hbox_new(FALSE, 3);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100764 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
765 drawing_area = gtk_drawing_area_new();
766 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
767 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
768 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
Jens Axboe09d574e2012-03-15 10:45:48 +0100769 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
770 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
Jens Axboea2697902012-03-05 16:43:49 +0100771
772 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
773out:
774 if (ovals)
775 free(ovals);
776}
777
Jens Axboe3650a3c2012-03-05 14:09:03 +0100778static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
779 unsigned long max, double mean, double dev)
780{
781 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100782 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100783 char *minp, *maxp;
784 char tmp[64];
785
786 if (!usec_to_msec(&min, &max, &mean, &dev))
787 base = "(msec)";
788
789 minp = num2str(min, 6, 1, 0);
790 maxp = num2str(max, 6, 1, 0);
791
Jens Axboe3650a3c2012-03-05 14:09:03 +0100792 sprintf(tmp, "%s %s", name, base);
793 frame = gtk_frame_new(tmp);
794 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
795
Jens Axboe3650a3c2012-03-05 14:09:03 +0100796 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100797 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100798
799 label = new_info_label_in_frame(hbox, "Minimum");
800 gtk_label_set_text(GTK_LABEL(label), minp);
801 label = new_info_label_in_frame(hbox, "Maximum");
802 gtk_label_set_text(GTK_LABEL(label), maxp);
803 label = new_info_label_in_frame(hbox, "Average");
804 sprintf(tmp, "%5.02f", mean);
805 gtk_label_set_text(GTK_LABEL(label), tmp);
806 label = new_info_label_in_frame(hbox, "Standard deviation");
807 sprintf(tmp, "%5.02f", dev);
808 gtk_label_set_text(GTK_LABEL(label), tmp);
809
810 free(minp);
811 free(maxp);
812
813}
814
Jens Axboeca850992012-03-05 20:04:43 +0100815#define GFIO_CLAT 1
816#define GFIO_SLAT 2
817#define GFIO_LAT 4
818
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100819static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
820 struct group_run_stats *rs,
Jens Axboe3650a3c2012-03-05 14:09:03 +0100821 struct thread_stat *ts, int ddir)
822{
823 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100824 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100825 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100826 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100827 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100828 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100829 char *io_p, *bw_p, *iops_p;
830 int i2p;
831
832 if (!ts->runtime[ddir])
833 return;
834
835 i2p = is_power_of_2(rs->kb_base);
836 runt = ts->runtime[ddir];
837
838 bw = (1000 * ts->io_bytes[ddir]) / runt;
839 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
840 bw_p = num2str(bw, 6, 1, i2p);
841
842 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
843 iops_p = num2str(iops, 6, 1, 0);
844
845 box = gtk_hbox_new(FALSE, 3);
846 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
847
848 frame = gtk_frame_new(ddir_label[ddir]);
849 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
850
Jens Axboe0b761302012-03-05 20:44:11 +0100851 main_vbox = gtk_vbox_new(FALSE, 3);
852 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100853
854 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100855 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100856
857 label = new_info_label_in_frame(box, "IO");
858 gtk_label_set_text(GTK_LABEL(label), io_p);
859 label = new_info_label_in_frame(box, "Bandwidth");
860 gtk_label_set_text(GTK_LABEL(label), bw_p);
861 label = new_info_label_in_frame(box, "IOPS");
862 gtk_label_set_text(GTK_LABEL(label), iops_p);
863 label = new_info_label_in_frame(box, "Runtime (msec)");
864 label_set_int_value(label, ts->runtime[ddir]);
865
Jens Axboee0681f32012-03-06 12:14:42 +0100866 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100867 double p_of_agg = 100.0;
868 const char *bw_str = "KB";
869 char tmp[32];
870
871 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100872 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100873 if (p_of_agg > 100.0)
874 p_of_agg = 100.0;
875 }
876
Jens Axboee0681f32012-03-06 12:14:42 +0100877 if (mean[0] > 999999.9) {
878 min[0] /= 1000.0;
879 max[0] /= 1000.0;
880 mean[0] /= 1000.0;
881 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100882 bw_str = "MB";
883 }
884
Jens Axboe0b761302012-03-05 20:44:11 +0100885 sprintf(tmp, "Bandwidth (%s)", bw_str);
886 frame = gtk_frame_new(tmp);
887 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100888
Jens Axboe0b761302012-03-05 20:44:11 +0100889 box = gtk_hbox_new(FALSE, 3);
890 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100891
Jens Axboe0b761302012-03-05 20:44:11 +0100892 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100893 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100894 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100895 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100896 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100897 sprintf(tmp, "%3.2f%%", p_of_agg);
898 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100899 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100900 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100901 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100902 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100903 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100904 gtk_label_set_text(GTK_LABEL(label), tmp);
905 }
906
Jens Axboee0681f32012-03-06 12:14:42 +0100907 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100908 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100909 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100910 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100911 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100912 flags |= GFIO_LAT;
913
914 if (flags) {
915 frame = gtk_frame_new("Latency");
916 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
917
918 vbox = gtk_vbox_new(FALSE, 3);
919 gtk_container_add(GTK_CONTAINER(frame), vbox);
920
921 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100922 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100923 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100924 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100925 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100926 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100927 }
928
929 if (ts->clat_percentiles)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100930 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
Jens Axboe2b089892012-03-06 08:09:17 +0100931
Jens Axboe3650a3c2012-03-05 14:09:03 +0100932 free(io_p);
933 free(bw_p);
934 free(iops_p);
935}
936
Jens Axboe09d574e2012-03-15 10:45:48 +0100937static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
938 const char **labels,
939 unsigned int len,
940 double xdim, double ydim)
941{
942 struct graph *g;
943 int i;
944
945 g = graph_new(xdim, ydim, gfio_graph_font);
946 graph_title(g, title);
947 graph_x_title(g, "Buckets");
948
949 for (i = 0; i < len; i++) {
950 graph_add_label(g, labels[i]);
951 graph_add_data(g, labels[i], lat[i]);
952 }
953
954 return g;
955}
956
957static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
958 int num)
Jens Axboee5bd1342012-03-05 21:38:12 +0100959{
960 GtkWidget *tree_view;
961 GtkTreeSelection *selection;
962 GtkListStore *model;
963 GtkTreeIter iter;
964 GType *types;
Jens Axboe09d574e2012-03-15 10:45:48 +0100965 int i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100966
967 types = malloc(num * sizeof(GType));
968
969 for (i = 0; i < num; i++)
970 types[i] = G_TYPE_STRING;
971
972 model = gtk_list_store_newv(num, types);
973 free(types);
974 types = NULL;
975
976 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
977 gtk_widget_set_can_focus(tree_view, FALSE);
978
Jens Axboe661f7412012-03-06 13:55:45 +0100979 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
980 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
981
Jens Axboee5bd1342012-03-05 21:38:12 +0100982 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
983 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
984
985 for (i = 0; i < num; i++)
986 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
987
988 gtk_list_store_append(model, &iter);
989
990 for (i = 0; i < num; i++) {
991 char fbuf[32];
992
993 if (lat[i] <= 0.0)
994 sprintf(fbuf, "0.00");
995 else
996 sprintf(fbuf, "%3.2f%%", lat[i]);
997
998 gtk_list_store_set(model, &iter, i, fbuf, -1);
999 }
1000
1001 return tree_view;
1002}
1003
Jens Axboe09d574e2012-03-15 10:45:48 +01001004static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
1005 struct thread_stat *ts)
Jens Axboee5bd1342012-03-05 21:38:12 +01001006{
Jens Axboe09d574e2012-03-15 10:45:48 +01001007 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
1008 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
1009 "250u", "500u", "750u", "1m", "2m",
1010 "4m", "10m", "20m", "50m", "100m",
1011 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
1012 int start, end, i;
1013 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
1014 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
1015 struct gui_entry *ge = gc->ge;
Jens Axboee5bd1342012-03-05 21:38:12 +01001016
Jens Axboe09d574e2012-03-15 10:45:48 +01001017 stat_calc_lat_u(ts, io_u_lat);
1018 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
Jens Axboee5bd1342012-03-05 21:38:12 +01001019
Jens Axboe09d574e2012-03-15 10:45:48 +01001020 /*
1021 * Found out which first bucket has entries, and which last bucket
1022 */
1023 start = end = -1U;
1024 for (i = 0; i < total; i++) {
1025 if (io_u_lat[i] == 0.00)
1026 continue;
Jens Axboee5bd1342012-03-05 21:38:12 +01001027
Jens Axboe09d574e2012-03-15 10:45:48 +01001028 if (start == -1U)
1029 start = i;
1030 end = i;
Jens Axboee5bd1342012-03-05 21:38:12 +01001031 }
1032
Jens Axboe09d574e2012-03-15 10:45:48 +01001033 /*
1034 * No entries...
1035 */
1036 if (start == -1U)
1037 return;
1038
1039 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1040 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
Jens Axboee5bd1342012-03-05 21:38:12 +01001041
Jens Axboe09d574e2012-03-15 10:45:48 +01001042 frame = gtk_frame_new("Latency buckets");
1043 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1044
1045 completion_vbox = gtk_vbox_new(FALSE, 3);
1046 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1047 hbox = gtk_hbox_new(FALSE, 3);
1048 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1049
1050 drawing_area = gtk_drawing_area_new();
1051 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1052 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1053 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1054 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1055 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1056
1057 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
Jens Axboee5bd1342012-03-05 21:38:12 +01001058}
1059
Jens Axboe2e331012012-03-05 22:07:54 +01001060static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1061{
1062 GtkWidget *box, *frame, *entry;
1063 double usr_cpu, sys_cpu;
1064 unsigned long runtime;
1065 char tmp[32];
1066
1067 runtime = ts->total_run_time;
1068 if (runtime) {
1069 double runt = (double) runtime;
1070
1071 usr_cpu = (double) ts->usr_time * 100 / runt;
1072 sys_cpu = (double) ts->sys_time * 100 / runt;
1073 } else {
1074 usr_cpu = 0;
1075 sys_cpu = 0;
1076 }
1077
1078 frame = gtk_frame_new("OS resources");
1079 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1080
1081 box = gtk_hbox_new(FALSE, 3);
1082 gtk_container_add(GTK_CONTAINER(frame), box);
1083
1084 entry = new_info_entry_in_frame(box, "User CPU");
1085 sprintf(tmp, "%3.2f%%", usr_cpu);
1086 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1087 entry = new_info_entry_in_frame(box, "System CPU");
1088 sprintf(tmp, "%3.2f%%", sys_cpu);
1089 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1090 entry = new_info_entry_in_frame(box, "Context switches");
1091 entry_set_int_value(entry, ts->ctx);
1092 entry = new_info_entry_in_frame(box, "Major faults");
1093 entry_set_int_value(entry, ts->majf);
1094 entry = new_info_entry_in_frame(box, "Minor faults");
1095 entry_set_int_value(entry, ts->minf);
1096}
Jens Axboe19998db2012-03-06 09:17:59 +01001097static void gfio_add_sc_depths_tree(GtkListStore *model,
1098 struct thread_stat *ts, unsigned int len,
1099 int submit)
1100{
1101 double io_u_dist[FIO_IO_U_MAP_NR];
1102 GtkTreeIter iter;
1103 /* Bits 0, and 3-8 */
1104 const int add_mask = 0x1f9;
1105 int i, j;
1106
1107 if (submit)
1108 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1109 else
1110 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1111
1112 gtk_list_store_append(model, &iter);
1113
1114 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1115
1116 for (i = 1, j = 0; i < len; i++) {
1117 char fbuf[32];
1118
1119 if (!(add_mask & (1UL << (i - 1))))
1120 sprintf(fbuf, "0.0%%");
1121 else {
1122 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1123 j++;
1124 }
1125
1126 gtk_list_store_set(model, &iter, i, fbuf, -1);
1127 }
1128
1129}
1130
1131static void gfio_add_total_depths_tree(GtkListStore *model,
1132 struct thread_stat *ts, unsigned int len)
1133{
1134 double io_u_dist[FIO_IO_U_MAP_NR];
1135 GtkTreeIter iter;
1136 /* Bits 1-6, and 8 */
1137 const int add_mask = 0x17e;
1138 int i, j;
1139
1140 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1141
1142 gtk_list_store_append(model, &iter);
1143
1144 gtk_list_store_set(model, &iter, 0, "Total", -1);
1145
1146 for (i = 1, j = 0; i < len; i++) {
1147 char fbuf[32];
1148
1149 if (!(add_mask & (1UL << (i - 1))))
1150 sprintf(fbuf, "0.0%%");
1151 else {
1152 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1153 j++;
1154 }
1155
1156 gtk_list_store_set(model, &iter, i, fbuf, -1);
1157 }
1158
1159}
Jens Axboe2e331012012-03-05 22:07:54 +01001160
1161static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1162{
Jens Axboe2e331012012-03-05 22:07:54 +01001163 GtkWidget *frame, *box, *tree_view;
1164 GtkTreeSelection *selection;
1165 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +01001166 GType types[FIO_IO_U_MAP_NR + 1];
1167 int i;
Jens Axboe19998db2012-03-06 09:17:59 +01001168#define NR_LABELS 10
1169 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +01001170
1171 frame = gtk_frame_new("IO depths");
1172 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1173
1174 box = gtk_hbox_new(FALSE, 3);
1175 gtk_container_add(GTK_CONTAINER(frame), box);
1176
Jens Axboe19998db2012-03-06 09:17:59 +01001177 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001178 types[i] = G_TYPE_STRING;
1179
Jens Axboe19998db2012-03-06 09:17:59 +01001180 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +01001181
1182 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1183 gtk_widget_set_can_focus(tree_view, FALSE);
1184
Jens Axboe661f7412012-03-06 13:55:45 +01001185 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1186 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1187
Jens Axboe2e331012012-03-05 22:07:54 +01001188 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1189 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1190
Jens Axboe19998db2012-03-06 09:17:59 +01001191 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001192 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1193
Jens Axboe19998db2012-03-06 09:17:59 +01001194 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1195 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1196 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001197
1198 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1199}
1200
Jens Axboef9d40b42012-03-06 09:52:49 +01001201static gboolean results_window_delete(GtkWidget *w, gpointer data)
1202{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001203 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001204
1205 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001206 ge->results_window = NULL;
1207 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001208 return TRUE;
1209}
1210
Jens Axboe17b97212012-03-14 20:17:57 +01001211static void results_close(GtkWidget *w, gpointer *data)
1212{
1213 struct gui_entry *ge = (struct gui_entry *) data;
1214
1215 gtk_widget_destroy(ge->results_window);
1216}
1217
1218static GtkActionEntry results_menu_items[] = {
1219 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1220 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1221 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1222};
1223static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1224
1225static const gchar *results_ui_string = " \
1226 <ui> \
1227 <menubar name=\"MainMenu\"> \
1228 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1229 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1230 </menu> \
1231 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1232 </menu>\
1233 </menubar> \
1234 </ui> \
1235";
1236
1237static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1238{
1239 GtkActionGroup *action_group;
1240 GtkWidget *widget;
1241 GError *error = 0;
1242
1243 ge->results_uimanager = gtk_ui_manager_new();
1244
1245 action_group = gtk_action_group_new("ResultsMenu");
1246 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1247
1248 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1249 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1250
1251 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1252
1253 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1254 return widget;
1255}
1256
Jens Axboe2f99deb2012-03-09 14:37:29 +01001257static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001258{
Jens Axboe17b97212012-03-14 20:17:57 +01001259 GtkWidget *win, *notebook, *vbox;
Jens Axboef9d40b42012-03-06 09:52:49 +01001260
Jens Axboe2f99deb2012-03-09 14:37:29 +01001261 if (ge->results_window)
1262 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001263
1264 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1265 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001266 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001267 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1268 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001269
Jens Axboe17b97212012-03-14 20:17:57 +01001270 vbox = gtk_vbox_new(FALSE, 0);
1271 gtk_container_add(GTK_CONTAINER(win), vbox);
1272
1273 ge->results_menu = get_results_menubar(win, ge);
1274 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1275
Jens Axboef9d40b42012-03-06 09:52:49 +01001276 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001277 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1278 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboe17b97212012-03-14 20:17:57 +01001279 gtk_container_add(GTK_CONTAINER(vbox), notebook);
Jens Axboef9d40b42012-03-06 09:52:49 +01001280
Jens Axboe2f99deb2012-03-09 14:37:29 +01001281 ge->results_window = win;
1282 ge->results_notebook = notebook;
1283 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001284}
1285
Jens Axboe7da23b42012-03-15 13:45:02 +01001286static void disk_util_destroy(GtkWidget *w, gpointer data)
Jens Axboe3650a3c2012-03-05 14:09:03 +01001287{
Jens Axboe7da23b42012-03-15 13:45:02 +01001288 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001289
Jens Axboe7da23b42012-03-15 13:45:02 +01001290 ge->disk_util_vbox = NULL;
1291 gtk_widget_destroy(w);
Jens Axboe781ccba2012-03-15 09:44:42 +01001292}
Jens Axboe3650a3c2012-03-05 14:09:03 +01001293
Jens Axboef0602d72012-03-15 15:38:02 +01001294static GtkWidget *get_scrolled_window(gint border_width)
1295{
1296 GtkWidget *scroll;
1297
1298 scroll = gtk_scrolled_window_new(NULL, NULL);
1299 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1300 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1301
1302 return scroll;
1303}
1304
1305static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1306{
1307 GtkWidget *vbox, *box, *scroll, *res_notebook;
1308
1309 if (ge->disk_util_vbox)
1310 return ge->disk_util_vbox;
1311
1312 scroll = get_scrolled_window(5);
1313 vbox = gtk_vbox_new(FALSE, 3);
1314 box = gtk_hbox_new(FALSE, 0);
1315 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1316
1317 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1318 res_notebook = get_results_window(ge);
1319
1320 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1321 ge->disk_util_vbox = box;
1322 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1323
1324 return ge->disk_util_vbox;
1325}
1326
Jens Axboe7da23b42012-03-15 13:45:02 +01001327static int __gfio_disk_util_show(GtkWidget *res_notebook,
1328 struct gfio_client *gc, struct cmd_du_pdu *p)
Jens Axboe781ccba2012-03-15 09:44:42 +01001329{
Jens Axboef0602d72012-03-15 15:38:02 +01001330 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
Jens Axboe7da23b42012-03-15 13:45:02 +01001331 struct gui_entry *ge = gc->ge;
Jens Axboe604cfe32012-03-07 19:51:36 +01001332 double util;
1333 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001334
Jens Axboef0602d72012-03-15 15:38:02 +01001335 util_vbox = gfio_disk_util_get_vbox(ge);
Jens Axboee0681f32012-03-06 12:14:42 +01001336
1337 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboef0602d72012-03-15 15:38:02 +01001338 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
Jens Axboee0681f32012-03-06 12:14:42 +01001339
1340 frame = gtk_frame_new((char *) p->dus.name);
1341 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1342
1343 box = gtk_vbox_new(FALSE, 3);
1344 gtk_container_add(GTK_CONTAINER(frame), box);
1345
1346 frame = gtk_frame_new("Read");
1347 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1348 vbox = gtk_hbox_new(TRUE, 3);
1349 gtk_container_add(GTK_CONTAINER(frame), vbox);
1350 entry = new_info_entry_in_frame(vbox, "IOs");
1351 entry_set_int_value(entry, p->dus.ios[0]);
1352 entry = new_info_entry_in_frame(vbox, "Merges");
1353 entry_set_int_value(entry, p->dus.merges[0]);
1354 entry = new_info_entry_in_frame(vbox, "Sectors");
1355 entry_set_int_value(entry, p->dus.sectors[0]);
1356 entry = new_info_entry_in_frame(vbox, "Ticks");
1357 entry_set_int_value(entry, p->dus.ticks[0]);
1358
1359 frame = gtk_frame_new("Write");
1360 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1361 vbox = gtk_hbox_new(TRUE, 3);
1362 gtk_container_add(GTK_CONTAINER(frame), vbox);
1363 entry = new_info_entry_in_frame(vbox, "IOs");
1364 entry_set_int_value(entry, p->dus.ios[1]);
1365 entry = new_info_entry_in_frame(vbox, "Merges");
1366 entry_set_int_value(entry, p->dus.merges[1]);
1367 entry = new_info_entry_in_frame(vbox, "Sectors");
1368 entry_set_int_value(entry, p->dus.sectors[1]);
1369 entry = new_info_entry_in_frame(vbox, "Ticks");
1370 entry_set_int_value(entry, p->dus.ticks[1]);
1371
1372 frame = gtk_frame_new("Shared");
1373 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1374 vbox = gtk_hbox_new(TRUE, 3);
1375 gtk_container_add(GTK_CONTAINER(frame), vbox);
1376 entry = new_info_entry_in_frame(vbox, "IO ticks");
1377 entry_set_int_value(entry, p->dus.io_ticks);
1378 entry = new_info_entry_in_frame(vbox, "Time in queue");
1379 entry_set_int_value(entry, p->dus.time_in_queue);
1380
Jens Axboe604cfe32012-03-07 19:51:36 +01001381 util = 0.0;
1382 if (p->dus.msec)
1383 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1384 if (util > 100.0)
1385 util = 100.0;
1386
1387 sprintf(tmp, "%3.2f%%", util);
1388 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1389 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1390
Jens Axboe7da23b42012-03-15 13:45:02 +01001391 gtk_widget_show_all(ge->results_window);
1392 return 0;
1393}
1394
1395static int gfio_disk_util_show(struct gfio_client *gc)
1396{
1397 struct gui_entry *ge = gc->ge;
1398 GtkWidget *res_notebook;
1399 int i;
1400
1401 if (!gc->nr_du)
1402 return 1;
1403
1404 res_notebook = get_results_window(ge);
1405
1406 for (i = 0; i < gc->nr_du; i++) {
1407 struct cmd_du_pdu *p = &gc->du[i];
1408
1409 __gfio_disk_util_show(res_notebook, gc, p);
1410 }
1411
1412 gtk_widget_show_all(ge->results_window);
1413 return 0;
1414}
1415
1416static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1417 struct group_run_stats *rs)
1418{
1419 unsigned int nr = gc->nr_results;
1420
1421 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1422 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1423 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1424 gc->nr_results++;
1425}
1426
1427static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1428 struct thread_stat *ts,
1429 struct group_run_stats *rs)
1430{
1431 GtkWidget *box, *vbox, *entry, *scroll;
1432
1433 scroll = gtk_scrolled_window_new(NULL, NULL);
1434 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1435 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1436
1437 vbox = gtk_vbox_new(FALSE, 3);
1438
1439 box = gtk_hbox_new(FALSE, 0);
1440 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1441
1442 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1443
1444 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1445
1446 entry = new_info_entry_in_frame(box, "Name");
1447 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1448 if (strlen(ts->description)) {
1449 entry = new_info_entry_in_frame(box, "Description");
1450 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1451 }
1452 entry = new_info_entry_in_frame(box, "Group ID");
1453 entry_set_int_value(entry, ts->groupid);
1454 entry = new_info_entry_in_frame(box, "Jobs");
1455 entry_set_int_value(entry, ts->members);
1456 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1457 entry_set_int_value(entry, ts->error);
1458 entry = new_info_entry_in_frame(box, "PID");
1459 entry_set_int_value(entry, ts->pid);
1460
1461 if (ts->io_bytes[DDIR_READ])
1462 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1463 if (ts->io_bytes[DDIR_WRITE])
1464 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1465
1466 gfio_show_latency_buckets(gc, vbox, ts);
1467 gfio_show_cpu_usage(vbox, ts);
1468 gfio_show_io_depths(vbox, ts);
1469}
1470
1471static void gfio_display_end_results(struct gfio_client *gc)
1472{
1473 struct gui_entry *ge = gc->ge;
1474 GtkWidget *res_notebook;
1475 int i;
1476
1477 res_notebook = get_results_window(ge);
1478
1479 for (i = 0; i < gc->nr_results; i++) {
1480 struct end_results *e = &gc->results[i];
1481
1482 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1483 }
1484
1485 if (gfio_disk_util_show(gc))
1486 gtk_widget_show_all(ge->results_window);
1487}
1488
1489static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1490 struct group_run_stats *rs)
1491{
1492 struct gfio_client *gc = client->client_data;
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001493 struct gui_entry *ge = gc->ge;
Jens Axboe7da23b42012-03-15 13:45:02 +01001494
1495 gfio_add_end_results(gc, ts, rs);
1496
1497 gdk_threads_enter();
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001498 if (ge->results_window)
1499 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1500 else
1501 gfio_display_end_results(gc);
Jens Axboe7da23b42012-03-15 13:45:02 +01001502 gdk_threads_leave();
1503}
1504
1505static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1506{
1507 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1508 struct gui *ui = &main_ui;
1509 GtkTreeIter iter;
1510 struct tm *tm;
1511 time_t sec;
1512 char tmp[64], timebuf[80];
1513
1514 sec = p->log_sec;
1515 tm = localtime(&sec);
1516 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1517 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1518
1519 gdk_threads_enter();
1520
1521 gtk_list_store_append(ui->log_model, &iter);
1522 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1523 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1524 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1525 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1526
1527 if (p->level == FIO_LOG_ERR)
1528 view_log(NULL, (gpointer) ui);
1529
1530 gdk_threads_leave();
1531}
1532
1533static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1534{
1535 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1536 struct gfio_client *gc = client->client_data;
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001537 struct gui_entry *ge = gc->ge;
Jens Axboe7da23b42012-03-15 13:45:02 +01001538 unsigned int nr = gc->nr_du;
1539
1540 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1541 memcpy(&gc->du[nr], p, sizeof(*p));
1542 gc->nr_du++;
1543
1544 gdk_threads_enter();
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001545 if (ge->results_window)
1546 __gfio_disk_util_show(ge->results_notebook, gc, p);
1547 else
1548 gfio_disk_util_show(gc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001549 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001550}
1551
Jens Axboe3650a3c2012-03-05 14:09:03 +01001552extern int sum_stat_clients;
1553extern struct thread_stat client_ts;
1554extern struct group_run_stats client_gs;
1555
1556static int sum_stat_nr;
1557
Jens Axboe89e5fad2012-03-05 09:21:12 +01001558static void gfio_thread_status_op(struct fio_client *client,
1559 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001560{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001561 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1562
1563 gfio_display_ts(client, &p->ts, &p->rs);
1564
1565 if (sum_stat_clients == 1)
1566 return;
1567
1568 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1569 sum_group_stats(&client_gs, &p->rs);
1570
1571 client_ts.members++;
Jens Axboe2f122b12012-03-15 13:10:19 +01001572 client_ts.thread_number = p->ts.thread_number;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001573 client_ts.groupid = p->ts.groupid;
1574
1575 if (++sum_stat_nr == sum_stat_clients) {
1576 strcpy(client_ts.name, "All clients");
1577 gfio_display_ts(client, &client_ts, &client_gs);
1578 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001579}
1580
Jens Axboe89e5fad2012-03-05 09:21:12 +01001581static void gfio_group_stats_op(struct fio_client *client,
1582 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001583{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001584 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001585}
1586
Jens Axboe2f99deb2012-03-09 14:37:29 +01001587static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1588 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001589{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001590 struct gfio_graphs *g = data;
1591
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001592 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1593 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1594 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1595 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001596 return TRUE;
1597}
1598
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001599static void draw_graph(struct graph *g, cairo_t *cr)
1600{
1601 line_graph_draw(g, cr);
1602 cairo_stroke(cr);
1603}
1604
Jens Axboe93e2db22012-03-13 09:45:22 +01001605static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1606 gboolean keyboard_mode, GtkTooltip *tooltip,
1607 gpointer data)
1608{
1609 struct gfio_graphs *g = data;
1610 const char *text = NULL;
1611
1612 if (graph_contains_xy(g->iops_graph, x, y))
1613 text = graph_find_tooltip(g->iops_graph, x, y);
1614 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1615 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1616
1617 if (text) {
1618 gtk_tooltip_set_text(tooltip, text);
1619 return TRUE;
1620 }
1621
1622 return FALSE;
1623}
1624
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001625static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1626{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001627 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001628 cairo_t *cr;
1629
1630 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001631
1632 if (graph_has_tooltips(g->iops_graph) ||
1633 graph_has_tooltips(g->bandwidth_graph)) {
1634 g_object_set(w, "has-tooltip", TRUE, NULL);
1635 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1636 }
1637
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001638 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001639 draw_graph(g->iops_graph, cr);
1640 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001641 cairo_destroy(cr);
1642
1643 return FALSE;
1644}
1645
Jens Axboe2f99deb2012-03-09 14:37:29 +01001646/*
1647 * Client specific ETA
1648 */
1649static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001650{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001651 struct gfio_client *gc = client->client_data;
1652 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001653 static int eta_good;
1654 char eta_str[128];
1655 char output[256];
1656 char tmp[32];
1657 double perc = 0.0;
1658 int i2p = 0;
1659
Jens Axboe0050e5f2012-03-06 09:23:27 +01001660 gdk_threads_enter();
1661
Jens Axboe3e47bd22012-02-29 13:45:02 +01001662 eta_str[0] = '\0';
1663 output[0] = '\0';
1664
1665 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1666 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1667 eta_to_str(eta_str, je->eta_sec);
1668 }
1669
1670 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001671 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001672 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001673 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001674
1675#if 0
1676 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1677 if (je->m_rate || je->t_rate) {
1678 char *tr, *mr;
1679
1680 mr = num2str(je->m_rate, 4, 0, i2p);
1681 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001682 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001683 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1684 free(tr);
1685 free(mr);
1686 } else if (je->m_iops || je->t_iops)
1687 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001688
Jens Axboe2f99deb2012-03-09 14:37:29 +01001689 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1690 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1691 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1692 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001693#endif
1694
1695 if (je->eta_sec != INT_MAX && je->nr_running) {
1696 char *iops_str[2];
1697 char *rate_str[2];
1698
1699 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1700 strcpy(output, "-.-% done");
1701 else {
1702 eta_good = 1;
1703 perc *= 100.0;
1704 sprintf(output, "%3.1f%% done", perc);
1705 }
1706
1707 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1708 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1709
1710 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1711 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1712
Jens Axboe2f99deb2012-03-09 14:37:29 +01001713 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1714 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1715 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1716 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001717
Jens Axboe93e2db22012-03-13 09:45:22 +01001718 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1719 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1720 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1721 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001722
1723 free(rate_str[0]);
1724 free(rate_str[1]);
1725 free(iops_str[0]);
1726 free(iops_str[1]);
1727 }
1728
1729 if (eta_str[0]) {
1730 char *dst = output + strlen(output);
1731
1732 sprintf(dst, " - %s", eta_str);
1733 }
1734
Jens Axboe9988ca72012-03-09 15:14:06 +01001735 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001736 gdk_threads_leave();
1737}
1738
1739/*
1740 * Update ETA in main window for all clients
1741 */
1742static void gfio_update_all_eta(struct jobs_eta *je)
1743{
1744 struct gui *ui = &main_ui;
1745 static int eta_good;
1746 char eta_str[128];
1747 char output[256];
1748 double perc = 0.0;
1749 int i2p = 0;
1750
1751 gdk_threads_enter();
1752
1753 eta_str[0] = '\0';
1754 output[0] = '\0';
1755
1756 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1757 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1758 eta_to_str(eta_str, je->eta_sec);
1759 }
1760
1761#if 0
1762 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1763 if (je->m_rate || je->t_rate) {
1764 char *tr, *mr;
1765
1766 mr = num2str(je->m_rate, 4, 0, i2p);
1767 tr = num2str(je->t_rate, 4, 0, i2p);
1768 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1769 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1770 free(tr);
1771 free(mr);
1772 } else if (je->m_iops || je->t_iops)
1773 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1774
1775 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1776 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1777 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1778 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1779#endif
1780
Jens Axboe3863d1a2012-03-09 17:39:05 +01001781 entry_set_int_value(ui->eta.jobs, je->nr_running);
1782
Jens Axboe2f99deb2012-03-09 14:37:29 +01001783 if (je->eta_sec != INT_MAX && je->nr_running) {
1784 char *iops_str[2];
1785 char *rate_str[2];
1786
1787 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1788 strcpy(output, "-.-% done");
1789 else {
1790 eta_good = 1;
1791 perc *= 100.0;
1792 sprintf(output, "%3.1f%% done", perc);
1793 }
1794
1795 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1796 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1797
1798 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1799 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1800
1801 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1802 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1803 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1804 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1805
Jens Axboe93e2db22012-03-13 09:45:22 +01001806 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1807 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1808 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1809 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001810
Jens Axboe3e47bd22012-02-29 13:45:02 +01001811 free(rate_str[0]);
1812 free(rate_str[1]);
1813 free(iops_str[0]);
1814 free(iops_str[1]);
1815 }
1816
1817 if (eta_str[0]) {
1818 char *dst = output + strlen(output);
1819
1820 sprintf(dst, " - %s", eta_str);
1821 }
1822
Jens Axboe9988ca72012-03-09 15:14:06 +01001823 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001824 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001825}
1826
Stephen M. Camerona1820202012-02-24 08:17:31 +01001827static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1828{
Jens Axboe843ad232012-02-29 11:44:53 +01001829 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001830 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001831 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001832 const char *os, *arch;
1833 char buf[64];
1834
1835 os = fio_get_os_string(probe->os);
1836 if (!os)
1837 os = "unknown";
1838
1839 arch = fio_get_arch_string(probe->arch);
1840 if (!arch)
1841 os = "unknown";
1842
1843 if (!client->name)
1844 client->name = strdup((char *) probe->hostname);
1845
Jens Axboe0050e5f2012-03-06 09:23:27 +01001846 gdk_threads_enter();
1847
Jens Axboe2f99deb2012-03-09 14:37:29 +01001848 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1849 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1850 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001851 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001852 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001853
Jens Axboe85dd01e2012-03-12 14:33:16 +01001854 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001855
1856 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001857}
1858
Jens Axboe9988ca72012-03-09 15:14:06 +01001859static void gfio_update_thread_status(struct gui_entry *ge,
1860 char *status_message, double perc)
1861{
1862 static char message[100];
1863 const char *m = message;
1864
1865 strncpy(message, status_message, sizeof(message) - 1);
1866 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1867 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1868 gtk_widget_queue_draw(main_ui.window);
1869}
1870
1871static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001872{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001873 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001874 static char message[100];
1875 const char *m = message;
1876
1877 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001878 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1879 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1880 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001881}
1882
Jens Axboe35c0ba72012-03-14 10:56:40 +01001883static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001884{
Jens Axboee0681f32012-03-06 12:14:42 +01001885 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001886
Jens Axboe0050e5f2012-03-06 09:23:27 +01001887 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001888 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001889 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001890}
1891
Jens Axboe807f9972012-03-02 10:25:24 +01001892static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1893{
1894 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001895 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001896 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001897 struct gui_entry *ge = gc->ge;
Jens Axboe99d633a2012-03-15 15:55:04 +01001898 char *c1, *c2, *c3, *c4;
1899 char tmp[80];
Jens Axboe807f9972012-03-02 10:25:24 +01001900
Jens Axboe2f122b12012-03-15 13:10:19 +01001901 p->thread_number = le32_to_cpu(p->thread_number);
1902 p->groupid = le32_to_cpu(p->groupid);
Jens Axboedcaeb602012-03-08 19:45:37 +01001903 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001904
Jens Axboe0050e5f2012-03-06 09:23:27 +01001905 gdk_threads_enter();
1906
Jens Axboe2f99deb2012-03-09 14:37:29 +01001907 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1908
Jens Axboe3863d1a2012-03-09 17:39:05 +01001909 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1910 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1911
Jens Axboeddbafc12012-03-15 18:57:03 +01001912 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1913 multitext_add_entry(&ge->eta.iotype, tmp);
Jens Axboe99d633a2012-03-15 15:55:04 +01001914
1915 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1916 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1917 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1918 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1919 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1920 free(c1);
1921 free(c2);
1922 free(c3);
1923 free(c4);
1924 multitext_add_entry(&ge->eta.bs, tmp);
1925
Jens Axboec80b74b2012-03-12 10:23:28 +01001926 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001927
Jens Axboedcaeb602012-03-08 19:45:37 +01001928 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001929 multitext_add_entry(&ge->eta.iodepth, tmp);
1930
1931 multitext_set_entry(&ge->eta.iotype, 0);
Jens Axboe99d633a2012-03-15 15:55:04 +01001932 multitext_set_entry(&ge->eta.bs, 0);
Jens Axboec80b74b2012-03-12 10:23:28 +01001933 multitext_set_entry(&ge->eta.ioengine, 0);
1934 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001935
Jens Axboe85dd01e2012-03-12 14:33:16 +01001936 gfio_set_state(ge, GE_STATE_JOB_SENT);
1937
Jens Axboe0050e5f2012-03-06 09:23:27 +01001938 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001939}
1940
Jens Axboeed727a42012-03-02 12:14:40 +01001941static void gfio_client_timed_out(struct fio_client *client)
1942{
Jens Axboee0681f32012-03-06 12:14:42 +01001943 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001944 char buf[256];
1945
1946 gdk_threads_enter();
1947
Jens Axboe85dd01e2012-03-12 14:33:16 +01001948 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001949 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001950
1951 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001952 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001953
1954 gdk_threads_leave();
1955}
1956
Jens Axboe6b79c802012-03-08 10:51:36 +01001957static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1958{
1959 struct gfio_client *gc = client->client_data;
1960
1961 gdk_threads_enter();
1962
Jens Axboe85dd01e2012-03-12 14:33:16 +01001963 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001964
1965 if (gc->err_entry)
1966 entry_set_int_value(gc->err_entry, client->error);
1967
1968 gdk_threads_leave();
1969}
1970
Jens Axboe85dd01e2012-03-12 14:33:16 +01001971static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1972{
1973 struct gfio_client *gc = client->client_data;
1974
1975 gdk_threads_enter();
1976 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1977 gdk_threads_leave();
1978}
1979
1980static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1981{
1982 struct gfio_client *gc = client->client_data;
1983
1984 gdk_threads_enter();
1985 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1986 gdk_threads_leave();
1987}
1988
Jens Axboe1b427252012-03-14 15:03:03 +01001989static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1990{
Jens Axboe284b1e62012-03-14 21:55:07 +01001991 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
Jens Axboe1b427252012-03-14 15:03:03 +01001992 free(pdu);
1993}
1994
Stephen M. Camerona1820202012-02-24 08:17:31 +01001995struct client_ops gfio_client_ops = {
Jens Axboe35c0ba72012-03-14 10:56:40 +01001996 .text = gfio_text_op,
Jens Axboe0420ba62012-02-29 11:16:52 +01001997 .disk_util = gfio_disk_util_op,
1998 .thread_status = gfio_thread_status_op,
1999 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01002000 .jobs_eta = gfio_update_client_eta,
2001 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01002002 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002003 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01002004 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01002005 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01002006 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01002007 .start = gfio_client_start,
2008 .job_start = gfio_client_job_start,
Jens Axboe1b427252012-03-14 15:03:03 +01002009 .iolog = gfio_client_iolog,
Jens Axboe6433ee02012-03-09 20:10:51 +01002010 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002011 .stay_connected = 1,
Jens Axboe46bcd492012-03-14 11:31:21 +01002012 .client_type = FIO_CLIENT_TYPE_GUI,
Stephen M. Camerona1820202012-02-24 08:17:31 +01002013};
2014
Jens Axboe0fd18982012-03-14 10:34:48 +01002015/*
2016 * FIXME: need more handling here
2017 */
2018static void ge_destroy(struct gui_entry *ge)
2019{
2020 struct gfio_client *gc = ge->client;
2021
2022 if (gc && gc->client) {
2023 if (ge->state >= GE_STATE_CONNECTED)
2024 fio_client_terminate(gc->client);
2025
2026 fio_put_client(gc->client);
2027 }
2028
2029 flist_del(&ge->list);
2030 free(ge);
2031}
2032
2033static void ge_widget_destroy(GtkWidget *w, gpointer data)
2034{
Jens Axboe0fd18982012-03-14 10:34:48 +01002035}
2036
2037static void gfio_quit(struct gui *ui)
2038{
2039 struct gui_entry *ge;
2040
2041 while (!flist_empty(&ui->list)) {
2042 ge = flist_entry(ui->list.next, struct gui_entry, list);
2043 ge_destroy(ge);
2044 }
2045
2046 gtk_main_quit();
2047}
2048
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002049static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
2050 __attribute__((unused)) gpointer data)
2051{
Jens Axboe0fd18982012-03-14 10:34:48 +01002052 gfio_quit(data);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002053}
2054
Stephen M. Cameron25927252012-02-24 08:17:31 +01002055static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002056{
Jens Axboea9eccde2012-03-09 14:59:42 +01002057 struct gui *ui = arg;
2058
2059 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01002060 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01002061 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01002062 return NULL;
2063}
2064
Jens Axboe2f99deb2012-03-09 14:37:29 +01002065static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01002066{
Jens Axboe9988ca72012-03-09 15:14:06 +01002067 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01002068 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01002069
Jens Axboe2f99deb2012-03-09 14:37:29 +01002070 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01002071 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01002072 if (ret < 0) {
2073 GError *error;
2074
2075 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
2076 report_error(error);
2077 g_error_free(error);
2078 break;
2079 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01002080 break;
2081
Jens Axboe2f99deb2012-03-09 14:37:29 +01002082 free(ge->job_files[i]);
2083 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01002084 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002085 while (i < ge->nr_job_files) {
2086 free(ge->job_files[i]);
2087 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01002088 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01002089 }
2090
Jens Axboe2c77d832012-03-13 19:02:04 +01002091 free(ge->job_files);
2092 ge->job_files = NULL;
Jens Axboe3af45202012-03-13 09:59:53 +01002093 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01002094 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01002095}
2096
Jens Axboe63a130b2012-03-06 20:08:59 +01002097static void *server_thread(void *arg)
2098{
2099 is_backend = 1;
2100 gfio_server_running = 1;
2101 fio_start_server(NULL);
2102 gfio_server_running = 0;
2103 return NULL;
2104}
2105
Jens Axboe2f99deb2012-03-09 14:37:29 +01002106static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01002107{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002108 struct gui *ui = &main_ui;
2109
Jens Axboe63a130b2012-03-06 20:08:59 +01002110 if (!gfio_server_running) {
2111 gfio_server_running = 1;
2112 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01002113 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01002114 }
2115}
2116
Stephen M. Cameron25927252012-02-24 08:17:31 +01002117static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2118 gpointer data)
2119{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002120 struct gui_entry *ge = data;
2121 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01002122
Jens Axboe78cb2fe2012-03-12 23:05:29 +01002123 if (gc)
2124 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002125}
2126
Jens Axboedf06f222012-03-02 13:32:04 +01002127static void file_open(GtkWidget *w, gpointer data);
2128
2129static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01002130{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002131 struct gui_entry *ge = data;
2132 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002133
Jens Axboe85dd01e2012-03-12 14:33:16 +01002134 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01002135 int ret;
2136
Jens Axboe2f99deb2012-03-09 14:37:29 +01002137 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01002138 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002139 if (!ge->nr_job_files)
2140 return;
2141
Jens Axboed3b70f32012-03-15 14:05:01 +01002142 gc = ge->client;
2143
Jens Axboe2f99deb2012-03-09 14:37:29 +01002144 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2145 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01002146 ret = fio_client_connect(gc->client);
2147 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01002148 if (!ge->ui->handler_running)
2149 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002150 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01002151 } else {
2152 GError *error;
2153
2154 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2155 report_error(error);
2156 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01002157 }
Jens Axboedf06f222012-03-02 13:32:04 +01002158 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002159 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002160 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002161 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01002162 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01002163}
2164
Jens Axboeb9d2f302012-03-08 20:36:28 +01002165static void send_clicked(GtkWidget *widget, gpointer data)
2166{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002167 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01002168
Jens Axboe2f99deb2012-03-09 14:37:29 +01002169 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01002170 GError *error;
2171
2172 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send one or more job files for client %s", ge->client->client->hostname);
2173 report_error(error);
2174 g_error_free(error);
2175
Jens Axboe2f99deb2012-03-09 14:37:29 +01002176 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002177 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01002178}
2179
Jens Axboe0420ba62012-02-29 11:16:52 +01002180static void on_info_bar_response(GtkWidget *widget, gint response,
2181 gpointer data)
2182{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002183 struct gui *ui = &main_ui;
2184
Jens Axboe0420ba62012-02-29 11:16:52 +01002185 if (response == GTK_RESPONSE_OK) {
2186 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002187 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01002188 }
2189}
2190
Jens Axboedf06f222012-03-02 13:32:04 +01002191void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01002192{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002193 struct gui *ui = &main_ui;
2194
2195 if (ui->error_info_bar == NULL) {
2196 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01002197 GTK_RESPONSE_OK,
2198 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002199 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2200 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01002201 GTK_MESSAGE_ERROR);
2202
Jens Axboe2f99deb2012-03-09 14:37:29 +01002203 ui->error_label = gtk_label_new(error->message);
2204 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2205 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01002206
Jens Axboe2f99deb2012-03-09 14:37:29 +01002207 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2208 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01002209 } else {
2210 char buffer[256];
2211 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002212 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01002213 }
2214}
2215
Jens Axboe62bc9372012-03-07 11:45:07 +01002216struct connection_widgets
2217{
2218 GtkWidget *hentry;
2219 GtkWidget *combo;
2220 GtkWidget *button;
2221};
2222
2223static void hostname_cb(GtkEntry *entry, gpointer data)
2224{
2225 struct connection_widgets *cw = data;
2226 int uses_net = 0, is_localhost = 0;
2227 const gchar *text;
2228 gchar *ctext;
2229
2230 /*
2231 * Check whether to display the 'auto start backend' box
2232 * or not. Show it if we are a localhost and using network,
2233 * or using a socket.
2234 */
2235 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2236 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2237 uses_net = 1;
2238 g_free(ctext);
2239
2240 if (uses_net) {
2241 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2242 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2243 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2244 !strcmp(text, "ip6-loopback"))
2245 is_localhost = 1;
2246 }
2247
2248 if (!uses_net || is_localhost) {
2249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2250 gtk_widget_set_sensitive(cw->button, 1);
2251 } else {
2252 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2253 gtk_widget_set_sensitive(cw->button, 0);
2254 }
2255}
2256
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002257static int get_connection_details(char **host, int *port, int *type,
2258 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01002259{
Jens Axboe62bc9372012-03-07 11:45:07 +01002260 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2261 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002262 char *typeentry;
2263
2264 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002265 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01002266 GTK_DIALOG_DESTROY_WITH_PARENT,
2267 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2268 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2269
2270 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01002271 /* gtk_dialog_get_content_area() is 2.14 and newer */
2272 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002273 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2274
2275 box = gtk_vbox_new(FALSE, 6);
2276 gtk_container_add(GTK_CONTAINER(frame), box);
2277
2278 hbox = gtk_hbox_new(TRUE, 10);
2279 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01002280 cw.hentry = gtk_entry_new();
2281 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2282 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002283
2284 frame = gtk_frame_new("Port");
2285 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2286 box = gtk_vbox_new(FALSE, 10);
2287 gtk_container_add(GTK_CONTAINER(frame), box);
2288
2289 hbox = gtk_hbox_new(TRUE, 4);
2290 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2291 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2292
2293 frame = gtk_frame_new("Type");
2294 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2295 box = gtk_vbox_new(FALSE, 10);
2296 gtk_container_add(GTK_CONTAINER(frame), box);
2297
2298 hbox = gtk_hbox_new(TRUE, 4);
2299 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2300
Jens Axboe62bc9372012-03-07 11:45:07 +01002301 cw.combo = gtk_combo_box_new_text();
2302 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2303 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2304 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2305 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002306
Jens Axboe62bc9372012-03-07 11:45:07 +01002307 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002308
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002309 frame = gtk_frame_new("Options");
2310 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2311 box = gtk_vbox_new(FALSE, 10);
2312 gtk_container_add(GTK_CONTAINER(frame), box);
2313
2314 hbox = gtk_hbox_new(TRUE, 4);
2315 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2316
Jens Axboe62bc9372012-03-07 11:45:07 +01002317 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2318 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2319 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.");
2320 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2321
2322 /*
2323 * Connect edit signal, so we can show/not-show the auto start button
2324 */
2325 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2326 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002327
Jens Axboea7a42ce2012-03-02 13:12:04 +01002328 gtk_widget_show_all(dialog);
2329
2330 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2331 gtk_widget_destroy(dialog);
2332 return 1;
2333 }
2334
Jens Axboe62bc9372012-03-07 11:45:07 +01002335 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002336 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2337
Jens Axboe62bc9372012-03-07 11:45:07 +01002338 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002339 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2340 *type = Fio_client_ipv4;
2341 else if (!strncmp(typeentry, "IPv6", 4))
2342 *type = Fio_client_ipv6;
2343 else
2344 *type = Fio_client_socket;
2345 g_free(typeentry);
2346
Jens Axboe62bc9372012-03-07 11:45:07 +01002347 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002348
Jens Axboea7a42ce2012-03-02 13:12:04 +01002349 gtk_widget_destroy(dialog);
2350 return 0;
2351}
2352
Jens Axboe2f99deb2012-03-09 14:37:29 +01002353static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002354{
2355 struct gfio_client *gc;
2356
2357 gc = malloc(sizeof(*gc));
2358 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002359 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002360 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002361
Jens Axboe2f99deb2012-03-09 14:37:29 +01002362 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002363
2364 client->client_data = gc;
2365}
2366
Jens Axboe2f99deb2012-03-09 14:37:29 +01002367static GtkWidget *new_client_page(struct gui_entry *ge);
2368
2369static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2370{
2371 struct gui_entry *ge;
2372
2373 ge = malloc(sizeof(*ge));
2374 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002375 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002376 INIT_FLIST_HEAD(&ge->list);
2377 flist_add_tail(&ge->list, &ui->list);
2378 ge->ui = ui;
2379 return ge;
2380}
2381
Jens Axboe2f99deb2012-03-09 14:37:29 +01002382static struct gui_entry *get_new_ge_with_tab(const char *name)
2383{
2384 struct gui_entry *ge;
2385
2386 ge = alloc_new_gui_entry(&main_ui);
2387
2388 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002389 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002390
2391 ge->page_label = gtk_label_new(name);
2392 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2393
2394 gtk_widget_show_all(main_ui.window);
2395 return ge;
2396}
2397
2398static void file_new(GtkWidget *w, gpointer data)
2399{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002400 struct gui *ui = (struct gui *) data;
2401 struct gui_entry *ge;
2402
2403 ge = get_new_ge_with_tab("Untitled");
2404 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002405}
2406
2407/*
2408 * Return the 'ge' corresponding to the tab. If the active tab is the
2409 * main tab, open a new tab.
2410 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002411static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002412{
2413 struct flist_head *entry;
2414 struct gui_entry *ge;
2415
Jens Axboe38634cb2012-03-13 12:26:41 +01002416 if (!cur_page) {
2417 if (created)
2418 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002419 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002420 }
2421
2422 if (created)
2423 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002424
2425 flist_for_each(entry, &main_ui.list) {
2426 ge = flist_entry(entry, struct gui_entry, list);
2427 if (ge->page_num == cur_page)
2428 return ge;
2429 }
2430
2431 return NULL;
2432}
2433
Jens Axboe85dd01e2012-03-12 14:33:16 +01002434static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2435{
2436 gint cur_page;
2437
2438 /*
2439 * Main tab is tab 0, so any current page other than 0 holds
2440 * a ge entry.
2441 */
2442 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2443 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002444 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002445
2446 return NULL;
2447}
2448
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002449static void file_close(GtkWidget *w, gpointer data)
2450{
2451 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002452 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002453
2454 /*
2455 * Can't close the main tab
2456 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002457 ge = get_ge_from_cur_tab(ui);
2458 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002459 gtk_widget_destroy(ge->vbox);
2460 return;
2461 }
2462
Jens Axboef5c67262012-03-13 08:20:41 +01002463 if (!flist_empty(&ui->list)) {
2464 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2465 return;
2466 }
2467
Jens Axboe0fd18982012-03-14 10:34:48 +01002468 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002469}
2470
Jens Axboe38634cb2012-03-13 12:26:41 +01002471static void file_add_recent(struct gui *ui, const gchar *uri)
2472{
Jens Axboea217ba72012-03-13 20:29:39 +01002473 GtkRecentData grd;
2474
2475 memset(&grd, 0, sizeof(grd));
2476 grd.display_name = strdup("gfio");
2477 grd.description = strdup("Fio job file");
2478 grd.mime_type = strdup(GFIO_MIME);
2479 grd.app_name = strdup(g_get_application_name());
2480 grd.app_exec = strdup("gfio %f/%u");
2481
2482 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002483}
2484
2485static gchar *get_filename_from_uri(const gchar *uri)
2486{
2487 if (strncmp(uri, "file://", 7))
2488 return strdup(uri);
2489
2490 return strdup(uri + 7);
2491}
2492
2493static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2494 int type, int port)
2495{
2496 struct fio_client *client;
2497 gchar *filename;
2498
2499 filename = get_filename_from_uri(uri);
2500
2501 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2502 ge->job_files[ge->nr_job_files] = strdup(filename);
2503 ge->nr_job_files++;
2504
2505 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2506 if (!client) {
2507 GError *error;
2508
2509 error = g_error_new(g_quark_from_string("fio"), 1,
2510 "Failed to add client %s", host);
2511 report_error(error);
2512 g_error_free(error);
2513 return 1;
2514 }
2515
2516 gfio_client_added(ge, client);
2517 file_add_recent(ge->ui, uri);
2518 return 0;
2519}
2520
Jens Axboea6790902012-03-13 15:16:11 +01002521static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002522{
Jens Axboea6790902012-03-13 15:16:11 +01002523 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002524 struct gui_entry *ge;
2525 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002526 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002527 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002528
2529 /*
2530 * Creates new tab if current tab is the main window, or the
2531 * current tab already has a client.
2532 */
2533 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002534 ge = get_ge_from_page(cur_page, &ge_is_new);
2535 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002536 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002537 ge_is_new = 1;
2538 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002539
2540 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002541
Jens Axboea6790902012-03-13 15:16:11 +01002542 if (get_connection_details(&host, &port, &type, &server_start)) {
2543 if (ge_is_new)
2544 gtk_widget_destroy(ge->vbox);
2545
2546 return 1;
2547 }
2548
2549 ret = do_file_open(ge, uri, host, type, port);
2550
2551 free(host);
2552
2553 if (!ret) {
2554 if (server_start)
2555 gfio_start_server();
2556 } else {
2557 if (ge_is_new)
2558 gtk_widget_destroy(ge->vbox);
2559 }
2560
2561 return ret;
2562}
2563
2564static void recent_open(GtkAction *action, gpointer data)
2565{
2566 struct gui *ui = (struct gui *) data;
2567 GtkRecentInfo *info;
2568 const gchar *uri;
2569
2570 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2571 uri = gtk_recent_info_get_uri(info);
2572
2573 do_file_open_with_tab(ui, uri);
2574}
2575
2576static void file_open(GtkWidget *w, gpointer data)
2577{
2578 struct gui *ui = data;
2579 GtkWidget *dialog;
2580 GSList *filenames, *fn_glist;
2581 GtkFileFilter *filter;
2582
Jens Axboe0420ba62012-02-29 11:16:52 +01002583 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002584 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002585 GTK_FILE_CHOOSER_ACTION_OPEN,
2586 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2587 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2588 NULL);
2589 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2590
2591 filter = gtk_file_filter_new();
2592 gtk_file_filter_add_pattern(filter, "*.fio");
2593 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002594 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002595 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002596 gtk_file_filter_set_name(filter, "Fio job file");
2597 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2598
2599 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2600 gtk_widget_destroy(dialog);
2601 return;
2602 }
2603
2604 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002605
2606 gtk_widget_destroy(dialog);
2607
Jens Axboe0420ba62012-02-29 11:16:52 +01002608 filenames = fn_glist;
2609 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002610 if (do_file_open_with_tab(ui, filenames->data))
2611 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002612 filenames = g_slist_next(filenames);
2613 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002614
Jens Axboe0420ba62012-02-29 11:16:52 +01002615 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002616}
2617
2618static void file_save(GtkWidget *w, gpointer data)
2619{
Jens Axboe63a130b2012-03-06 20:08:59 +01002620 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002621 GtkWidget *dialog;
2622
2623 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002624 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002625 GTK_FILE_CHOOSER_ACTION_SAVE,
2626 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2627 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2628 NULL);
2629
2630 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2631 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2632
2633 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2634 char *filename;
2635
2636 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2637 // save_job_file(filename);
2638 g_free(filename);
2639 }
2640 gtk_widget_destroy(dialog);
2641}
2642
Jens Axboe9b260bd2012-03-06 11:02:52 +01002643static void view_log_destroy(GtkWidget *w, gpointer data)
2644{
2645 struct gui *ui = (struct gui *) data;
2646
2647 gtk_widget_ref(ui->log_tree);
2648 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2649 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002650 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002651}
2652
2653static void view_log(GtkWidget *w, gpointer data)
2654{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002655 GtkWidget *win, *scroll, *vbox, *box;
2656 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002657
Jens Axboe4cbe7212012-03-06 13:36:17 +01002658 if (ui->log_view)
2659 return;
2660
2661 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002662 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002663 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002664
Jens Axboe4cbe7212012-03-06 13:36:17 +01002665 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002666
Jens Axboe4cbe7212012-03-06 13:36:17 +01002667 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2668
2669 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2670
2671 box = gtk_hbox_new(TRUE, 0);
2672 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2673 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2674 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2675
2676 vbox = gtk_vbox_new(TRUE, 5);
2677 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2678
2679 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002680 gtk_widget_show_all(win);
2681}
2682
Jens Axboe85dd01e2012-03-12 14:33:16 +01002683static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002684{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002685 struct gui *ui = (struct gui *) data;
2686 struct gui_entry *ge;
2687
2688 ge = get_ge_from_cur_tab(ui);
2689 if (ge)
2690 connect_clicked(w, ge);
2691}
2692
2693static void send_job_entry(GtkWidget *w, gpointer data)
2694{
2695 struct gui *ui = (struct gui *) data;
2696 struct gui_entry *ge;
2697
2698 ge = get_ge_from_cur_tab(ui);
2699 if (ge)
2700 send_clicked(w, ge);
2701
2702}
2703
2704static void edit_job_entry(GtkWidget *w, gpointer data)
2705{
2706}
2707
2708static void start_job_entry(GtkWidget *w, gpointer data)
2709{
2710 struct gui *ui = (struct gui *) data;
2711 struct gui_entry *ge;
2712
2713 ge = get_ge_from_cur_tab(ui);
2714 if (ge)
2715 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002716}
2717
Jens Axboe781ccba2012-03-15 09:44:42 +01002718static void view_results(GtkWidget *w, gpointer data)
2719{
2720 struct gui *ui = (struct gui *) data;
2721 struct gfio_client *gc;
2722 struct gui_entry *ge;
2723
2724 ge = get_ge_from_cur_tab(ui);
2725 if (!ge)
2726 return;
2727
2728 if (ge->results_window)
2729 return;
2730
2731 gc = ge->client;
2732 if (gc && gc->nr_results)
2733 gfio_display_end_results(gc);
2734}
2735
Jens Axboe8577f4f2012-03-09 19:28:27 +01002736static void __update_graph_limits(struct gfio_graphs *g)
2737{
2738 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2739 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2740}
2741
2742static void update_graph_limits(void)
2743{
2744 struct flist_head *entry;
2745 struct gui_entry *ge;
2746
2747 __update_graph_limits(&main_ui.graphs);
2748
2749 flist_for_each(entry, &main_ui.list) {
2750 ge = flist_entry(entry, struct gui_entry, list);
2751 __update_graph_limits(&ge->graphs);
2752 }
2753}
2754
Jens Axboe46974a72012-03-02 19:34:13 +01002755static void preferences(GtkWidget *w, gpointer data)
2756{
Jens Axboef3e84402012-03-07 13:14:32 +01002757 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002758 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002759 int i;
2760
2761 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002762 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002763 GTK_DIALOG_DESTROY_WITH_PARENT,
2764 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2765 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2766 NULL);
2767
Jens Axboe8577f4f2012-03-09 19:28:27 +01002768 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002769 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2770 vbox = gtk_vbox_new(FALSE, 6);
2771 gtk_container_add(GTK_CONTAINER(frame), vbox);
2772
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002773 hbox = gtk_hbox_new(FALSE, 5);
2774 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2775 entry = gtk_label_new("Font face to use for graph labels");
2776 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2777
Jens Axboef3e84402012-03-07 13:14:32 +01002778 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002779 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002780
Jens Axboe8577f4f2012-03-09 19:28:27 +01002781 box = gtk_vbox_new(FALSE, 6);
2782 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2783
2784 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002785 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002786 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2787 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2788
Jens Axboec05d9052012-03-11 13:05:35 +01002789 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002790
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002791 box = gtk_vbox_new(FALSE, 6);
2792 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2793
2794 hbox = gtk_hbox_new(FALSE, 5);
2795 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2796 entry = gtk_label_new("Client ETA request interval (msec)");
2797 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2798
2799 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002800 frame = gtk_frame_new("Debug logging");
2801 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2802 vbox = gtk_vbox_new(FALSE, 6);
2803 gtk_container_add(GTK_CONTAINER(frame), vbox);
2804
2805 box = gtk_hbox_new(FALSE, 6);
2806 gtk_container_add(GTK_CONTAINER(vbox), box);
2807
2808 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2809
2810 for (i = 0; i < FD_DEBUG_MAX; i++) {
2811 if (i == 7) {
2812 box = gtk_hbox_new(FALSE, 6);
2813 gtk_container_add(GTK_CONTAINER(vbox), box);
2814 }
2815
2816
2817 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2818 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2819 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2820 }
2821
Jens Axboe46974a72012-03-02 19:34:13 +01002822 gtk_widget_show_all(dialog);
2823
2824 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2825 gtk_widget_destroy(dialog);
2826 return;
2827 }
2828
2829 for (i = 0; i < FD_DEBUG_MAX; i++) {
2830 int set;
2831
2832 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2833 if (set)
2834 fio_debug |= (1UL << i);
2835 }
2836
Jens Axboef3e84402012-03-07 13:14:32 +01002837 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002838 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2839 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002840 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002841
Jens Axboe46974a72012-03-02 19:34:13 +01002842 gtk_widget_destroy(dialog);
2843}
2844
Jens Axboe0420ba62012-02-29 11:16:52 +01002845static void about_dialog(GtkWidget *w, gpointer data)
2846{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002847 const char *authors[] = {
2848 "Jens Axboe <axboe@kernel.dk>",
2849 "Stephen Carmeron <stephenmcameron@gmail.com>",
2850 NULL
2851 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002852 const char *license[] = {
2853 "Fio is free software; you can redistribute it and/or modify "
2854 "it under the terms of the GNU General Public License as published by "
2855 "the Free Software Foundation; either version 2 of the License, or "
2856 "(at your option) any later version.\n",
2857 "Fio is distributed in the hope that it will be useful, "
2858 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2859 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2860 "GNU General Public License for more details.\n",
2861 "You should have received a copy of the GNU General Public License "
2862 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2863 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2864 };
2865 char *license_trans;
2866
2867 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2868 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002869
Jens Axboe0420ba62012-02-29 11:16:52 +01002870 gtk_show_about_dialog(NULL,
2871 "program-name", "gfio",
2872 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002873 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002874 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2875 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002876 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002877 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002878 "logo-icon-name", "fio",
2879 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002880 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002881 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002882
Jens Axboe2f99deb2012-03-09 14:37:29 +01002883 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002884}
2885
2886static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002887 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002888 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002889 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002890 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002891 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002892 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002893 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2894 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2895 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002896 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01002897 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002898 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2899 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2900 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2901 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002902 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2903 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002904};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002905static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002906
2907static const gchar *ui_string = " \
2908 <ui> \
2909 <menubar name=\"MainMenu\"> \
2910 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002911 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01002912 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002913 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002914 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002915 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002916 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002917 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2918 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002919 <placeholder name=\"FileRecentFiles\"/> \
2920 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002921 <menuitem name=\"Quit\" action=\"Quit\" /> \
2922 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002923 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002924 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002925 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002926 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2927 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002928 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002929 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002930 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002931 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01002932 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2933 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002934 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2935 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002936 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2937 <menuitem name=\"About\" action=\"About\" /> \
2938 </menu> \
2939 </menubar> \
2940 </ui> \
2941";
2942
Jens Axboe4cbe7212012-03-06 13:36:17 +01002943static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2944 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002945{
Jens Axboeca664f42012-03-14 19:49:40 +01002946 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002947 GError *error = 0;
2948
2949 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002950 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002951
2952 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2953 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2954
2955 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002956
Jens Axboe0420ba62012-02-29 11:16:52 +01002957 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2958}
2959
2960void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2961 GtkWidget *vbox, GtkUIManager *ui_manager)
2962{
2963 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2964}
2965
Jens Axboec80b74b2012-03-12 10:23:28 +01002966static void combo_entry_changed(GtkComboBox *box, gpointer data)
2967{
2968 struct gui_entry *ge = (struct gui_entry *) data;
2969 gint index;
2970
2971 index = gtk_combo_box_get_active(box);
2972
2973 multitext_set_entry(&ge->eta.iotype, index);
Jens Axboe99d633a2012-03-15 15:55:04 +01002974 multitext_set_entry(&ge->eta.bs, index);
Jens Axboec80b74b2012-03-12 10:23:28 +01002975 multitext_set_entry(&ge->eta.ioengine, index);
2976 multitext_set_entry(&ge->eta.iodepth, index);
2977}
2978
2979static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2980{
2981 struct gui_entry *ge = (struct gui_entry *) data;
2982
2983 multitext_free(&ge->eta.iotype);
Jens Axboe99d633a2012-03-15 15:55:04 +01002984 multitext_free(&ge->eta.bs);
Jens Axboec80b74b2012-03-12 10:23:28 +01002985 multitext_free(&ge->eta.ioengine);
2986 multitext_free(&ge->eta.iodepth);
2987}
2988
Jens Axboe2f99deb2012-03-09 14:37:29 +01002989static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002990{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002991 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002992 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01002993
Jens Axboe2f99deb2012-03-09 14:37:29 +01002994 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002995
Jens Axboe65476332012-03-13 10:37:04 +01002996 top_align = gtk_alignment_new(0, 0, 1, 0);
2997 top_vbox = gtk_vbox_new(FALSE, 3);
2998 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2999 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003000
Jens Axboe3e47bd22012-02-29 13:45:02 +01003001 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003002 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01003003 probe_frame = gtk_vbox_new(FALSE, 3);
3004 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
3005
3006 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003007 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3008 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
3009 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
3010 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
3011 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01003012
Jens Axboe3e47bd22012-02-29 13:45:02 +01003013 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003014 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3015
Jens Axboe3863d1a2012-03-09 17:39:05 +01003016 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01003017 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
3018 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
3019 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
Jens Axboe99d633a2012-03-15 15:55:04 +01003020 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
Jens Axboec80b74b2012-03-12 10:23:28 +01003021 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
3022 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003023 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
3024 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
3025
3026 probe_box = gtk_hbox_new(FALSE, 3);
3027 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
3028 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3029 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3030 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3031 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
3032
3033 /*
3034 * Only add this if we have a commit rate
3035 */
3036#if 0
3037 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01003038 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01003039
Jens Axboe2f99deb2012-03-09 14:37:29 +01003040 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3041 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3042
3043 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3044 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3045#endif
3046
3047 /*
3048 * Set up a drawing area and IOPS and bandwidth graphs
3049 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01003050 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01003051 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01003052 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003053 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3054 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3055 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3056 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3057 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01003058 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3059 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003060 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01003061 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003062 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01003063 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003064
3065 setup_graphs(&ge->graphs);
3066
3067 /*
3068 * Set up alignments for widgets at the bottom of ui,
3069 * align bottom left, expand horizontally but not vertically
3070 */
Jens Axboe65476332012-03-13 10:37:04 +01003071 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003072 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003073 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3074 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003075
3076 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3077
3078 /*
3079 * Set up thread status progress bar
3080 */
3081 ge->thread_status_pb = gtk_progress_bar_new();
3082 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3083 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3084 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3085
3086
3087 return main_vbox;
3088}
3089
3090static GtkWidget *new_main_page(struct gui *ui)
3091{
3092 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01003093 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003094
3095 main_vbox = gtk_vbox_new(FALSE, 3);
3096
3097 /*
3098 * Set up alignments for widgets at the top of ui,
3099 * align top left, expand horizontally but not vertically
3100 */
Jens Axboe65476332012-03-13 10:37:04 +01003101 top_align = gtk_alignment_new(0, 0, 1, 0);
3102 top_vbox = gtk_vbox_new(FALSE, 0);
3103 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3104 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003105
3106 probe = gtk_frame_new("Run statistics");
3107 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3108 probe_frame = gtk_vbox_new(FALSE, 3);
3109 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01003110
3111 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003112 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01003113 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01003114 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3115 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3116 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3117 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01003118
3119 /*
3120 * Only add this if we have a commit rate
3121 */
3122#if 0
3123 probe_box = gtk_hbox_new(FALSE, 3);
3124 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3125
Jens Axboe3e47bd22012-02-29 13:45:02 +01003126 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3127 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3128
Jens Axboe3e47bd22012-02-29 13:45:02 +01003129 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3130 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01003131#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01003132
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01003133 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003134 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003135 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01003136 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01003137 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01003138 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003139 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3140 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3141 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3142 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3143 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01003144 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3145 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003146 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01003147 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003148 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01003149 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01003150 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003151
Jens Axboe2f99deb2012-03-09 14:37:29 +01003152 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003153
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003154 /*
3155 * Set up alignments for widgets at the bottom of ui,
3156 * align bottom left, expand horizontally but not vertically
3157 */
Jens Axboe65476332012-03-13 10:37:04 +01003158 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003159 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003160 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3161 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003162
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003163 /*
3164 * Set up thread status progress bar
3165 */
3166 ui->thread_status_pb = gtk_progress_bar_new();
3167 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01003168 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003169 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3170
Jens Axboe2f99deb2012-03-09 14:37:29 +01003171 return main_vbox;
3172}
3173
3174static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3175 guint page, gpointer data)
3176
3177{
Jens Axboe02421e62012-03-12 12:05:50 +01003178 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01003179 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01003180
Jens Axboe85dd01e2012-03-12 14:33:16 +01003181 if (!page) {
3182 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01003183 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003184 return TRUE;
3185 }
3186
3187 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01003188 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003189 if (ge)
3190 update_button_states(ui, ge);
3191
Jens Axboe2f99deb2012-03-09 14:37:29 +01003192 return TRUE;
3193}
3194
Jens Axboe38634cb2012-03-13 12:26:41 +01003195static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3196{
3197 time_t time_a = gtk_recent_info_get_visited(a);
3198 time_t time_b = gtk_recent_info_get_visited(b);
3199
3200 return time_b - time_a;
3201}
3202
3203static void add_recent_file_items(struct gui *ui)
3204{
3205 const gchar *gfio = g_get_application_name();
3206 GList *items, *item;
3207 int i = 0;
3208
3209 if (ui->recent_ui_id) {
3210 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3211 gtk_ui_manager_ensure_update(ui->uimanager);
3212 }
3213 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3214
3215 if (ui->actiongroup) {
3216 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3217 g_object_unref(ui->actiongroup);
3218 }
3219 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3220
3221 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3222
3223 items = gtk_recent_manager_get_items(ui->recentmanager);
3224 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3225
3226 for (item = items; item && item->data; item = g_list_next(item)) {
3227 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3228 gchar *action_name;
3229 const gchar *label;
3230 GtkAction *action;
3231
3232 if (!gtk_recent_info_has_application(info, gfio))
3233 continue;
3234
3235 /*
3236 * We only support local files for now
3237 */
3238 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3239 continue;
3240
3241 action_name = g_strdup_printf("RecentFile%u", i++);
3242 label = gtk_recent_info_get_display_name(info);
3243
3244 action = g_object_new(GTK_TYPE_ACTION,
3245 "name", action_name,
3246 "label", label, NULL);
3247
3248 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3249 gtk_recent_info_ref(info),
3250 (GDestroyNotify) gtk_recent_info_unref);
3251
3252
3253 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3254
3255 gtk_action_group_add_action(ui->actiongroup, action);
3256 g_object_unref(action);
3257
3258 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3259 "/MainMenu/FileMenu/FileRecentFiles",
3260 label, action_name,
3261 GTK_UI_MANAGER_MENUITEM, FALSE);
3262
3263 g_free(action_name);
3264
3265 if (i == 8)
3266 break;
3267 }
3268
3269 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3270 g_list_free(items);
3271}
3272
Jens Axboea6790902012-03-13 15:16:11 +01003273static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3274 gint x, gint y, GtkSelectionData *data,
3275 guint info, guint time)
3276{
3277 struct gui *ui = &main_ui;
3278 gchar **uris;
3279 GtkWidget *source;
3280 int i;
3281
3282 source = gtk_drag_get_source_widget(ctx);
3283 if (source && widget == gtk_widget_get_toplevel(source)) {
3284 gtk_drag_finish(ctx, FALSE, FALSE, time);
3285 return;
3286 }
3287
3288 uris = gtk_selection_data_get_uris(data);
3289 if (!uris) {
3290 gtk_drag_finish(ctx, FALSE, FALSE, time);
3291 return;
3292 }
3293
3294 i = 0;
3295 while (uris[i]) {
3296 if (do_file_open_with_tab(ui, uris[i]))
3297 break;
3298 i++;
3299 }
3300
3301 gtk_drag_finish(ctx, TRUE, FALSE, time);
3302 g_strfreev(uris);
3303}
3304
Jens Axboe2f99deb2012-03-09 14:37:29 +01003305static void init_ui(int *argc, char **argv[], struct gui *ui)
3306{
3307 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003308 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003309
3310 /* Magical g*thread incantation, you just need this thread stuff.
3311 * Without it, the update that happens in gfio_update_thread_status
3312 * doesn't really happen in a timely fashion, you need expose events
3313 */
3314 if (!g_thread_supported())
3315 g_thread_init(NULL);
3316 gdk_threads_init();
3317
3318 gtk_init(argc, argv);
3319 settings = gtk_settings_get_default();
3320 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3321 g_type_init();
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003322 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003323
3324 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003325 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003326 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3327
3328 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3329 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3330
3331 ui->vbox = gtk_vbox_new(FALSE, 0);
3332 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3333
Jens Axboe02421e62012-03-12 12:05:50 +01003334 ui->uimanager = gtk_ui_manager_new();
3335 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3336 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003337
Jens Axboe38634cb2012-03-13 12:26:41 +01003338 ui->recentmanager = gtk_recent_manager_get_default();
3339 add_recent_file_items(ui);
3340
Jens Axboe2f99deb2012-03-09 14:37:29 +01003341 ui->notebook = gtk_notebook_new();
3342 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003343 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003344 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003345 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3346
3347 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003348 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3349 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3350 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003351
3352 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3353
Jens Axboe9b260bd2012-03-06 11:02:52 +01003354 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003355
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003356 gtk_widget_show_all(ui->window);
3357}
3358
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003359int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003360{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003361 if (initialize_fio(envp))
3362 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003363 if (fio_init_options())
3364 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003365
Jens Axboe2f99deb2012-03-09 14:37:29 +01003366 memset(&main_ui, 0, sizeof(main_ui));
3367 INIT_FLIST_HEAD(&main_ui.list);
3368
3369 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003370
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003371 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003372 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003373 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003374 return 0;
3375}