blob: ffc66054208c2ce97472ec86be16d1ca5633e6bf [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;
55 const char *tooltiptext;
Jens Axboe3e47bd22012-02-29 13:45:02 +010056 const int start_insensitive;
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 Axboe3e47bd22012-02-29 13:45:02 +010061 { "Connect", connect_clicked, "Connect to host", 0 },
Jens Axboeb9d2f302012-03-08 20:36:28 +010062 { "Send", send_clicked, "Send job description to host", 1 },
63 { "Start Job", start_job_clicked,
Jens Axboe2f99deb2012-03-09 14:37:29 +010064 "Start the current job on the server", 1 },
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;
84 struct multitext_widget ioengine;
85 struct multitext_widget iodepth;
Jens Axboe3e47bd22012-02-29 13:45:02 +010086 GtkWidget *jobs;
87 GtkWidget *files;
88 GtkWidget *read_bw;
89 GtkWidget *read_iops;
90 GtkWidget *cr_bw;
91 GtkWidget *cr_iops;
92 GtkWidget *write_bw;
93 GtkWidget *write_iops;
94 GtkWidget *cw_bw;
95 GtkWidget *cw_iops;
96};
97
Jens Axboe2f99deb2012-03-09 14:37:29 +010098struct gfio_graphs {
99#define DRAWING_AREA_XDIM 1000
100#define DRAWING_AREA_YDIM 400
101 GtkWidget *drawing_area;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100102 struct graph *iops_graph;
103 struct graph *bandwidth_graph;
104};
105
106/*
107 * Main window widgets and data
108 */
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100109struct gui {
Jens Axboe02421e62012-03-12 12:05:50 +0100110 GtkUIManager *uimanager;
Jens Axboe38634cb2012-03-13 12:26:41 +0100111 GtkRecentManager *recentmanager;
112 GtkActionGroup *actiongroup;
113 guint recent_ui_id;
Jens Axboe02421e62012-03-12 12:05:50 +0100114 GtkWidget *menu;
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100115 GtkWidget *window;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100116 GtkWidget *vbox;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100117 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100118 GtkWidget *buttonbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100119 GtkWidget *notebook;
120 GtkWidget *error_info_bar;
121 GtkWidget *error_label;
122 GtkListStore *log_model;
123 GtkWidget *log_tree;
124 GtkWidget *log_view;
125 struct gfio_graphs graphs;
126 struct probe_widget probe;
127 struct eta_widget eta;
128 pthread_t server_t;
129
Jens Axboea9eccde2012-03-09 14:59:42 +0100130 pthread_t t;
131 int handler_running;
132
Jens Axboe2f99deb2012-03-09 14:37:29 +0100133 struct flist_head list;
134} main_ui;
135
Jens Axboe85dd01e2012-03-12 14:33:16 +0100136enum {
137 GE_STATE_NEW = 1,
138 GE_STATE_CONNECTED,
139 GE_STATE_JOB_SENT,
140 GE_STATE_JOB_STARTED,
141 GE_STATE_JOB_RUNNING,
142 GE_STATE_JOB_DONE,
143};
144
Jens Axboe2f99deb2012-03-09 14:37:29 +0100145/*
146 * Notebook entry
147 */
148struct gui_entry {
149 struct flist_head list;
150 struct gui *ui;
151
152 GtkWidget *vbox;
Jens Axboec80b74b2012-03-12 10:23:28 +0100153 GtkWidget *job_notebook;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100154 GtkWidget *thread_status_pb;
155 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100156 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Jens Axboe2f99deb2012-03-09 14:37:29 +0100157 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100158 GtkWidget *error_info_bar;
159 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100160 GtkWidget *results_window;
Jens Axboe7da23b42012-03-15 13:45:02 +0100161 GtkWidget *results_notebook;
Jens Axboe17b97212012-03-14 20:17:57 +0100162 GtkUIManager *results_uimanager;
Jens Axboe17b97212012-03-14 20:17:57 +0100163 GtkWidget *results_menu;
Jens Axboe7da23b42012-03-15 13:45:02 +0100164 GtkWidget *disk_util_vbox;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100165 GtkListStore *log_model;
166 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100167 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100168 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100169 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100170 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100171 GtkWidget *page_label;
172 gint page_num;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100173 unsigned int state;
Jens Axboe0420ba62012-02-29 11:16:52 +0100174
Jens Axboe09d574e2012-03-15 10:45:48 +0100175 struct graph *clat_graph;
176 struct graph *lat_bucket_graph;
177
Jens Axboeb9d2f302012-03-08 20:36:28 +0100178 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100179 int nr_job_files;
180 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100181};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100182
Jens Axboe781ccba2012-03-15 09:44:42 +0100183struct end_results {
184 struct group_run_stats gs;
185 struct thread_stat ts;
186};
187
Jens Axboee0681f32012-03-06 12:14:42 +0100188struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100189 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100190 struct fio_client *client;
Jens Axboe6b79c802012-03-08 10:51:36 +0100191 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100192 unsigned int job_added;
193 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, "");
304 multitext_update_entry(&ge->eta.ioengine, 0, "");
305 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100306 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
309 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
310 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
311 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100312}
313
Jens Axboe3863d1a2012-03-09 17:39:05 +0100314static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
315{
316 GtkWidget *entry, *frame;
317
318 frame = gtk_frame_new(label);
319 entry = gtk_combo_box_new_text();
320 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
321 gtk_container_add(GTK_CONTAINER(frame), entry);
322
323 return entry;
324}
325
Jens Axboe3650a3c2012-03-05 14:09:03 +0100326static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
327{
328 GtkWidget *entry, *frame;
329
330 frame = gtk_frame_new(label);
331 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100332 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100333 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
334 gtk_container_add(GTK_CONTAINER(frame), entry);
335
336 return entry;
337}
338
339static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
340{
341 GtkWidget *label_widget;
342 GtkWidget *frame;
343
344 frame = gtk_frame_new(label);
345 label_widget = gtk_label_new(NULL);
346 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
347 gtk_container_add(GTK_CONTAINER(frame), label_widget);
348
349 return label_widget;
350}
351
352static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
353{
354 GtkWidget *button, *box;
355
356 box = gtk_hbox_new(FALSE, 3);
357 gtk_container_add(GTK_CONTAINER(hbox), box);
358
359 button = gtk_spin_button_new_with_range(min, max, 1.0);
360 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
361
362 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
363 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
364
365 return button;
366}
367
Jens Axboe3650a3c2012-03-05 14:09:03 +0100368static void label_set_int_value(GtkWidget *entry, unsigned int val)
369{
370 char tmp[80];
371
372 sprintf(tmp, "%u", val);
373 gtk_label_set_text(GTK_LABEL(entry), tmp);
374}
375
376static void entry_set_int_value(GtkWidget *entry, unsigned int val)
377{
378 char tmp[80];
379
380 sprintf(tmp, "%u", val);
381 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
382}
383
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100384static void show_info_dialog(struct gui *ui, const char *title,
385 const char *message)
386{
387 GtkWidget *dialog, *content, *label;
388
389 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
390 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
391 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
392
393 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
394 label = gtk_label_new(message);
395 gtk_container_add(GTK_CONTAINER(content), label);
396 gtk_widget_show_all(dialog);
397 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
398 gtk_dialog_run(GTK_DIALOG(dialog));
399 gtk_widget_destroy(dialog);
400}
401
Jens Axboe781ccba2012-03-15 09:44:42 +0100402static void set_menu_entry_text(struct gui *ui, const char *path,
403 const char *text)
404{
405 GtkWidget *w;
406
407 w = gtk_ui_manager_get_widget(ui->uimanager, path);
408 if (w)
409 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
410 else
411 fprintf(stderr, "gfio: can't find path %s\n", path);
412}
413
414
415static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
416{
417 GtkWidget *w;
418
419 w = gtk_ui_manager_get_widget(ui->uimanager, path);
420 if (w)
421 gtk_widget_set_sensitive(w, show);
422 else
423 fprintf(stderr, "gfio: can't find path %s\n", path);
424}
425
426static void set_job_menu_visible(struct gui *ui, int visible)
427{
428 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
429}
430
431static void set_view_results_visible(struct gui *ui, int visible)
432{
433 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
434}
435
Jens Axboe85dd01e2012-03-12 14:33:16 +0100436/*
437 * Update sensitivity of job buttons and job menu items, based on the
438 * state of the client.
439 */
440static void update_button_states(struct gui *ui, struct gui_entry *ge)
441{
442 unsigned int connect_state, send_state, start_state, edit_state;
443 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100444
445 switch (ge->state) {
446 default: {
447 char tmp[80];
448
449 sprintf(tmp, "Bad client state: %u\n", ge->state);
450 show_info_dialog(ui, "Error", tmp);
451 /* fall through to new state */
452 }
453
454 case GE_STATE_NEW:
455 connect_state = 1;
456 edit_state = 0;
457 connect_str = "Connect";
458 send_state = 0;
459 start_state = 0;
460 break;
461 case GE_STATE_CONNECTED:
462 connect_state = 1;
463 edit_state = 0;
464 connect_str = "Disconnect";
465 send_state = 1;
466 start_state = 0;
467 break;
468 case GE_STATE_JOB_SENT:
469 connect_state = 1;
470 edit_state = 0;
471 connect_str = "Disconnect";
472 send_state = 0;
473 start_state = 1;
474 break;
475 case GE_STATE_JOB_STARTED:
476 connect_state = 1;
477 edit_state = 1;
478 connect_str = "Disconnect";
479 send_state = 0;
480 start_state = 1;
481 break;
482 case GE_STATE_JOB_RUNNING:
483 connect_state = 1;
484 edit_state = 0;
485 connect_str = "Disconnect";
486 send_state = 0;
487 start_state = 0;
488 break;
489 case GE_STATE_JOB_DONE:
490 connect_state = 1;
491 edit_state = 0;
492 connect_str = "Connect";
493 send_state = 0;
494 start_state = 0;
495 break;
496 }
497
498 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
499 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
500 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
501 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
502
Jens Axboe781ccba2012-03-15 09:44:42 +0100503 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
504 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100505
Jens Axboe781ccba2012-03-15 09:44:42 +0100506 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
507 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
508 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100509
Jens Axboe781ccba2012-03-15 09:44:42 +0100510 if (ge->client && ge->client->nr_results)
511 set_view_results_visible(ui, 1);
512 else
513 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100514}
515
516static void gfio_set_state(struct gui_entry *ge, unsigned int state)
517{
518 ge->state = state;
519 update_button_states(ge->ui, ge);
520}
521
Jens Axboea2697902012-03-05 16:43:49 +0100522#define ALIGN_LEFT 1
523#define ALIGN_RIGHT 2
524#define INVISIBLE 4
525#define UNSORTABLE 8
526
527GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
528{
529 GtkCellRenderer *renderer;
530 GtkTreeViewColumn *col;
531 double xalign = 0.0; /* left as default */
532 PangoAlignment align;
533 gboolean visible;
534
535 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
536 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
537 PANGO_ALIGN_CENTER;
538 visible = !(flags & INVISIBLE);
539
540 renderer = gtk_cell_renderer_text_new();
541 col = gtk_tree_view_column_new();
542
543 gtk_tree_view_column_set_title(col, title);
544 if (!(flags & UNSORTABLE))
545 gtk_tree_view_column_set_sort_column_id(col, index);
546 gtk_tree_view_column_set_resizable(col, TRUE);
547 gtk_tree_view_column_pack_start(col, renderer, TRUE);
548 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
549 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
550 switch (align) {
551 case PANGO_ALIGN_LEFT:
552 xalign = 0.0;
553 break;
554 case PANGO_ALIGN_CENTER:
555 xalign = 0.5;
556 break;
557 case PANGO_ALIGN_RIGHT:
558 xalign = 1.0;
559 break;
560 }
561 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
562 gtk_tree_view_column_set_visible(col, visible);
563 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
564 return col;
565}
566
Jens Axboe9b260bd2012-03-06 11:02:52 +0100567static void gfio_ui_setup_log(struct gui *ui)
568{
569 GtkTreeSelection *selection;
570 GtkListStore *model;
571 GtkWidget *tree_view;
572
573 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
574
575 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
576 gtk_widget_set_can_focus(tree_view, FALSE);
577
578 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
579 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100580 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
581 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100582
583 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
584 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
585 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100586 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100587
588 ui->log_model = model;
589 ui->log_tree = tree_view;
590}
591
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100592static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
593 fio_fp64_t *plist,
594 unsigned int len,
595 double xdim, double ydim)
596{
597 struct graph *g;
598 int i;
599
600 g = graph_new(xdim, ydim, gfio_graph_font);
601 graph_title(g, title);
602 graph_x_title(g, "Percentile");
603
604 for (i = 0; i < len; i++) {
605 char fbuf[8];
606
607 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
608 graph_add_label(g, fbuf);
609 graph_add_data(g, fbuf, (double) ovals[i]);
610 }
611
612 return g;
613}
614
Jens Axboea2697902012-03-05 16:43:49 +0100615static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
616 fio_fp64_t *plist,
617 unsigned int len,
618 const char *base,
619 unsigned int scale)
620{
621 GType types[FIO_IO_U_LIST_MAX_LEN];
622 GtkWidget *tree_view;
623 GtkTreeSelection *selection;
624 GtkListStore *model;
625 GtkTreeIter iter;
626 int i;
627
628 for (i = 0; i < len; i++)
629 types[i] = G_TYPE_INT;
630
631 model = gtk_list_store_newv(len, types);
632
633 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
634 gtk_widget_set_can_focus(tree_view, FALSE);
635
Jens Axboe661f7412012-03-06 13:55:45 +0100636 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
637 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
638
Jens Axboea2697902012-03-05 16:43:49 +0100639 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
640 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
641
642 for (i = 0; i < len; i++) {
643 char fbuf[8];
644
645 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
646 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
647 }
648
649 gtk_list_store_append(model, &iter);
650
Jens Axboee0681f32012-03-06 12:14:42 +0100651 for (i = 0; i < len; i++) {
652 if (scale)
653 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100654 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100655 }
Jens Axboea2697902012-03-05 16:43:49 +0100656
657 return tree_view;
658}
659
Jens Axboe09d574e2012-03-15 10:45:48 +0100660static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100661{
662 struct graph *g = p;
663 cairo_t *cr;
664
665 cr = gdk_cairo_create(w->window);
666#if 0
667 if (graph_has_tooltips(g)) {
668 g_object_set(w, "has-tooltip", TRUE, NULL);
669 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
670 }
671#endif
672 cairo_set_source_rgb(cr, 0, 0, 0);
673 bar_graph_draw(g, cr);
674 cairo_destroy(cr);
675
676 return FALSE;
677}
678
Jens Axboe09d574e2012-03-15 10:45:48 +0100679static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
680 gpointer data)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100681{
682 struct graph *g = data;
683
684 graph_set_size(g, w->allocation.width, w->allocation.height);
685 graph_set_size(g, w->allocation.width, w->allocation.height);
686 graph_set_position(g, 0, 0);
687 return TRUE;
688}
689
690static void gfio_show_clat_percentiles(struct gfio_client *gc,
691 GtkWidget *vbox, struct thread_stat *ts,
Jens Axboea2697902012-03-05 16:43:49 +0100692 int ddir)
693{
694 unsigned int *io_u_plat = ts->io_u_plat[ddir];
695 unsigned long nr = ts->clat_stat[ddir].samples;
696 fio_fp64_t *plist = ts->percentile_list;
697 unsigned int *ovals, len, minv, maxv, scale_down;
698 const char *base;
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100699 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
Jens Axboe09d574e2012-03-15 10:45:48 +0100700 struct gui_entry *ge = gc->ge;
Jens Axboea2697902012-03-05 16:43:49 +0100701 char tmp[64];
702
703 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
704 if (!len)
705 goto out;
706
707 /*
708 * We default to usecs, but if the value range is such that we
709 * should scale down to msecs, do that.
710 */
711 if (minv > 2000 && maxv > 99999) {
712 scale_down = 1;
713 base = "msec";
714 } else {
715 scale_down = 0;
716 base = "usec";
717 }
718
Jens Axboea2697902012-03-05 16:43:49 +0100719 sprintf(tmp, "Completion percentiles (%s)", base);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100720 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
Jens Axboe09d574e2012-03-15 10:45:48 +0100721 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100722
Jens Axboea2697902012-03-05 16:43:49 +0100723 frame = gtk_frame_new(tmp);
724 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
725
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100726 completion_vbox = gtk_vbox_new(FALSE, 3);
727 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
Jens Axboea2697902012-03-05 16:43:49 +0100728 hbox = gtk_hbox_new(FALSE, 3);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100729 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
730 drawing_area = gtk_drawing_area_new();
731 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
732 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
733 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
Jens Axboe09d574e2012-03-15 10:45:48 +0100734 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
735 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 +0100736
737 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
738out:
739 if (ovals)
740 free(ovals);
741}
742
Jens Axboe3650a3c2012-03-05 14:09:03 +0100743static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
744 unsigned long max, double mean, double dev)
745{
746 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100747 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100748 char *minp, *maxp;
749 char tmp[64];
750
751 if (!usec_to_msec(&min, &max, &mean, &dev))
752 base = "(msec)";
753
754 minp = num2str(min, 6, 1, 0);
755 maxp = num2str(max, 6, 1, 0);
756
Jens Axboe3650a3c2012-03-05 14:09:03 +0100757 sprintf(tmp, "%s %s", name, base);
758 frame = gtk_frame_new(tmp);
759 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
760
Jens Axboe3650a3c2012-03-05 14:09:03 +0100761 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100762 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100763
764 label = new_info_label_in_frame(hbox, "Minimum");
765 gtk_label_set_text(GTK_LABEL(label), minp);
766 label = new_info_label_in_frame(hbox, "Maximum");
767 gtk_label_set_text(GTK_LABEL(label), maxp);
768 label = new_info_label_in_frame(hbox, "Average");
769 sprintf(tmp, "%5.02f", mean);
770 gtk_label_set_text(GTK_LABEL(label), tmp);
771 label = new_info_label_in_frame(hbox, "Standard deviation");
772 sprintf(tmp, "%5.02f", dev);
773 gtk_label_set_text(GTK_LABEL(label), tmp);
774
775 free(minp);
776 free(maxp);
777
778}
779
Jens Axboeca850992012-03-05 20:04:43 +0100780#define GFIO_CLAT 1
781#define GFIO_SLAT 2
782#define GFIO_LAT 4
783
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100784static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
785 struct group_run_stats *rs,
Jens Axboe3650a3c2012-03-05 14:09:03 +0100786 struct thread_stat *ts, int ddir)
787{
788 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100789 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100790 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100791 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100792 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100793 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100794 char *io_p, *bw_p, *iops_p;
795 int i2p;
796
797 if (!ts->runtime[ddir])
798 return;
799
800 i2p = is_power_of_2(rs->kb_base);
801 runt = ts->runtime[ddir];
802
803 bw = (1000 * ts->io_bytes[ddir]) / runt;
804 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
805 bw_p = num2str(bw, 6, 1, i2p);
806
807 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
808 iops_p = num2str(iops, 6, 1, 0);
809
810 box = gtk_hbox_new(FALSE, 3);
811 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
812
813 frame = gtk_frame_new(ddir_label[ddir]);
814 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
815
Jens Axboe0b761302012-03-05 20:44:11 +0100816 main_vbox = gtk_vbox_new(FALSE, 3);
817 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100818
819 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100820 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100821
822 label = new_info_label_in_frame(box, "IO");
823 gtk_label_set_text(GTK_LABEL(label), io_p);
824 label = new_info_label_in_frame(box, "Bandwidth");
825 gtk_label_set_text(GTK_LABEL(label), bw_p);
826 label = new_info_label_in_frame(box, "IOPS");
827 gtk_label_set_text(GTK_LABEL(label), iops_p);
828 label = new_info_label_in_frame(box, "Runtime (msec)");
829 label_set_int_value(label, ts->runtime[ddir]);
830
Jens Axboee0681f32012-03-06 12:14:42 +0100831 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100832 double p_of_agg = 100.0;
833 const char *bw_str = "KB";
834 char tmp[32];
835
836 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100837 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100838 if (p_of_agg > 100.0)
839 p_of_agg = 100.0;
840 }
841
Jens Axboee0681f32012-03-06 12:14:42 +0100842 if (mean[0] > 999999.9) {
843 min[0] /= 1000.0;
844 max[0] /= 1000.0;
845 mean[0] /= 1000.0;
846 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100847 bw_str = "MB";
848 }
849
Jens Axboe0b761302012-03-05 20:44:11 +0100850 sprintf(tmp, "Bandwidth (%s)", bw_str);
851 frame = gtk_frame_new(tmp);
852 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100853
Jens Axboe0b761302012-03-05 20:44:11 +0100854 box = gtk_hbox_new(FALSE, 3);
855 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100856
Jens Axboe0b761302012-03-05 20:44:11 +0100857 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100858 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100859 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100860 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100861 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100862 sprintf(tmp, "%3.2f%%", p_of_agg);
863 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100864 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100865 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100866 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100867 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100868 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100869 gtk_label_set_text(GTK_LABEL(label), tmp);
870 }
871
Jens Axboee0681f32012-03-06 12:14:42 +0100872 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100873 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100874 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100875 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100876 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100877 flags |= GFIO_LAT;
878
879 if (flags) {
880 frame = gtk_frame_new("Latency");
881 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
882
883 vbox = gtk_vbox_new(FALSE, 3);
884 gtk_container_add(GTK_CONTAINER(frame), vbox);
885
886 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100887 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100888 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100889 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100890 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100891 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100892 }
893
894 if (ts->clat_percentiles)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100895 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
Jens Axboe2b089892012-03-06 08:09:17 +0100896
Jens Axboe3650a3c2012-03-05 14:09:03 +0100897 free(io_p);
898 free(bw_p);
899 free(iops_p);
900}
901
Jens Axboe09d574e2012-03-15 10:45:48 +0100902static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
903 const char **labels,
904 unsigned int len,
905 double xdim, double ydim)
906{
907 struct graph *g;
908 int i;
909
910 g = graph_new(xdim, ydim, gfio_graph_font);
911 graph_title(g, title);
912 graph_x_title(g, "Buckets");
913
914 for (i = 0; i < len; i++) {
915 graph_add_label(g, labels[i]);
916 graph_add_data(g, labels[i], lat[i]);
917 }
918
919 return g;
920}
921
922static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
923 int num)
Jens Axboee5bd1342012-03-05 21:38:12 +0100924{
925 GtkWidget *tree_view;
926 GtkTreeSelection *selection;
927 GtkListStore *model;
928 GtkTreeIter iter;
929 GType *types;
Jens Axboe09d574e2012-03-15 10:45:48 +0100930 int i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100931
932 types = malloc(num * sizeof(GType));
933
934 for (i = 0; i < num; i++)
935 types[i] = G_TYPE_STRING;
936
937 model = gtk_list_store_newv(num, types);
938 free(types);
939 types = NULL;
940
941 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
942 gtk_widget_set_can_focus(tree_view, FALSE);
943
Jens Axboe661f7412012-03-06 13:55:45 +0100944 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
945 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
946
Jens Axboee5bd1342012-03-05 21:38:12 +0100947 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
948 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
949
950 for (i = 0; i < num; i++)
951 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
952
953 gtk_list_store_append(model, &iter);
954
955 for (i = 0; i < num; i++) {
956 char fbuf[32];
957
958 if (lat[i] <= 0.0)
959 sprintf(fbuf, "0.00");
960 else
961 sprintf(fbuf, "%3.2f%%", lat[i]);
962
963 gtk_list_store_set(model, &iter, i, fbuf, -1);
964 }
965
966 return tree_view;
967}
968
Jens Axboe09d574e2012-03-15 10:45:48 +0100969static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
970 struct thread_stat *ts)
Jens Axboee5bd1342012-03-05 21:38:12 +0100971{
Jens Axboe09d574e2012-03-15 10:45:48 +0100972 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
973 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
974 "250u", "500u", "750u", "1m", "2m",
975 "4m", "10m", "20m", "50m", "100m",
976 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
977 int start, end, i;
978 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
979 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
980 struct gui_entry *ge = gc->ge;
Jens Axboee5bd1342012-03-05 21:38:12 +0100981
Jens Axboe09d574e2012-03-15 10:45:48 +0100982 stat_calc_lat_u(ts, io_u_lat);
983 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
Jens Axboee5bd1342012-03-05 21:38:12 +0100984
Jens Axboe09d574e2012-03-15 10:45:48 +0100985 /*
986 * Found out which first bucket has entries, and which last bucket
987 */
988 start = end = -1U;
989 for (i = 0; i < total; i++) {
990 if (io_u_lat[i] == 0.00)
991 continue;
Jens Axboee5bd1342012-03-05 21:38:12 +0100992
Jens Axboe09d574e2012-03-15 10:45:48 +0100993 if (start == -1U)
994 start = i;
995 end = i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100996 }
997
Jens Axboe09d574e2012-03-15 10:45:48 +0100998 /*
999 * No entries...
1000 */
1001 if (start == -1U)
1002 return;
1003
1004 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1005 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 +01001006
Jens Axboe09d574e2012-03-15 10:45:48 +01001007 frame = gtk_frame_new("Latency buckets");
1008 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1009
1010 completion_vbox = gtk_vbox_new(FALSE, 3);
1011 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1012 hbox = gtk_hbox_new(FALSE, 3);
1013 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1014
1015 drawing_area = gtk_drawing_area_new();
1016 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1017 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1018 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1019 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1020 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1021
1022 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
Jens Axboee5bd1342012-03-05 21:38:12 +01001023}
1024
Jens Axboe2e331012012-03-05 22:07:54 +01001025static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1026{
1027 GtkWidget *box, *frame, *entry;
1028 double usr_cpu, sys_cpu;
1029 unsigned long runtime;
1030 char tmp[32];
1031
1032 runtime = ts->total_run_time;
1033 if (runtime) {
1034 double runt = (double) runtime;
1035
1036 usr_cpu = (double) ts->usr_time * 100 / runt;
1037 sys_cpu = (double) ts->sys_time * 100 / runt;
1038 } else {
1039 usr_cpu = 0;
1040 sys_cpu = 0;
1041 }
1042
1043 frame = gtk_frame_new("OS resources");
1044 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1045
1046 box = gtk_hbox_new(FALSE, 3);
1047 gtk_container_add(GTK_CONTAINER(frame), box);
1048
1049 entry = new_info_entry_in_frame(box, "User CPU");
1050 sprintf(tmp, "%3.2f%%", usr_cpu);
1051 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1052 entry = new_info_entry_in_frame(box, "System CPU");
1053 sprintf(tmp, "%3.2f%%", sys_cpu);
1054 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1055 entry = new_info_entry_in_frame(box, "Context switches");
1056 entry_set_int_value(entry, ts->ctx);
1057 entry = new_info_entry_in_frame(box, "Major faults");
1058 entry_set_int_value(entry, ts->majf);
1059 entry = new_info_entry_in_frame(box, "Minor faults");
1060 entry_set_int_value(entry, ts->minf);
1061}
Jens Axboe19998db2012-03-06 09:17:59 +01001062static void gfio_add_sc_depths_tree(GtkListStore *model,
1063 struct thread_stat *ts, unsigned int len,
1064 int submit)
1065{
1066 double io_u_dist[FIO_IO_U_MAP_NR];
1067 GtkTreeIter iter;
1068 /* Bits 0, and 3-8 */
1069 const int add_mask = 0x1f9;
1070 int i, j;
1071
1072 if (submit)
1073 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1074 else
1075 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1076
1077 gtk_list_store_append(model, &iter);
1078
1079 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1080
1081 for (i = 1, j = 0; i < len; i++) {
1082 char fbuf[32];
1083
1084 if (!(add_mask & (1UL << (i - 1))))
1085 sprintf(fbuf, "0.0%%");
1086 else {
1087 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1088 j++;
1089 }
1090
1091 gtk_list_store_set(model, &iter, i, fbuf, -1);
1092 }
1093
1094}
1095
1096static void gfio_add_total_depths_tree(GtkListStore *model,
1097 struct thread_stat *ts, unsigned int len)
1098{
1099 double io_u_dist[FIO_IO_U_MAP_NR];
1100 GtkTreeIter iter;
1101 /* Bits 1-6, and 8 */
1102 const int add_mask = 0x17e;
1103 int i, j;
1104
1105 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1106
1107 gtk_list_store_append(model, &iter);
1108
1109 gtk_list_store_set(model, &iter, 0, "Total", -1);
1110
1111 for (i = 1, j = 0; i < len; i++) {
1112 char fbuf[32];
1113
1114 if (!(add_mask & (1UL << (i - 1))))
1115 sprintf(fbuf, "0.0%%");
1116 else {
1117 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1118 j++;
1119 }
1120
1121 gtk_list_store_set(model, &iter, i, fbuf, -1);
1122 }
1123
1124}
Jens Axboe2e331012012-03-05 22:07:54 +01001125
1126static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1127{
Jens Axboe2e331012012-03-05 22:07:54 +01001128 GtkWidget *frame, *box, *tree_view;
1129 GtkTreeSelection *selection;
1130 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +01001131 GType types[FIO_IO_U_MAP_NR + 1];
1132 int i;
Jens Axboe19998db2012-03-06 09:17:59 +01001133#define NR_LABELS 10
1134 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +01001135
1136 frame = gtk_frame_new("IO depths");
1137 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1138
1139 box = gtk_hbox_new(FALSE, 3);
1140 gtk_container_add(GTK_CONTAINER(frame), box);
1141
Jens Axboe19998db2012-03-06 09:17:59 +01001142 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001143 types[i] = G_TYPE_STRING;
1144
Jens Axboe19998db2012-03-06 09:17:59 +01001145 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +01001146
1147 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1148 gtk_widget_set_can_focus(tree_view, FALSE);
1149
Jens Axboe661f7412012-03-06 13:55:45 +01001150 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1151 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1152
Jens Axboe2e331012012-03-05 22:07:54 +01001153 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1154 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1155
Jens Axboe19998db2012-03-06 09:17:59 +01001156 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001157 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1158
Jens Axboe19998db2012-03-06 09:17:59 +01001159 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1160 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1161 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001162
1163 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1164}
1165
Jens Axboef9d40b42012-03-06 09:52:49 +01001166static gboolean results_window_delete(GtkWidget *w, gpointer data)
1167{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001168 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001169
1170 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001171 ge->results_window = NULL;
1172 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001173 return TRUE;
1174}
1175
Jens Axboe17b97212012-03-14 20:17:57 +01001176static void results_close(GtkWidget *w, gpointer *data)
1177{
1178 struct gui_entry *ge = (struct gui_entry *) data;
1179
1180 gtk_widget_destroy(ge->results_window);
1181}
1182
1183static GtkActionEntry results_menu_items[] = {
1184 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1185 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1186 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1187};
1188static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1189
1190static const gchar *results_ui_string = " \
1191 <ui> \
1192 <menubar name=\"MainMenu\"> \
1193 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1194 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1195 </menu> \
1196 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1197 </menu>\
1198 </menubar> \
1199 </ui> \
1200";
1201
1202static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1203{
1204 GtkActionGroup *action_group;
1205 GtkWidget *widget;
1206 GError *error = 0;
1207
1208 ge->results_uimanager = gtk_ui_manager_new();
1209
1210 action_group = gtk_action_group_new("ResultsMenu");
1211 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1212
1213 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1214 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1215
1216 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1217
1218 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1219 return widget;
1220}
1221
Jens Axboe2f99deb2012-03-09 14:37:29 +01001222static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001223{
Jens Axboe17b97212012-03-14 20:17:57 +01001224 GtkWidget *win, *notebook, *vbox;
Jens Axboef9d40b42012-03-06 09:52:49 +01001225
Jens Axboe2f99deb2012-03-09 14:37:29 +01001226 if (ge->results_window)
1227 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001228
1229 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1230 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001231 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001232 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1233 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001234
Jens Axboe17b97212012-03-14 20:17:57 +01001235 vbox = gtk_vbox_new(FALSE, 0);
1236 gtk_container_add(GTK_CONTAINER(win), vbox);
1237
1238 ge->results_menu = get_results_menubar(win, ge);
1239 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1240
Jens Axboef9d40b42012-03-06 09:52:49 +01001241 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001242 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1243 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboe17b97212012-03-14 20:17:57 +01001244 gtk_container_add(GTK_CONTAINER(vbox), notebook);
Jens Axboef9d40b42012-03-06 09:52:49 +01001245
Jens Axboe2f99deb2012-03-09 14:37:29 +01001246 ge->results_window = win;
1247 ge->results_notebook = notebook;
1248 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001249}
1250
Jens Axboe7da23b42012-03-15 13:45:02 +01001251static void disk_util_destroy(GtkWidget *w, gpointer data)
Jens Axboe3650a3c2012-03-05 14:09:03 +01001252{
Jens Axboe7da23b42012-03-15 13:45:02 +01001253 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001254
Jens Axboe7da23b42012-03-15 13:45:02 +01001255 ge->disk_util_vbox = NULL;
1256 gtk_widget_destroy(w);
Jens Axboe781ccba2012-03-15 09:44:42 +01001257}
Jens Axboe3650a3c2012-03-05 14:09:03 +01001258
Jens Axboe7da23b42012-03-15 13:45:02 +01001259static int __gfio_disk_util_show(GtkWidget *res_notebook,
1260 struct gfio_client *gc, struct cmd_du_pdu *p)
Jens Axboe781ccba2012-03-15 09:44:42 +01001261{
Jens Axboee0681f32012-03-06 12:14:42 +01001262 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe7da23b42012-03-15 13:45:02 +01001263 struct gui_entry *ge = gc->ge;
Jens Axboe604cfe32012-03-07 19:51:36 +01001264 double util;
1265 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001266
Jens Axboe7da23b42012-03-15 13:45:02 +01001267 res_notebook = get_results_window(ge);
Jens Axboee0681f32012-03-06 12:14:42 +01001268
Jens Axboe7da23b42012-03-15 13:45:02 +01001269 if (!ge->disk_util_vbox) {
Jens Axboe60d09292012-03-15 12:37:35 +01001270 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe7da23b42012-03-15 13:45:02 +01001271 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), vbox, gtk_label_new("Disk utilization"));
1272 ge->disk_util_vbox = vbox;
1273 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
Jens Axboee0681f32012-03-06 12:14:42 +01001274 }
1275
1276 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe7da23b42012-03-15 13:45:02 +01001277 gtk_container_add(GTK_CONTAINER(ge->disk_util_vbox), vbox);
Jens Axboee0681f32012-03-06 12:14:42 +01001278
1279 frame = gtk_frame_new((char *) p->dus.name);
1280 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1281
1282 box = gtk_vbox_new(FALSE, 3);
1283 gtk_container_add(GTK_CONTAINER(frame), box);
1284
1285 frame = gtk_frame_new("Read");
1286 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1287 vbox = gtk_hbox_new(TRUE, 3);
1288 gtk_container_add(GTK_CONTAINER(frame), vbox);
1289 entry = new_info_entry_in_frame(vbox, "IOs");
1290 entry_set_int_value(entry, p->dus.ios[0]);
1291 entry = new_info_entry_in_frame(vbox, "Merges");
1292 entry_set_int_value(entry, p->dus.merges[0]);
1293 entry = new_info_entry_in_frame(vbox, "Sectors");
1294 entry_set_int_value(entry, p->dus.sectors[0]);
1295 entry = new_info_entry_in_frame(vbox, "Ticks");
1296 entry_set_int_value(entry, p->dus.ticks[0]);
1297
1298 frame = gtk_frame_new("Write");
1299 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1300 vbox = gtk_hbox_new(TRUE, 3);
1301 gtk_container_add(GTK_CONTAINER(frame), vbox);
1302 entry = new_info_entry_in_frame(vbox, "IOs");
1303 entry_set_int_value(entry, p->dus.ios[1]);
1304 entry = new_info_entry_in_frame(vbox, "Merges");
1305 entry_set_int_value(entry, p->dus.merges[1]);
1306 entry = new_info_entry_in_frame(vbox, "Sectors");
1307 entry_set_int_value(entry, p->dus.sectors[1]);
1308 entry = new_info_entry_in_frame(vbox, "Ticks");
1309 entry_set_int_value(entry, p->dus.ticks[1]);
1310
1311 frame = gtk_frame_new("Shared");
1312 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1313 vbox = gtk_hbox_new(TRUE, 3);
1314 gtk_container_add(GTK_CONTAINER(frame), vbox);
1315 entry = new_info_entry_in_frame(vbox, "IO ticks");
1316 entry_set_int_value(entry, p->dus.io_ticks);
1317 entry = new_info_entry_in_frame(vbox, "Time in queue");
1318 entry_set_int_value(entry, p->dus.time_in_queue);
1319
Jens Axboe604cfe32012-03-07 19:51:36 +01001320 util = 0.0;
1321 if (p->dus.msec)
1322 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1323 if (util > 100.0)
1324 util = 100.0;
1325
1326 sprintf(tmp, "%3.2f%%", util);
1327 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1328 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1329
Jens Axboe7da23b42012-03-15 13:45:02 +01001330 gtk_widget_show_all(ge->results_window);
1331 return 0;
1332}
1333
1334static int gfio_disk_util_show(struct gfio_client *gc)
1335{
1336 struct gui_entry *ge = gc->ge;
1337 GtkWidget *res_notebook;
1338 int i;
1339
1340 if (!gc->nr_du)
1341 return 1;
1342
1343 res_notebook = get_results_window(ge);
1344
1345 for (i = 0; i < gc->nr_du; i++) {
1346 struct cmd_du_pdu *p = &gc->du[i];
1347
1348 __gfio_disk_util_show(res_notebook, gc, p);
1349 }
1350
1351 gtk_widget_show_all(ge->results_window);
1352 return 0;
1353}
1354
1355static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1356 struct group_run_stats *rs)
1357{
1358 unsigned int nr = gc->nr_results;
1359
1360 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1361 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1362 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1363 gc->nr_results++;
1364}
1365
1366static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1367 struct thread_stat *ts,
1368 struct group_run_stats *rs)
1369{
1370 GtkWidget *box, *vbox, *entry, *scroll;
1371
1372 scroll = gtk_scrolled_window_new(NULL, NULL);
1373 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1374 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1375
1376 vbox = gtk_vbox_new(FALSE, 3);
1377
1378 box = gtk_hbox_new(FALSE, 0);
1379 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1380
1381 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1382
1383 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1384
1385 entry = new_info_entry_in_frame(box, "Name");
1386 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1387 if (strlen(ts->description)) {
1388 entry = new_info_entry_in_frame(box, "Description");
1389 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1390 }
1391 entry = new_info_entry_in_frame(box, "Group ID");
1392 entry_set_int_value(entry, ts->groupid);
1393 entry = new_info_entry_in_frame(box, "Jobs");
1394 entry_set_int_value(entry, ts->members);
1395 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1396 entry_set_int_value(entry, ts->error);
1397 entry = new_info_entry_in_frame(box, "PID");
1398 entry_set_int_value(entry, ts->pid);
1399
1400 if (ts->io_bytes[DDIR_READ])
1401 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1402 if (ts->io_bytes[DDIR_WRITE])
1403 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1404
1405 gfio_show_latency_buckets(gc, vbox, ts);
1406 gfio_show_cpu_usage(vbox, ts);
1407 gfio_show_io_depths(vbox, ts);
1408}
1409
1410static void gfio_display_end_results(struct gfio_client *gc)
1411{
1412 struct gui_entry *ge = gc->ge;
1413 GtkWidget *res_notebook;
1414 int i;
1415
1416 res_notebook = get_results_window(ge);
1417
1418 for (i = 0; i < gc->nr_results; i++) {
1419 struct end_results *e = &gc->results[i];
1420
1421 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1422 }
1423
1424 if (gfio_disk_util_show(gc))
1425 gtk_widget_show_all(ge->results_window);
1426}
1427
1428static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1429 struct group_run_stats *rs)
1430{
1431 struct gfio_client *gc = client->client_data;
1432
1433 gfio_add_end_results(gc, ts, rs);
1434
1435 gdk_threads_enter();
1436 gfio_display_end_results(gc);
1437 gdk_threads_leave();
1438}
1439
1440static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1441{
1442 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1443 struct gui *ui = &main_ui;
1444 GtkTreeIter iter;
1445 struct tm *tm;
1446 time_t sec;
1447 char tmp[64], timebuf[80];
1448
1449 sec = p->log_sec;
1450 tm = localtime(&sec);
1451 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1452 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1453
1454 gdk_threads_enter();
1455
1456 gtk_list_store_append(ui->log_model, &iter);
1457 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1458 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1459 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1460 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1461
1462 if (p->level == FIO_LOG_ERR)
1463 view_log(NULL, (gpointer) ui);
1464
1465 gdk_threads_leave();
1466}
1467
1468static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1469{
1470 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1471 struct gfio_client *gc = client->client_data;
1472 unsigned int nr = gc->nr_du;
1473
1474 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1475 memcpy(&gc->du[nr], p, sizeof(*p));
1476 gc->nr_du++;
1477
1478 gdk_threads_enter();
1479 gfio_disk_util_show(gc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001480 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001481}
1482
Jens Axboe3650a3c2012-03-05 14:09:03 +01001483extern int sum_stat_clients;
1484extern struct thread_stat client_ts;
1485extern struct group_run_stats client_gs;
1486
1487static int sum_stat_nr;
1488
Jens Axboe89e5fad2012-03-05 09:21:12 +01001489static void gfio_thread_status_op(struct fio_client *client,
1490 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001491{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001492 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1493
1494 gfio_display_ts(client, &p->ts, &p->rs);
1495
1496 if (sum_stat_clients == 1)
1497 return;
1498
1499 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1500 sum_group_stats(&client_gs, &p->rs);
1501
1502 client_ts.members++;
Jens Axboe2f122b12012-03-15 13:10:19 +01001503 client_ts.thread_number = p->ts.thread_number;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001504 client_ts.groupid = p->ts.groupid;
1505
1506 if (++sum_stat_nr == sum_stat_clients) {
1507 strcpy(client_ts.name, "All clients");
1508 gfio_display_ts(client, &client_ts, &client_gs);
1509 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001510}
1511
Jens Axboe89e5fad2012-03-05 09:21:12 +01001512static void gfio_group_stats_op(struct fio_client *client,
1513 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001514{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001515 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001516}
1517
Jens Axboe2f99deb2012-03-09 14:37:29 +01001518static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1519 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001520{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001521 struct gfio_graphs *g = data;
1522
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001523 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1524 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1525 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1526 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001527 return TRUE;
1528}
1529
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001530static void draw_graph(struct graph *g, cairo_t *cr)
1531{
1532 line_graph_draw(g, cr);
1533 cairo_stroke(cr);
1534}
1535
Jens Axboe93e2db22012-03-13 09:45:22 +01001536static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1537 gboolean keyboard_mode, GtkTooltip *tooltip,
1538 gpointer data)
1539{
1540 struct gfio_graphs *g = data;
1541 const char *text = NULL;
1542
1543 if (graph_contains_xy(g->iops_graph, x, y))
1544 text = graph_find_tooltip(g->iops_graph, x, y);
1545 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1546 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1547
1548 if (text) {
1549 gtk_tooltip_set_text(tooltip, text);
1550 return TRUE;
1551 }
1552
1553 return FALSE;
1554}
1555
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001556static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1557{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001558 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001559 cairo_t *cr;
1560
1561 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001562
1563 if (graph_has_tooltips(g->iops_graph) ||
1564 graph_has_tooltips(g->bandwidth_graph)) {
1565 g_object_set(w, "has-tooltip", TRUE, NULL);
1566 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1567 }
1568
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001569 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001570 draw_graph(g->iops_graph, cr);
1571 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001572 cairo_destroy(cr);
1573
1574 return FALSE;
1575}
1576
Jens Axboe2f99deb2012-03-09 14:37:29 +01001577/*
1578 * Client specific ETA
1579 */
1580static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001581{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001582 struct gfio_client *gc = client->client_data;
1583 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001584 static int eta_good;
1585 char eta_str[128];
1586 char output[256];
1587 char tmp[32];
1588 double perc = 0.0;
1589 int i2p = 0;
1590
Jens Axboe0050e5f2012-03-06 09:23:27 +01001591 gdk_threads_enter();
1592
Jens Axboe3e47bd22012-02-29 13:45:02 +01001593 eta_str[0] = '\0';
1594 output[0] = '\0';
1595
1596 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1597 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1598 eta_to_str(eta_str, je->eta_sec);
1599 }
1600
1601 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001602 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001603 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001604 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001605
1606#if 0
1607 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1608 if (je->m_rate || je->t_rate) {
1609 char *tr, *mr;
1610
1611 mr = num2str(je->m_rate, 4, 0, i2p);
1612 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001613 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001614 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1615 free(tr);
1616 free(mr);
1617 } else if (je->m_iops || je->t_iops)
1618 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001619
Jens Axboe2f99deb2012-03-09 14:37:29 +01001620 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1621 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1622 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1623 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001624#endif
1625
1626 if (je->eta_sec != INT_MAX && je->nr_running) {
1627 char *iops_str[2];
1628 char *rate_str[2];
1629
1630 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1631 strcpy(output, "-.-% done");
1632 else {
1633 eta_good = 1;
1634 perc *= 100.0;
1635 sprintf(output, "%3.1f%% done", perc);
1636 }
1637
1638 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1639 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1640
1641 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1642 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1643
Jens Axboe2f99deb2012-03-09 14:37:29 +01001644 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1645 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1646 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1647 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001648
Jens Axboe93e2db22012-03-13 09:45:22 +01001649 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1650 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1651 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1652 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 +01001653
1654 free(rate_str[0]);
1655 free(rate_str[1]);
1656 free(iops_str[0]);
1657 free(iops_str[1]);
1658 }
1659
1660 if (eta_str[0]) {
1661 char *dst = output + strlen(output);
1662
1663 sprintf(dst, " - %s", eta_str);
1664 }
1665
Jens Axboe9988ca72012-03-09 15:14:06 +01001666 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001667 gdk_threads_leave();
1668}
1669
1670/*
1671 * Update ETA in main window for all clients
1672 */
1673static void gfio_update_all_eta(struct jobs_eta *je)
1674{
1675 struct gui *ui = &main_ui;
1676 static int eta_good;
1677 char eta_str[128];
1678 char output[256];
1679 double perc = 0.0;
1680 int i2p = 0;
1681
1682 gdk_threads_enter();
1683
1684 eta_str[0] = '\0';
1685 output[0] = '\0';
1686
1687 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1688 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1689 eta_to_str(eta_str, je->eta_sec);
1690 }
1691
1692#if 0
1693 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1694 if (je->m_rate || je->t_rate) {
1695 char *tr, *mr;
1696
1697 mr = num2str(je->m_rate, 4, 0, i2p);
1698 tr = num2str(je->t_rate, 4, 0, i2p);
1699 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1700 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1701 free(tr);
1702 free(mr);
1703 } else if (je->m_iops || je->t_iops)
1704 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1705
1706 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1707 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1708 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1709 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1710#endif
1711
Jens Axboe3863d1a2012-03-09 17:39:05 +01001712 entry_set_int_value(ui->eta.jobs, je->nr_running);
1713
Jens Axboe2f99deb2012-03-09 14:37:29 +01001714 if (je->eta_sec != INT_MAX && je->nr_running) {
1715 char *iops_str[2];
1716 char *rate_str[2];
1717
1718 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1719 strcpy(output, "-.-% done");
1720 else {
1721 eta_good = 1;
1722 perc *= 100.0;
1723 sprintf(output, "%3.1f%% done", perc);
1724 }
1725
1726 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1727 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1728
1729 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1730 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1731
1732 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1733 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1734 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1735 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1736
Jens Axboe93e2db22012-03-13 09:45:22 +01001737 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1738 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1739 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1740 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 +01001741
Jens Axboe3e47bd22012-02-29 13:45:02 +01001742 free(rate_str[0]);
1743 free(rate_str[1]);
1744 free(iops_str[0]);
1745 free(iops_str[1]);
1746 }
1747
1748 if (eta_str[0]) {
1749 char *dst = output + strlen(output);
1750
1751 sprintf(dst, " - %s", eta_str);
1752 }
1753
Jens Axboe9988ca72012-03-09 15:14:06 +01001754 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001755 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001756}
1757
Stephen M. Camerona1820202012-02-24 08:17:31 +01001758static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1759{
Jens Axboe843ad232012-02-29 11:44:53 +01001760 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001761 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001762 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001763 const char *os, *arch;
1764 char buf[64];
1765
1766 os = fio_get_os_string(probe->os);
1767 if (!os)
1768 os = "unknown";
1769
1770 arch = fio_get_arch_string(probe->arch);
1771 if (!arch)
1772 os = "unknown";
1773
1774 if (!client->name)
1775 client->name = strdup((char *) probe->hostname);
1776
Jens Axboe0050e5f2012-03-06 09:23:27 +01001777 gdk_threads_enter();
1778
Jens Axboe2f99deb2012-03-09 14:37:29 +01001779 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1780 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1781 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001782 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001783 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001784
Jens Axboe85dd01e2012-03-12 14:33:16 +01001785 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001786
1787 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001788}
1789
Jens Axboe9988ca72012-03-09 15:14:06 +01001790static void gfio_update_thread_status(struct gui_entry *ge,
1791 char *status_message, double perc)
1792{
1793 static char message[100];
1794 const char *m = message;
1795
1796 strncpy(message, status_message, sizeof(message) - 1);
1797 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1798 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1799 gtk_widget_queue_draw(main_ui.window);
1800}
1801
1802static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001803{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001804 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001805 static char message[100];
1806 const char *m = message;
1807
1808 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001809 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1810 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1811 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001812}
1813
Jens Axboe35c0ba72012-03-14 10:56:40 +01001814static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001815{
Jens Axboee0681f32012-03-06 12:14:42 +01001816 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001817
Jens Axboe0050e5f2012-03-06 09:23:27 +01001818 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001819 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001820 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001821}
1822
Jens Axboe807f9972012-03-02 10:25:24 +01001823static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1824{
1825 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001826 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001827 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001828 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001829 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001830
Jens Axboe2f122b12012-03-15 13:10:19 +01001831 p->thread_number = le32_to_cpu(p->thread_number);
1832 p->groupid = le32_to_cpu(p->groupid);
Jens Axboedcaeb602012-03-08 19:45:37 +01001833 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001834
Jens Axboe0050e5f2012-03-06 09:23:27 +01001835 gdk_threads_enter();
1836
Jens Axboe2f99deb2012-03-09 14:37:29 +01001837 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1838
Jens Axboe3863d1a2012-03-09 17:39:05 +01001839 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1840 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1841
Jens Axboec80b74b2012-03-12 10:23:28 +01001842 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1843 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001844
Jens Axboedcaeb602012-03-08 19:45:37 +01001845 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001846 multitext_add_entry(&ge->eta.iodepth, tmp);
1847
1848 multitext_set_entry(&ge->eta.iotype, 0);
1849 multitext_set_entry(&ge->eta.ioengine, 0);
1850 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001851
Jens Axboedcaeb602012-03-08 19:45:37 +01001852 gc->job_added++;
1853
Jens Axboe85dd01e2012-03-12 14:33:16 +01001854 gfio_set_state(ge, GE_STATE_JOB_SENT);
1855
Jens Axboe0050e5f2012-03-06 09:23:27 +01001856 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001857}
1858
Jens Axboeed727a42012-03-02 12:14:40 +01001859static void gfio_client_timed_out(struct fio_client *client)
1860{
Jens Axboee0681f32012-03-06 12:14:42 +01001861 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001862 char buf[256];
1863
1864 gdk_threads_enter();
1865
Jens Axboe85dd01e2012-03-12 14:33:16 +01001866 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001867 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001868
1869 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001870 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001871
1872 gdk_threads_leave();
1873}
1874
Jens Axboe6b79c802012-03-08 10:51:36 +01001875static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1876{
1877 struct gfio_client *gc = client->client_data;
1878
1879 gdk_threads_enter();
1880
Jens Axboe85dd01e2012-03-12 14:33:16 +01001881 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001882
1883 if (gc->err_entry)
1884 entry_set_int_value(gc->err_entry, client->error);
1885
1886 gdk_threads_leave();
1887}
1888
Jens Axboe85dd01e2012-03-12 14:33:16 +01001889static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1890{
1891 struct gfio_client *gc = client->client_data;
1892
1893 gdk_threads_enter();
1894 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1895 gdk_threads_leave();
1896}
1897
1898static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1899{
1900 struct gfio_client *gc = client->client_data;
1901
1902 gdk_threads_enter();
1903 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1904 gdk_threads_leave();
1905}
1906
Jens Axboe1b427252012-03-14 15:03:03 +01001907static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1908{
Jens Axboe284b1e62012-03-14 21:55:07 +01001909 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 +01001910 free(pdu);
1911}
1912
Stephen M. Camerona1820202012-02-24 08:17:31 +01001913struct client_ops gfio_client_ops = {
Jens Axboe35c0ba72012-03-14 10:56:40 +01001914 .text = gfio_text_op,
Jens Axboe0420ba62012-02-29 11:16:52 +01001915 .disk_util = gfio_disk_util_op,
1916 .thread_status = gfio_thread_status_op,
1917 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001918 .jobs_eta = gfio_update_client_eta,
1919 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001920 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001921 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001922 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001923 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001924 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001925 .start = gfio_client_start,
1926 .job_start = gfio_client_job_start,
Jens Axboe1b427252012-03-14 15:03:03 +01001927 .iolog = gfio_client_iolog,
Jens Axboe6433ee02012-03-09 20:10:51 +01001928 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001929 .stay_connected = 1,
Jens Axboe46bcd492012-03-14 11:31:21 +01001930 .client_type = FIO_CLIENT_TYPE_GUI,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001931};
1932
Jens Axboe0fd18982012-03-14 10:34:48 +01001933/*
1934 * FIXME: need more handling here
1935 */
1936static void ge_destroy(struct gui_entry *ge)
1937{
1938 struct gfio_client *gc = ge->client;
1939
1940 if (gc && gc->client) {
1941 if (ge->state >= GE_STATE_CONNECTED)
1942 fio_client_terminate(gc->client);
1943
1944 fio_put_client(gc->client);
1945 }
1946
1947 flist_del(&ge->list);
1948 free(ge);
1949}
1950
1951static void ge_widget_destroy(GtkWidget *w, gpointer data)
1952{
Jens Axboe0fd18982012-03-14 10:34:48 +01001953}
1954
1955static void gfio_quit(struct gui *ui)
1956{
1957 struct gui_entry *ge;
1958
1959 while (!flist_empty(&ui->list)) {
1960 ge = flist_entry(ui->list.next, struct gui_entry, list);
1961 ge_destroy(ge);
1962 }
1963
1964 gtk_main_quit();
1965}
1966
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001967static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1968 __attribute__((unused)) gpointer data)
1969{
Jens Axboe0fd18982012-03-14 10:34:48 +01001970 gfio_quit(data);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001971}
1972
Stephen M. Cameron25927252012-02-24 08:17:31 +01001973static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001974{
Jens Axboea9eccde2012-03-09 14:59:42 +01001975 struct gui *ui = arg;
1976
1977 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001978 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001979 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001980 return NULL;
1981}
1982
Jens Axboe2f99deb2012-03-09 14:37:29 +01001983static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001984{
Jens Axboe9988ca72012-03-09 15:14:06 +01001985 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001986 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001987
Jens Axboe2f99deb2012-03-09 14:37:29 +01001988 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001989 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001990 if (ret < 0) {
1991 GError *error;
1992
1993 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1994 report_error(error);
1995 g_error_free(error);
1996 break;
1997 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001998 break;
1999
Jens Axboe2f99deb2012-03-09 14:37:29 +01002000 free(ge->job_files[i]);
2001 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01002002 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002003 while (i < ge->nr_job_files) {
2004 free(ge->job_files[i]);
2005 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01002006 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01002007 }
2008
Jens Axboe2c77d832012-03-13 19:02:04 +01002009 free(ge->job_files);
2010 ge->job_files = NULL;
Jens Axboe3af45202012-03-13 09:59:53 +01002011 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01002012 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01002013}
2014
Jens Axboe63a130b2012-03-06 20:08:59 +01002015static void *server_thread(void *arg)
2016{
2017 is_backend = 1;
2018 gfio_server_running = 1;
2019 fio_start_server(NULL);
2020 gfio_server_running = 0;
2021 return NULL;
2022}
2023
Jens Axboe2f99deb2012-03-09 14:37:29 +01002024static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01002025{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002026 struct gui *ui = &main_ui;
2027
Jens Axboe63a130b2012-03-06 20:08:59 +01002028 if (!gfio_server_running) {
2029 gfio_server_running = 1;
2030 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01002031 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01002032 }
2033}
2034
Stephen M. Cameron25927252012-02-24 08:17:31 +01002035static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2036 gpointer data)
2037{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002038 struct gui_entry *ge = data;
2039 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01002040
Jens Axboe78cb2fe2012-03-12 23:05:29 +01002041 if (gc)
2042 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002043}
2044
Jens Axboedf06f222012-03-02 13:32:04 +01002045static void file_open(GtkWidget *w, gpointer data);
2046
2047static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01002048{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002049 struct gui_entry *ge = data;
2050 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002051
Jens Axboe85dd01e2012-03-12 14:33:16 +01002052 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01002053 int ret;
2054
Jens Axboe2f99deb2012-03-09 14:37:29 +01002055 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01002056 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002057 if (!ge->nr_job_files)
2058 return;
2059
2060 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2061 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01002062 ret = fio_client_connect(gc->client);
2063 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01002064 if (!ge->ui->handler_running)
2065 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002066 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01002067 } else {
2068 GError *error;
2069
2070 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2071 report_error(error);
2072 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01002073 }
Jens Axboedf06f222012-03-02 13:32:04 +01002074 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002075 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002076 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002077 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01002078 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01002079}
2080
Jens Axboeb9d2f302012-03-08 20:36:28 +01002081static void send_clicked(GtkWidget *widget, gpointer data)
2082{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002083 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01002084
Jens Axboe2f99deb2012-03-09 14:37:29 +01002085 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01002086 GError *error;
2087
2088 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);
2089 report_error(error);
2090 g_error_free(error);
2091
Jens Axboe2f99deb2012-03-09 14:37:29 +01002092 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002093 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01002094}
2095
Jens Axboe2f99deb2012-03-09 14:37:29 +01002096static GtkWidget *add_button(GtkWidget *buttonbox,
2097 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002098{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002099 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
2100
2101 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
2102 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
2103 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
2104 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
2105
2106 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002107}
2108
Jens Axboe2f99deb2012-03-09 14:37:29 +01002109static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
2110 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002111{
2112 int i;
2113
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002114 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002115 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002116}
2117
Jens Axboe0420ba62012-02-29 11:16:52 +01002118static void on_info_bar_response(GtkWidget *widget, gint response,
2119 gpointer data)
2120{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002121 struct gui *ui = &main_ui;
2122
Jens Axboe0420ba62012-02-29 11:16:52 +01002123 if (response == GTK_RESPONSE_OK) {
2124 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002125 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01002126 }
2127}
2128
Jens Axboedf06f222012-03-02 13:32:04 +01002129void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01002130{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002131 struct gui *ui = &main_ui;
2132
2133 if (ui->error_info_bar == NULL) {
2134 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01002135 GTK_RESPONSE_OK,
2136 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002137 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2138 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01002139 GTK_MESSAGE_ERROR);
2140
Jens Axboe2f99deb2012-03-09 14:37:29 +01002141 ui->error_label = gtk_label_new(error->message);
2142 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2143 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01002144
Jens Axboe2f99deb2012-03-09 14:37:29 +01002145 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2146 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01002147 } else {
2148 char buffer[256];
2149 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002150 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01002151 }
2152}
2153
Jens Axboe62bc9372012-03-07 11:45:07 +01002154struct connection_widgets
2155{
2156 GtkWidget *hentry;
2157 GtkWidget *combo;
2158 GtkWidget *button;
2159};
2160
2161static void hostname_cb(GtkEntry *entry, gpointer data)
2162{
2163 struct connection_widgets *cw = data;
2164 int uses_net = 0, is_localhost = 0;
2165 const gchar *text;
2166 gchar *ctext;
2167
2168 /*
2169 * Check whether to display the 'auto start backend' box
2170 * or not. Show it if we are a localhost and using network,
2171 * or using a socket.
2172 */
2173 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2174 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2175 uses_net = 1;
2176 g_free(ctext);
2177
2178 if (uses_net) {
2179 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2180 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2181 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2182 !strcmp(text, "ip6-loopback"))
2183 is_localhost = 1;
2184 }
2185
2186 if (!uses_net || is_localhost) {
2187 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2188 gtk_widget_set_sensitive(cw->button, 1);
2189 } else {
2190 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2191 gtk_widget_set_sensitive(cw->button, 0);
2192 }
2193}
2194
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002195static int get_connection_details(char **host, int *port, int *type,
2196 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01002197{
Jens Axboe62bc9372012-03-07 11:45:07 +01002198 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2199 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002200 char *typeentry;
2201
2202 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002203 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01002204 GTK_DIALOG_DESTROY_WITH_PARENT,
2205 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2206 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2207
2208 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01002209 /* gtk_dialog_get_content_area() is 2.14 and newer */
2210 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002211 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2212
2213 box = gtk_vbox_new(FALSE, 6);
2214 gtk_container_add(GTK_CONTAINER(frame), box);
2215
2216 hbox = gtk_hbox_new(TRUE, 10);
2217 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01002218 cw.hentry = gtk_entry_new();
2219 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2220 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002221
2222 frame = gtk_frame_new("Port");
2223 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2224 box = gtk_vbox_new(FALSE, 10);
2225 gtk_container_add(GTK_CONTAINER(frame), box);
2226
2227 hbox = gtk_hbox_new(TRUE, 4);
2228 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2229 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2230
2231 frame = gtk_frame_new("Type");
2232 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2233 box = gtk_vbox_new(FALSE, 10);
2234 gtk_container_add(GTK_CONTAINER(frame), box);
2235
2236 hbox = gtk_hbox_new(TRUE, 4);
2237 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2238
Jens Axboe62bc9372012-03-07 11:45:07 +01002239 cw.combo = gtk_combo_box_new_text();
2240 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2241 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2242 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2243 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002244
Jens Axboe62bc9372012-03-07 11:45:07 +01002245 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002246
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002247 frame = gtk_frame_new("Options");
2248 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2249 box = gtk_vbox_new(FALSE, 10);
2250 gtk_container_add(GTK_CONTAINER(frame), box);
2251
2252 hbox = gtk_hbox_new(TRUE, 4);
2253 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2254
Jens Axboe62bc9372012-03-07 11:45:07 +01002255 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2256 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2257 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.");
2258 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2259
2260 /*
2261 * Connect edit signal, so we can show/not-show the auto start button
2262 */
2263 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2264 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002265
Jens Axboea7a42ce2012-03-02 13:12:04 +01002266 gtk_widget_show_all(dialog);
2267
2268 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2269 gtk_widget_destroy(dialog);
2270 return 1;
2271 }
2272
Jens Axboe62bc9372012-03-07 11:45:07 +01002273 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002274 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2275
Jens Axboe62bc9372012-03-07 11:45:07 +01002276 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002277 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2278 *type = Fio_client_ipv4;
2279 else if (!strncmp(typeentry, "IPv6", 4))
2280 *type = Fio_client_ipv6;
2281 else
2282 *type = Fio_client_socket;
2283 g_free(typeentry);
2284
Jens Axboe62bc9372012-03-07 11:45:07 +01002285 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002286
Jens Axboea7a42ce2012-03-02 13:12:04 +01002287 gtk_widget_destroy(dialog);
2288 return 0;
2289}
2290
Jens Axboe2f99deb2012-03-09 14:37:29 +01002291static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002292{
2293 struct gfio_client *gc;
2294
2295 gc = malloc(sizeof(*gc));
2296 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002297 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002298 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002299
Jens Axboe2f99deb2012-03-09 14:37:29 +01002300 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002301
2302 client->client_data = gc;
2303}
2304
Jens Axboe2f99deb2012-03-09 14:37:29 +01002305static GtkWidget *new_client_page(struct gui_entry *ge);
2306
2307static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2308{
2309 struct gui_entry *ge;
2310
2311 ge = malloc(sizeof(*ge));
2312 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002313 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002314 INIT_FLIST_HEAD(&ge->list);
2315 flist_add_tail(&ge->list, &ui->list);
2316 ge->ui = ui;
2317 return ge;
2318}
2319
Jens Axboe2f99deb2012-03-09 14:37:29 +01002320static struct gui_entry *get_new_ge_with_tab(const char *name)
2321{
2322 struct gui_entry *ge;
2323
2324 ge = alloc_new_gui_entry(&main_ui);
2325
2326 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002327 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002328
2329 ge->page_label = gtk_label_new(name);
2330 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2331
2332 gtk_widget_show_all(main_ui.window);
2333 return ge;
2334}
2335
2336static void file_new(GtkWidget *w, gpointer data)
2337{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002338 struct gui *ui = (struct gui *) data;
2339 struct gui_entry *ge;
2340
2341 ge = get_new_ge_with_tab("Untitled");
2342 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002343}
2344
2345/*
2346 * Return the 'ge' corresponding to the tab. If the active tab is the
2347 * main tab, open a new tab.
2348 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002349static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002350{
2351 struct flist_head *entry;
2352 struct gui_entry *ge;
2353
Jens Axboe38634cb2012-03-13 12:26:41 +01002354 if (!cur_page) {
2355 if (created)
2356 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002357 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002358 }
2359
2360 if (created)
2361 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002362
2363 flist_for_each(entry, &main_ui.list) {
2364 ge = flist_entry(entry, struct gui_entry, list);
2365 if (ge->page_num == cur_page)
2366 return ge;
2367 }
2368
2369 return NULL;
2370}
2371
Jens Axboe85dd01e2012-03-12 14:33:16 +01002372static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2373{
2374 gint cur_page;
2375
2376 /*
2377 * Main tab is tab 0, so any current page other than 0 holds
2378 * a ge entry.
2379 */
2380 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2381 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002382 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002383
2384 return NULL;
2385}
2386
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002387static void file_close(GtkWidget *w, gpointer data)
2388{
2389 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002390 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002391
2392 /*
2393 * Can't close the main tab
2394 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002395 ge = get_ge_from_cur_tab(ui);
2396 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002397 gtk_widget_destroy(ge->vbox);
2398 return;
2399 }
2400
Jens Axboef5c67262012-03-13 08:20:41 +01002401 if (!flist_empty(&ui->list)) {
2402 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2403 return;
2404 }
2405
Jens Axboe0fd18982012-03-14 10:34:48 +01002406 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002407}
2408
Jens Axboe38634cb2012-03-13 12:26:41 +01002409static void file_add_recent(struct gui *ui, const gchar *uri)
2410{
Jens Axboea217ba72012-03-13 20:29:39 +01002411 GtkRecentData grd;
2412
2413 memset(&grd, 0, sizeof(grd));
2414 grd.display_name = strdup("gfio");
2415 grd.description = strdup("Fio job file");
2416 grd.mime_type = strdup(GFIO_MIME);
2417 grd.app_name = strdup(g_get_application_name());
2418 grd.app_exec = strdup("gfio %f/%u");
2419
2420 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002421}
2422
2423static gchar *get_filename_from_uri(const gchar *uri)
2424{
2425 if (strncmp(uri, "file://", 7))
2426 return strdup(uri);
2427
2428 return strdup(uri + 7);
2429}
2430
2431static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2432 int type, int port)
2433{
2434 struct fio_client *client;
2435 gchar *filename;
2436
2437 filename = get_filename_from_uri(uri);
2438
2439 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2440 ge->job_files[ge->nr_job_files] = strdup(filename);
2441 ge->nr_job_files++;
2442
2443 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2444 if (!client) {
2445 GError *error;
2446
2447 error = g_error_new(g_quark_from_string("fio"), 1,
2448 "Failed to add client %s", host);
2449 report_error(error);
2450 g_error_free(error);
2451 return 1;
2452 }
2453
2454 gfio_client_added(ge, client);
2455 file_add_recent(ge->ui, uri);
2456 return 0;
2457}
2458
Jens Axboea6790902012-03-13 15:16:11 +01002459static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002460{
Jens Axboea6790902012-03-13 15:16:11 +01002461 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002462 struct gui_entry *ge;
2463 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002464 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002465 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002466
2467 /*
2468 * Creates new tab if current tab is the main window, or the
2469 * current tab already has a client.
2470 */
2471 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002472 ge = get_ge_from_page(cur_page, &ge_is_new);
2473 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002474 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002475 ge_is_new = 1;
2476 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002477
2478 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002479
Jens Axboea6790902012-03-13 15:16:11 +01002480 if (get_connection_details(&host, &port, &type, &server_start)) {
2481 if (ge_is_new)
2482 gtk_widget_destroy(ge->vbox);
2483
2484 return 1;
2485 }
2486
2487 ret = do_file_open(ge, uri, host, type, port);
2488
2489 free(host);
2490
2491 if (!ret) {
2492 if (server_start)
2493 gfio_start_server();
2494 } else {
2495 if (ge_is_new)
2496 gtk_widget_destroy(ge->vbox);
2497 }
2498
2499 return ret;
2500}
2501
2502static void recent_open(GtkAction *action, gpointer data)
2503{
2504 struct gui *ui = (struct gui *) data;
2505 GtkRecentInfo *info;
2506 const gchar *uri;
2507
2508 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2509 uri = gtk_recent_info_get_uri(info);
2510
2511 do_file_open_with_tab(ui, uri);
2512}
2513
2514static void file_open(GtkWidget *w, gpointer data)
2515{
2516 struct gui *ui = data;
2517 GtkWidget *dialog;
2518 GSList *filenames, *fn_glist;
2519 GtkFileFilter *filter;
2520
Jens Axboe0420ba62012-02-29 11:16:52 +01002521 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002522 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002523 GTK_FILE_CHOOSER_ACTION_OPEN,
2524 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2525 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2526 NULL);
2527 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2528
2529 filter = gtk_file_filter_new();
2530 gtk_file_filter_add_pattern(filter, "*.fio");
2531 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002532 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002533 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002534 gtk_file_filter_set_name(filter, "Fio job file");
2535 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2536
2537 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2538 gtk_widget_destroy(dialog);
2539 return;
2540 }
2541
2542 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002543
2544 gtk_widget_destroy(dialog);
2545
Jens Axboe0420ba62012-02-29 11:16:52 +01002546 filenames = fn_glist;
2547 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002548 if (do_file_open_with_tab(ui, filenames->data))
2549 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002550 filenames = g_slist_next(filenames);
2551 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002552
Jens Axboe0420ba62012-02-29 11:16:52 +01002553 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002554}
2555
2556static void file_save(GtkWidget *w, gpointer data)
2557{
Jens Axboe63a130b2012-03-06 20:08:59 +01002558 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002559 GtkWidget *dialog;
2560
2561 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002562 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002563 GTK_FILE_CHOOSER_ACTION_SAVE,
2564 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2565 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2566 NULL);
2567
2568 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2569 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2570
2571 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2572 char *filename;
2573
2574 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2575 // save_job_file(filename);
2576 g_free(filename);
2577 }
2578 gtk_widget_destroy(dialog);
2579}
2580
Jens Axboe9b260bd2012-03-06 11:02:52 +01002581static void view_log_destroy(GtkWidget *w, gpointer data)
2582{
2583 struct gui *ui = (struct gui *) data;
2584
2585 gtk_widget_ref(ui->log_tree);
2586 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2587 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002588 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002589}
2590
2591static void view_log(GtkWidget *w, gpointer data)
2592{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002593 GtkWidget *win, *scroll, *vbox, *box;
2594 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002595
Jens Axboe4cbe7212012-03-06 13:36:17 +01002596 if (ui->log_view)
2597 return;
2598
2599 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002600 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002601 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002602
Jens Axboe4cbe7212012-03-06 13:36:17 +01002603 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002604
Jens Axboe4cbe7212012-03-06 13:36:17 +01002605 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2606
2607 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2608
2609 box = gtk_hbox_new(TRUE, 0);
2610 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2611 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2612 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2613
2614 vbox = gtk_vbox_new(TRUE, 5);
2615 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2616
2617 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002618 gtk_widget_show_all(win);
2619}
2620
Jens Axboe85dd01e2012-03-12 14:33:16 +01002621static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002622{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002623 struct gui *ui = (struct gui *) data;
2624 struct gui_entry *ge;
2625
2626 ge = get_ge_from_cur_tab(ui);
2627 if (ge)
2628 connect_clicked(w, ge);
2629}
2630
2631static void send_job_entry(GtkWidget *w, gpointer data)
2632{
2633 struct gui *ui = (struct gui *) data;
2634 struct gui_entry *ge;
2635
2636 ge = get_ge_from_cur_tab(ui);
2637 if (ge)
2638 send_clicked(w, ge);
2639
2640}
2641
2642static void edit_job_entry(GtkWidget *w, gpointer data)
2643{
2644}
2645
2646static void start_job_entry(GtkWidget *w, gpointer data)
2647{
2648 struct gui *ui = (struct gui *) data;
2649 struct gui_entry *ge;
2650
2651 ge = get_ge_from_cur_tab(ui);
2652 if (ge)
2653 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002654}
2655
Jens Axboe781ccba2012-03-15 09:44:42 +01002656static void view_results(GtkWidget *w, gpointer data)
2657{
2658 struct gui *ui = (struct gui *) data;
2659 struct gfio_client *gc;
2660 struct gui_entry *ge;
2661
2662 ge = get_ge_from_cur_tab(ui);
2663 if (!ge)
2664 return;
2665
2666 if (ge->results_window)
2667 return;
2668
2669 gc = ge->client;
2670 if (gc && gc->nr_results)
2671 gfio_display_end_results(gc);
2672}
2673
Jens Axboe8577f4f2012-03-09 19:28:27 +01002674static void __update_graph_limits(struct gfio_graphs *g)
2675{
2676 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2677 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2678}
2679
2680static void update_graph_limits(void)
2681{
2682 struct flist_head *entry;
2683 struct gui_entry *ge;
2684
2685 __update_graph_limits(&main_ui.graphs);
2686
2687 flist_for_each(entry, &main_ui.list) {
2688 ge = flist_entry(entry, struct gui_entry, list);
2689 __update_graph_limits(&ge->graphs);
2690 }
2691}
2692
Jens Axboe46974a72012-03-02 19:34:13 +01002693static void preferences(GtkWidget *w, gpointer data)
2694{
Jens Axboef3e84402012-03-07 13:14:32 +01002695 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002696 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002697 int i;
2698
2699 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002700 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002701 GTK_DIALOG_DESTROY_WITH_PARENT,
2702 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2703 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2704 NULL);
2705
Jens Axboe8577f4f2012-03-09 19:28:27 +01002706 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002707 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2708 vbox = gtk_vbox_new(FALSE, 6);
2709 gtk_container_add(GTK_CONTAINER(frame), vbox);
2710
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002711 hbox = gtk_hbox_new(FALSE, 5);
2712 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2713 entry = gtk_label_new("Font face to use for graph labels");
2714 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2715
Jens Axboef3e84402012-03-07 13:14:32 +01002716 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002717 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002718
Jens Axboe8577f4f2012-03-09 19:28:27 +01002719 box = gtk_vbox_new(FALSE, 6);
2720 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2721
2722 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002723 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002724 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2725 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2726
Jens Axboec05d9052012-03-11 13:05:35 +01002727 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002728
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002729 box = gtk_vbox_new(FALSE, 6);
2730 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2731
2732 hbox = gtk_hbox_new(FALSE, 5);
2733 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2734 entry = gtk_label_new("Client ETA request interval (msec)");
2735 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2736
2737 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002738 frame = gtk_frame_new("Debug logging");
2739 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2740 vbox = gtk_vbox_new(FALSE, 6);
2741 gtk_container_add(GTK_CONTAINER(frame), vbox);
2742
2743 box = gtk_hbox_new(FALSE, 6);
2744 gtk_container_add(GTK_CONTAINER(vbox), box);
2745
2746 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2747
2748 for (i = 0; i < FD_DEBUG_MAX; i++) {
2749 if (i == 7) {
2750 box = gtk_hbox_new(FALSE, 6);
2751 gtk_container_add(GTK_CONTAINER(vbox), box);
2752 }
2753
2754
2755 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2756 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2757 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2758 }
2759
Jens Axboe46974a72012-03-02 19:34:13 +01002760 gtk_widget_show_all(dialog);
2761
2762 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2763 gtk_widget_destroy(dialog);
2764 return;
2765 }
2766
2767 for (i = 0; i < FD_DEBUG_MAX; i++) {
2768 int set;
2769
2770 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2771 if (set)
2772 fio_debug |= (1UL << i);
2773 }
2774
Jens Axboef3e84402012-03-07 13:14:32 +01002775 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002776 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2777 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002778 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002779
Jens Axboe46974a72012-03-02 19:34:13 +01002780 gtk_widget_destroy(dialog);
2781}
2782
Jens Axboe0420ba62012-02-29 11:16:52 +01002783static void about_dialog(GtkWidget *w, gpointer data)
2784{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002785 const char *authors[] = {
2786 "Jens Axboe <axboe@kernel.dk>",
2787 "Stephen Carmeron <stephenmcameron@gmail.com>",
2788 NULL
2789 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002790 const char *license[] = {
2791 "Fio is free software; you can redistribute it and/or modify "
2792 "it under the terms of the GNU General Public License as published by "
2793 "the Free Software Foundation; either version 2 of the License, or "
2794 "(at your option) any later version.\n",
2795 "Fio is distributed in the hope that it will be useful, "
2796 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2797 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2798 "GNU General Public License for more details.\n",
2799 "You should have received a copy of the GNU General Public License "
2800 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2801 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2802 };
2803 char *license_trans;
2804
2805 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2806 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002807
Jens Axboe0420ba62012-02-29 11:16:52 +01002808 gtk_show_about_dialog(NULL,
2809 "program-name", "gfio",
2810 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002811 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002812 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2813 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002814 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002815 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002816 "logo-icon-name", "fio",
2817 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002818 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002819 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002820
Jens Axboe2f99deb2012-03-09 14:37:29 +01002821 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002822}
2823
2824static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002825 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002826 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002827 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002828 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002829 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002830 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002831 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2832 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2833 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002834 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01002835 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002836 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2837 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2838 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2839 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002840 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2841 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002842};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002843static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002844
2845static const gchar *ui_string = " \
2846 <ui> \
2847 <menubar name=\"MainMenu\"> \
2848 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002849 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01002850 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002851 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002852 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002853 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002854 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002855 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2856 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002857 <placeholder name=\"FileRecentFiles\"/> \
2858 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002859 <menuitem name=\"Quit\" action=\"Quit\" /> \
2860 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002861 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002862 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002863 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002864 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2865 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002866 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002867 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002868 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002869 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01002870 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2871 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002872 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2873 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002874 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2875 <menuitem name=\"About\" action=\"About\" /> \
2876 </menu> \
2877 </menubar> \
2878 </ui> \
2879";
2880
Jens Axboe4cbe7212012-03-06 13:36:17 +01002881static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2882 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002883{
Jens Axboeca664f42012-03-14 19:49:40 +01002884 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002885 GError *error = 0;
2886
2887 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002888 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002889
2890 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2891 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2892
2893 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002894
Jens Axboe0420ba62012-02-29 11:16:52 +01002895 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2896}
2897
2898void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2899 GtkWidget *vbox, GtkUIManager *ui_manager)
2900{
2901 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2902}
2903
Jens Axboec80b74b2012-03-12 10:23:28 +01002904static void combo_entry_changed(GtkComboBox *box, gpointer data)
2905{
2906 struct gui_entry *ge = (struct gui_entry *) data;
2907 gint index;
2908
2909 index = gtk_combo_box_get_active(box);
2910
2911 multitext_set_entry(&ge->eta.iotype, index);
2912 multitext_set_entry(&ge->eta.ioengine, index);
2913 multitext_set_entry(&ge->eta.iodepth, index);
2914}
2915
2916static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2917{
2918 struct gui_entry *ge = (struct gui_entry *) data;
2919
2920 multitext_free(&ge->eta.iotype);
2921 multitext_free(&ge->eta.ioengine);
2922 multitext_free(&ge->eta.iodepth);
2923}
2924
Jens Axboe2f99deb2012-03-09 14:37:29 +01002925static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002926{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002927 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002928 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01002929
Jens Axboe2f99deb2012-03-09 14:37:29 +01002930 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002931
Jens Axboe65476332012-03-13 10:37:04 +01002932 top_align = gtk_alignment_new(0, 0, 1, 0);
2933 top_vbox = gtk_vbox_new(FALSE, 3);
2934 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2935 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002936
Jens Axboe3e47bd22012-02-29 13:45:02 +01002937 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002938 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002939 probe_frame = gtk_vbox_new(FALSE, 3);
2940 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2941
2942 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002943 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2944 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2945 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2946 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2947 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002948
Jens Axboe3e47bd22012-02-29 13:45:02 +01002949 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002950 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2951
Jens Axboe3863d1a2012-03-09 17:39:05 +01002952 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002953 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2954 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2955 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2956 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2957 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002958 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2959 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2960
2961 probe_box = gtk_hbox_new(FALSE, 3);
2962 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2963 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2964 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2965 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2966 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2967
2968 /*
2969 * Only add this if we have a commit rate
2970 */
2971#if 0
2972 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002973 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002974
Jens Axboe2f99deb2012-03-09 14:37:29 +01002975 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2976 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2977
2978 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2979 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2980#endif
2981
2982 /*
2983 * Set up a drawing area and IOPS and bandwidth graphs
2984 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002985 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002986 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002987 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002988 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2989 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2990 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2991 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2992 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002993 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2994 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002995 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002996 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002997 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002998 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002999
3000 setup_graphs(&ge->graphs);
3001
3002 /*
3003 * Set up alignments for widgets at the bottom of ui,
3004 * align bottom left, expand horizontally but not vertically
3005 */
Jens Axboe65476332012-03-13 10:37:04 +01003006 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003007 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003008 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3009 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003010
3011 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3012
3013 /*
3014 * Set up thread status progress bar
3015 */
3016 ge->thread_status_pb = gtk_progress_bar_new();
3017 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3018 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3019 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3020
3021
3022 return main_vbox;
3023}
3024
3025static GtkWidget *new_main_page(struct gui *ui)
3026{
3027 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01003028 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003029
3030 main_vbox = gtk_vbox_new(FALSE, 3);
3031
3032 /*
3033 * Set up alignments for widgets at the top of ui,
3034 * align top left, expand horizontally but not vertically
3035 */
Jens Axboe65476332012-03-13 10:37:04 +01003036 top_align = gtk_alignment_new(0, 0, 1, 0);
3037 top_vbox = gtk_vbox_new(FALSE, 0);
3038 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3039 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003040
3041 probe = gtk_frame_new("Run statistics");
3042 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3043 probe_frame = gtk_vbox_new(FALSE, 3);
3044 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01003045
3046 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003047 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01003048 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01003049 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3050 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3051 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3052 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01003053
3054 /*
3055 * Only add this if we have a commit rate
3056 */
3057#if 0
3058 probe_box = gtk_hbox_new(FALSE, 3);
3059 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3060
Jens Axboe3e47bd22012-02-29 13:45:02 +01003061 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3062 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3063
Jens Axboe3e47bd22012-02-29 13:45:02 +01003064 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3065 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01003066#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01003067
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01003068 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003069 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003070 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01003071 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01003072 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01003073 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003074 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3075 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3076 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3077 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3078 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01003079 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3080 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003081 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01003082 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003083 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01003084 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01003085 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003086
Jens Axboe2f99deb2012-03-09 14:37:29 +01003087 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003088
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003089 /*
3090 * Set up alignments for widgets at the bottom of ui,
3091 * align bottom left, expand horizontally but not vertically
3092 */
Jens Axboe65476332012-03-13 10:37:04 +01003093 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003094 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003095 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3096 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003097
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003098 /*
3099 * Set up thread status progress bar
3100 */
3101 ui->thread_status_pb = gtk_progress_bar_new();
3102 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01003103 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003104 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3105
Jens Axboe2f99deb2012-03-09 14:37:29 +01003106 return main_vbox;
3107}
3108
3109static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3110 guint page, gpointer data)
3111
3112{
Jens Axboe02421e62012-03-12 12:05:50 +01003113 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01003114 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01003115
Jens Axboe85dd01e2012-03-12 14:33:16 +01003116 if (!page) {
3117 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01003118 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003119 return TRUE;
3120 }
3121
3122 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01003123 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003124 if (ge)
3125 update_button_states(ui, ge);
3126
Jens Axboe2f99deb2012-03-09 14:37:29 +01003127 return TRUE;
3128}
3129
Jens Axboe38634cb2012-03-13 12:26:41 +01003130static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3131{
3132 time_t time_a = gtk_recent_info_get_visited(a);
3133 time_t time_b = gtk_recent_info_get_visited(b);
3134
3135 return time_b - time_a;
3136}
3137
3138static void add_recent_file_items(struct gui *ui)
3139{
3140 const gchar *gfio = g_get_application_name();
3141 GList *items, *item;
3142 int i = 0;
3143
3144 if (ui->recent_ui_id) {
3145 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3146 gtk_ui_manager_ensure_update(ui->uimanager);
3147 }
3148 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3149
3150 if (ui->actiongroup) {
3151 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3152 g_object_unref(ui->actiongroup);
3153 }
3154 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3155
3156 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3157
3158 items = gtk_recent_manager_get_items(ui->recentmanager);
3159 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3160
3161 for (item = items; item && item->data; item = g_list_next(item)) {
3162 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3163 gchar *action_name;
3164 const gchar *label;
3165 GtkAction *action;
3166
3167 if (!gtk_recent_info_has_application(info, gfio))
3168 continue;
3169
3170 /*
3171 * We only support local files for now
3172 */
3173 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3174 continue;
3175
3176 action_name = g_strdup_printf("RecentFile%u", i++);
3177 label = gtk_recent_info_get_display_name(info);
3178
3179 action = g_object_new(GTK_TYPE_ACTION,
3180 "name", action_name,
3181 "label", label, NULL);
3182
3183 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3184 gtk_recent_info_ref(info),
3185 (GDestroyNotify) gtk_recent_info_unref);
3186
3187
3188 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3189
3190 gtk_action_group_add_action(ui->actiongroup, action);
3191 g_object_unref(action);
3192
3193 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3194 "/MainMenu/FileMenu/FileRecentFiles",
3195 label, action_name,
3196 GTK_UI_MANAGER_MENUITEM, FALSE);
3197
3198 g_free(action_name);
3199
3200 if (i == 8)
3201 break;
3202 }
3203
3204 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3205 g_list_free(items);
3206}
3207
Jens Axboea6790902012-03-13 15:16:11 +01003208static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3209 gint x, gint y, GtkSelectionData *data,
3210 guint info, guint time)
3211{
3212 struct gui *ui = &main_ui;
3213 gchar **uris;
3214 GtkWidget *source;
3215 int i;
3216
3217 source = gtk_drag_get_source_widget(ctx);
3218 if (source && widget == gtk_widget_get_toplevel(source)) {
3219 gtk_drag_finish(ctx, FALSE, FALSE, time);
3220 return;
3221 }
3222
3223 uris = gtk_selection_data_get_uris(data);
3224 if (!uris) {
3225 gtk_drag_finish(ctx, FALSE, FALSE, time);
3226 return;
3227 }
3228
3229 i = 0;
3230 while (uris[i]) {
3231 if (do_file_open_with_tab(ui, uris[i]))
3232 break;
3233 i++;
3234 }
3235
3236 gtk_drag_finish(ctx, TRUE, FALSE, time);
3237 g_strfreev(uris);
3238}
3239
Jens Axboe2f99deb2012-03-09 14:37:29 +01003240static void init_ui(int *argc, char **argv[], struct gui *ui)
3241{
3242 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003243 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003244
3245 /* Magical g*thread incantation, you just need this thread stuff.
3246 * Without it, the update that happens in gfio_update_thread_status
3247 * doesn't really happen in a timely fashion, you need expose events
3248 */
3249 if (!g_thread_supported())
3250 g_thread_init(NULL);
3251 gdk_threads_init();
3252
3253 gtk_init(argc, argv);
3254 settings = gtk_settings_get_default();
3255 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3256 g_type_init();
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003257 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003258
3259 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003260 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003261 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3262
3263 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3264 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3265
3266 ui->vbox = gtk_vbox_new(FALSE, 0);
3267 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3268
Jens Axboe02421e62012-03-12 12:05:50 +01003269 ui->uimanager = gtk_ui_manager_new();
3270 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3271 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003272
Jens Axboe38634cb2012-03-13 12:26:41 +01003273 ui->recentmanager = gtk_recent_manager_get_default();
3274 add_recent_file_items(ui);
3275
Jens Axboe2f99deb2012-03-09 14:37:29 +01003276 ui->notebook = gtk_notebook_new();
3277 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003278 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003279 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003280 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3281
3282 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003283 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3284 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3285 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003286
3287 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3288
Jens Axboe9b260bd2012-03-06 11:02:52 +01003289 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003290
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003291 gtk_widget_show_all(ui->window);
3292}
3293
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003294int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003295{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003296 if (initialize_fio(envp))
3297 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003298 if (fio_init_options())
3299 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003300
Jens Axboe2f99deb2012-03-09 14:37:29 +01003301 memset(&main_ui, 0, sizeof(main_ui));
3302 INIT_FLIST_HEAD(&main_ui.list);
3303
3304 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003305
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003306 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003307 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003308 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003309 return 0;
3310}