blob: 58faaaf9e29321c5332f54346b20f573b0f5edab [file] [log] [blame]
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
Jens Axboec0187f32012-03-06 15:39:15 +01005 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01006 *
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
Stephen M. Cameron8232e282012-02-24 08:17:31 +010024#include <locale.h>
Stephen M. Cameron60f6b332012-02-24 08:17:32 +010025#include <malloc.h>
Jens Axboe6b79c802012-03-08 10:51:36 +010026#include <string.h>
Stephen M. Cameron8232e282012-02-24 08:17:31 +010027
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +010028#include <glib.h>
Jens Axboe2fd3bb02012-03-07 08:07:39 +010029#include <cairo.h>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +010030#include <gtk/gtk.h>
31
Stephen M. Cameron8232e282012-02-24 08:17:31 +010032#include "fio.h"
Jens Axboe2fd3bb02012-03-07 08:07:39 +010033#include "graph.h"
Stephen M. Cameron8232e282012-02-24 08:17:31 +010034
Jens Axboe38634cb2012-03-13 12:26:41 +010035#define GFIO_MIME "text/fio"
36
Jens Axboe63a130b2012-03-06 20:08:59 +010037static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010038static const char *gfio_graph_font;
Jens Axboe8577f4f2012-03-09 19:28:27 +010039static unsigned int gfio_graph_limit = 100;
Stephen M. Cameron814479d2012-03-15 07:58:14 +010040static GdkColor white;
Jens Axboe63a130b2012-03-06 20:08:59 +010041
Jens Axboe6b79c802012-03-08 10:51:36 +010042static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010043
Stephen M. Cameronf3074002012-02-24 08:17:30 +010044#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
45
46typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
47
Jens Axboe3e47bd22012-02-29 13:45:02 +010048static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010049static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010050static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010051
52static struct button_spec {
53 const char *buttontext;
54 clickfunction f;
Jens Axboe014f4022012-03-15 14:03:01 +010055 const char *tooltiptext[2];
56 const int start_sensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010057} buttonspeclist[] = {
Jens Axboe3e47bd22012-02-29 13:45:02 +010058#define CONNECT_BUTTON 0
Jens Axboeb9d2f302012-03-08 20:36:28 +010059#define SEND_BUTTON 1
60#define START_JOB_BUTTON 2
Jens Axboe014f4022012-03-15 14:03:01 +010061 { "Connect", connect_clicked, { "Disconnect from host", "Connect to host" }, 1 },
62 { "Send", send_clicked, { "Send job description to host", NULL }, 0 },
Jens Axboeb9d2f302012-03-08 20:36:28 +010063 { "Start Job", start_job_clicked,
Jens Axboe014f4022012-03-15 14:03:01 +010064 { "Start the current job on the server", NULL }, 0 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010065};
66
Jens Axboe843ad232012-02-29 11:44:53 +010067struct probe_widget {
68 GtkWidget *hostname;
69 GtkWidget *os;
70 GtkWidget *arch;
71 GtkWidget *fio_ver;
72};
73
Jens Axboec80b74b2012-03-12 10:23:28 +010074struct multitext_widget {
75 GtkWidget *entry;
76 char **text;
77 unsigned int cur_text;
78 unsigned int max_text;
79};
80
Jens Axboe3e47bd22012-02-29 13:45:02 +010081struct eta_widget {
Jens Axboe3863d1a2012-03-09 17:39:05 +010082 GtkWidget *names;
Jens Axboec80b74b2012-03-12 10:23:28 +010083 struct multitext_widget iotype;
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 Axboe014f4022012-03-15 14:03:01 +0100436static const char *get_button_tooltip(struct button_spec *s, int sensitive)
437{
438 if (s->tooltiptext[sensitive])
439 return s->tooltiptext[sensitive];
440
441 return s->tooltiptext[0];
442}
443
444static GtkWidget *add_button(GtkWidget *buttonbox,
445 struct button_spec *buttonspec, gpointer data)
446{
447 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
448 gboolean sens = buttonspec->start_sensitive;
449
450 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
451 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
452
453 sens = buttonspec->start_sensitive;
454 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
455 gtk_widget_set_sensitive(button, sens);
456
457 return button;
458}
459
460static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
461 int nbuttons)
462{
463 int i;
464
465 for (i = 0; i < nbuttons; i++)
466 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
467}
468
Jens Axboe85dd01e2012-03-12 14:33:16 +0100469/*
470 * Update sensitivity of job buttons and job menu items, based on the
471 * state of the client.
472 */
473static void update_button_states(struct gui *ui, struct gui_entry *ge)
474{
475 unsigned int connect_state, send_state, start_state, edit_state;
476 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100477
478 switch (ge->state) {
479 default: {
480 char tmp[80];
481
482 sprintf(tmp, "Bad client state: %u\n", ge->state);
483 show_info_dialog(ui, "Error", tmp);
484 /* fall through to new state */
485 }
486
487 case GE_STATE_NEW:
488 connect_state = 1;
489 edit_state = 0;
490 connect_str = "Connect";
491 send_state = 0;
492 start_state = 0;
493 break;
494 case GE_STATE_CONNECTED:
495 connect_state = 1;
496 edit_state = 0;
497 connect_str = "Disconnect";
498 send_state = 1;
499 start_state = 0;
500 break;
501 case GE_STATE_JOB_SENT:
502 connect_state = 1;
503 edit_state = 0;
504 connect_str = "Disconnect";
505 send_state = 0;
506 start_state = 1;
507 break;
508 case GE_STATE_JOB_STARTED:
509 connect_state = 1;
510 edit_state = 1;
511 connect_str = "Disconnect";
512 send_state = 0;
513 start_state = 1;
514 break;
515 case GE_STATE_JOB_RUNNING:
516 connect_state = 1;
517 edit_state = 0;
518 connect_str = "Disconnect";
519 send_state = 0;
520 start_state = 0;
521 break;
522 case GE_STATE_JOB_DONE:
523 connect_state = 1;
524 edit_state = 0;
525 connect_str = "Connect";
526 send_state = 0;
527 start_state = 0;
528 break;
529 }
530
531 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
532 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
533 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
534 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
Jens Axboe014f4022012-03-15 14:03:01 +0100535 gtk_widget_set_tooltip_text(ge->button[CONNECT_BUTTON], get_button_tooltip(&buttonspeclist[CONNECT_BUTTON], connect_state));
Jens Axboe85dd01e2012-03-12 14:33:16 +0100536
Jens Axboe781ccba2012-03-15 09:44:42 +0100537 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
538 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100539
Jens Axboe781ccba2012-03-15 09:44:42 +0100540 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
541 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
542 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100543
Jens Axboe781ccba2012-03-15 09:44:42 +0100544 if (ge->client && ge->client->nr_results)
545 set_view_results_visible(ui, 1);
546 else
547 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100548}
549
550static void gfio_set_state(struct gui_entry *ge, unsigned int state)
551{
552 ge->state = state;
553 update_button_states(ge->ui, ge);
554}
555
Jens Axboea2697902012-03-05 16:43:49 +0100556#define ALIGN_LEFT 1
557#define ALIGN_RIGHT 2
558#define INVISIBLE 4
559#define UNSORTABLE 8
560
561GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
562{
563 GtkCellRenderer *renderer;
564 GtkTreeViewColumn *col;
565 double xalign = 0.0; /* left as default */
566 PangoAlignment align;
567 gboolean visible;
568
569 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
570 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
571 PANGO_ALIGN_CENTER;
572 visible = !(flags & INVISIBLE);
573
574 renderer = gtk_cell_renderer_text_new();
575 col = gtk_tree_view_column_new();
576
577 gtk_tree_view_column_set_title(col, title);
578 if (!(flags & UNSORTABLE))
579 gtk_tree_view_column_set_sort_column_id(col, index);
580 gtk_tree_view_column_set_resizable(col, TRUE);
581 gtk_tree_view_column_pack_start(col, renderer, TRUE);
582 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
583 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
584 switch (align) {
585 case PANGO_ALIGN_LEFT:
586 xalign = 0.0;
587 break;
588 case PANGO_ALIGN_CENTER:
589 xalign = 0.5;
590 break;
591 case PANGO_ALIGN_RIGHT:
592 xalign = 1.0;
593 break;
594 }
595 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
596 gtk_tree_view_column_set_visible(col, visible);
597 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
598 return col;
599}
600
Jens Axboe9b260bd2012-03-06 11:02:52 +0100601static void gfio_ui_setup_log(struct gui *ui)
602{
603 GtkTreeSelection *selection;
604 GtkListStore *model;
605 GtkWidget *tree_view;
606
607 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
608
609 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
610 gtk_widget_set_can_focus(tree_view, FALSE);
611
612 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
613 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100614 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
615 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100616
617 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
618 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
619 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100620 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100621
622 ui->log_model = model;
623 ui->log_tree = tree_view;
624}
625
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100626static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
627 fio_fp64_t *plist,
628 unsigned int len,
629 double xdim, double ydim)
630{
631 struct graph *g;
632 int i;
633
634 g = graph_new(xdim, ydim, gfio_graph_font);
635 graph_title(g, title);
636 graph_x_title(g, "Percentile");
637
638 for (i = 0; i < len; i++) {
639 char fbuf[8];
640
641 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
642 graph_add_label(g, fbuf);
643 graph_add_data(g, fbuf, (double) ovals[i]);
644 }
645
646 return g;
647}
648
Jens Axboea2697902012-03-05 16:43:49 +0100649static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
650 fio_fp64_t *plist,
651 unsigned int len,
652 const char *base,
653 unsigned int scale)
654{
655 GType types[FIO_IO_U_LIST_MAX_LEN];
656 GtkWidget *tree_view;
657 GtkTreeSelection *selection;
658 GtkListStore *model;
659 GtkTreeIter iter;
660 int i;
661
662 for (i = 0; i < len; i++)
663 types[i] = G_TYPE_INT;
664
665 model = gtk_list_store_newv(len, types);
666
667 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
668 gtk_widget_set_can_focus(tree_view, FALSE);
669
Jens Axboe661f7412012-03-06 13:55:45 +0100670 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
671 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
672
Jens Axboea2697902012-03-05 16:43:49 +0100673 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
674 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
675
676 for (i = 0; i < len; i++) {
677 char fbuf[8];
678
679 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
680 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
681 }
682
683 gtk_list_store_append(model, &iter);
684
Jens Axboee0681f32012-03-06 12:14:42 +0100685 for (i = 0; i < len; i++) {
686 if (scale)
687 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100688 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100689 }
Jens Axboea2697902012-03-05 16:43:49 +0100690
691 return tree_view;
692}
693
Jens Axboe09d574e2012-03-15 10:45:48 +0100694static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100695{
696 struct graph *g = p;
697 cairo_t *cr;
698
699 cr = gdk_cairo_create(w->window);
700#if 0
701 if (graph_has_tooltips(g)) {
702 g_object_set(w, "has-tooltip", TRUE, NULL);
703 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
704 }
705#endif
706 cairo_set_source_rgb(cr, 0, 0, 0);
707 bar_graph_draw(g, cr);
708 cairo_destroy(cr);
709
710 return FALSE;
711}
712
Jens Axboe09d574e2012-03-15 10:45:48 +0100713static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
714 gpointer data)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100715{
716 struct graph *g = data;
717
718 graph_set_size(g, w->allocation.width, w->allocation.height);
719 graph_set_size(g, w->allocation.width, w->allocation.height);
720 graph_set_position(g, 0, 0);
721 return TRUE;
722}
723
724static void gfio_show_clat_percentiles(struct gfio_client *gc,
725 GtkWidget *vbox, struct thread_stat *ts,
Jens Axboea2697902012-03-05 16:43:49 +0100726 int ddir)
727{
728 unsigned int *io_u_plat = ts->io_u_plat[ddir];
729 unsigned long nr = ts->clat_stat[ddir].samples;
730 fio_fp64_t *plist = ts->percentile_list;
731 unsigned int *ovals, len, minv, maxv, scale_down;
732 const char *base;
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100733 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
Jens Axboe09d574e2012-03-15 10:45:48 +0100734 struct gui_entry *ge = gc->ge;
Jens Axboea2697902012-03-05 16:43:49 +0100735 char tmp[64];
736
737 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
738 if (!len)
739 goto out;
740
741 /*
742 * We default to usecs, but if the value range is such that we
743 * should scale down to msecs, do that.
744 */
745 if (minv > 2000 && maxv > 99999) {
746 scale_down = 1;
747 base = "msec";
748 } else {
749 scale_down = 0;
750 base = "usec";
751 }
752
Jens Axboea2697902012-03-05 16:43:49 +0100753 sprintf(tmp, "Completion percentiles (%s)", base);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100754 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
Jens Axboe09d574e2012-03-15 10:45:48 +0100755 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100756
Jens Axboea2697902012-03-05 16:43:49 +0100757 frame = gtk_frame_new(tmp);
758 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
759
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100760 completion_vbox = gtk_vbox_new(FALSE, 3);
761 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
Jens Axboea2697902012-03-05 16:43:49 +0100762 hbox = gtk_hbox_new(FALSE, 3);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100763 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
764 drawing_area = gtk_drawing_area_new();
765 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
766 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
767 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
Jens Axboe09d574e2012-03-15 10:45:48 +0100768 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
769 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 +0100770
771 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
772out:
773 if (ovals)
774 free(ovals);
775}
776
Jens Axboe3650a3c2012-03-05 14:09:03 +0100777static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
778 unsigned long max, double mean, double dev)
779{
780 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100781 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100782 char *minp, *maxp;
783 char tmp[64];
784
785 if (!usec_to_msec(&min, &max, &mean, &dev))
786 base = "(msec)";
787
788 minp = num2str(min, 6, 1, 0);
789 maxp = num2str(max, 6, 1, 0);
790
Jens Axboe3650a3c2012-03-05 14:09:03 +0100791 sprintf(tmp, "%s %s", name, base);
792 frame = gtk_frame_new(tmp);
793 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
794
Jens Axboe3650a3c2012-03-05 14:09:03 +0100795 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100796 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100797
798 label = new_info_label_in_frame(hbox, "Minimum");
799 gtk_label_set_text(GTK_LABEL(label), minp);
800 label = new_info_label_in_frame(hbox, "Maximum");
801 gtk_label_set_text(GTK_LABEL(label), maxp);
802 label = new_info_label_in_frame(hbox, "Average");
803 sprintf(tmp, "%5.02f", mean);
804 gtk_label_set_text(GTK_LABEL(label), tmp);
805 label = new_info_label_in_frame(hbox, "Standard deviation");
806 sprintf(tmp, "%5.02f", dev);
807 gtk_label_set_text(GTK_LABEL(label), tmp);
808
809 free(minp);
810 free(maxp);
811
812}
813
Jens Axboeca850992012-03-05 20:04:43 +0100814#define GFIO_CLAT 1
815#define GFIO_SLAT 2
816#define GFIO_LAT 4
817
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100818static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
819 struct group_run_stats *rs,
Jens Axboe3650a3c2012-03-05 14:09:03 +0100820 struct thread_stat *ts, int ddir)
821{
822 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100823 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100824 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100825 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100826 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100827 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100828 char *io_p, *bw_p, *iops_p;
829 int i2p;
830
831 if (!ts->runtime[ddir])
832 return;
833
834 i2p = is_power_of_2(rs->kb_base);
835 runt = ts->runtime[ddir];
836
837 bw = (1000 * ts->io_bytes[ddir]) / runt;
838 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
839 bw_p = num2str(bw, 6, 1, i2p);
840
841 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
842 iops_p = num2str(iops, 6, 1, 0);
843
844 box = gtk_hbox_new(FALSE, 3);
845 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
846
847 frame = gtk_frame_new(ddir_label[ddir]);
848 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
849
Jens Axboe0b761302012-03-05 20:44:11 +0100850 main_vbox = gtk_vbox_new(FALSE, 3);
851 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100852
853 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100854 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100855
856 label = new_info_label_in_frame(box, "IO");
857 gtk_label_set_text(GTK_LABEL(label), io_p);
858 label = new_info_label_in_frame(box, "Bandwidth");
859 gtk_label_set_text(GTK_LABEL(label), bw_p);
860 label = new_info_label_in_frame(box, "IOPS");
861 gtk_label_set_text(GTK_LABEL(label), iops_p);
862 label = new_info_label_in_frame(box, "Runtime (msec)");
863 label_set_int_value(label, ts->runtime[ddir]);
864
Jens Axboee0681f32012-03-06 12:14:42 +0100865 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100866 double p_of_agg = 100.0;
867 const char *bw_str = "KB";
868 char tmp[32];
869
870 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100871 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100872 if (p_of_agg > 100.0)
873 p_of_agg = 100.0;
874 }
875
Jens Axboee0681f32012-03-06 12:14:42 +0100876 if (mean[0] > 999999.9) {
877 min[0] /= 1000.0;
878 max[0] /= 1000.0;
879 mean[0] /= 1000.0;
880 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100881 bw_str = "MB";
882 }
883
Jens Axboe0b761302012-03-05 20:44:11 +0100884 sprintf(tmp, "Bandwidth (%s)", bw_str);
885 frame = gtk_frame_new(tmp);
886 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100887
Jens Axboe0b761302012-03-05 20:44:11 +0100888 box = gtk_hbox_new(FALSE, 3);
889 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100890
Jens Axboe0b761302012-03-05 20:44:11 +0100891 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100892 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100893 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100894 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100895 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100896 sprintf(tmp, "%3.2f%%", p_of_agg);
897 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100898 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100899 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100900 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100901 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100902 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100903 gtk_label_set_text(GTK_LABEL(label), tmp);
904 }
905
Jens Axboee0681f32012-03-06 12:14:42 +0100906 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100907 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100908 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100909 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100910 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100911 flags |= GFIO_LAT;
912
913 if (flags) {
914 frame = gtk_frame_new("Latency");
915 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
916
917 vbox = gtk_vbox_new(FALSE, 3);
918 gtk_container_add(GTK_CONTAINER(frame), vbox);
919
920 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100921 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100922 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100923 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100924 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100925 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100926 }
927
928 if (ts->clat_percentiles)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100929 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
Jens Axboe2b089892012-03-06 08:09:17 +0100930
Jens Axboe3650a3c2012-03-05 14:09:03 +0100931 free(io_p);
932 free(bw_p);
933 free(iops_p);
934}
935
Jens Axboe09d574e2012-03-15 10:45:48 +0100936static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
937 const char **labels,
938 unsigned int len,
939 double xdim, double ydim)
940{
941 struct graph *g;
942 int i;
943
944 g = graph_new(xdim, ydim, gfio_graph_font);
945 graph_title(g, title);
946 graph_x_title(g, "Buckets");
947
948 for (i = 0; i < len; i++) {
949 graph_add_label(g, labels[i]);
950 graph_add_data(g, labels[i], lat[i]);
951 }
952
953 return g;
954}
955
956static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
957 int num)
Jens Axboee5bd1342012-03-05 21:38:12 +0100958{
959 GtkWidget *tree_view;
960 GtkTreeSelection *selection;
961 GtkListStore *model;
962 GtkTreeIter iter;
963 GType *types;
Jens Axboe09d574e2012-03-15 10:45:48 +0100964 int i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100965
966 types = malloc(num * sizeof(GType));
967
968 for (i = 0; i < num; i++)
969 types[i] = G_TYPE_STRING;
970
971 model = gtk_list_store_newv(num, types);
972 free(types);
973 types = NULL;
974
975 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
976 gtk_widget_set_can_focus(tree_view, FALSE);
977
Jens Axboe661f7412012-03-06 13:55:45 +0100978 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
979 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
980
Jens Axboee5bd1342012-03-05 21:38:12 +0100981 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
982 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
983
984 for (i = 0; i < num; i++)
985 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
986
987 gtk_list_store_append(model, &iter);
988
989 for (i = 0; i < num; i++) {
990 char fbuf[32];
991
992 if (lat[i] <= 0.0)
993 sprintf(fbuf, "0.00");
994 else
995 sprintf(fbuf, "%3.2f%%", lat[i]);
996
997 gtk_list_store_set(model, &iter, i, fbuf, -1);
998 }
999
1000 return tree_view;
1001}
1002
Jens Axboe09d574e2012-03-15 10:45:48 +01001003static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
1004 struct thread_stat *ts)
Jens Axboee5bd1342012-03-05 21:38:12 +01001005{
Jens Axboe09d574e2012-03-15 10:45:48 +01001006 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
1007 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
1008 "250u", "500u", "750u", "1m", "2m",
1009 "4m", "10m", "20m", "50m", "100m",
1010 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
1011 int start, end, i;
1012 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
1013 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
1014 struct gui_entry *ge = gc->ge;
Jens Axboee5bd1342012-03-05 21:38:12 +01001015
Jens Axboe09d574e2012-03-15 10:45:48 +01001016 stat_calc_lat_u(ts, io_u_lat);
1017 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
Jens Axboee5bd1342012-03-05 21:38:12 +01001018
Jens Axboe09d574e2012-03-15 10:45:48 +01001019 /*
1020 * Found out which first bucket has entries, and which last bucket
1021 */
1022 start = end = -1U;
1023 for (i = 0; i < total; i++) {
1024 if (io_u_lat[i] == 0.00)
1025 continue;
Jens Axboee5bd1342012-03-05 21:38:12 +01001026
Jens Axboe09d574e2012-03-15 10:45:48 +01001027 if (start == -1U)
1028 start = i;
1029 end = i;
Jens Axboee5bd1342012-03-05 21:38:12 +01001030 }
1031
Jens Axboe09d574e2012-03-15 10:45:48 +01001032 /*
1033 * No entries...
1034 */
1035 if (start == -1U)
1036 return;
1037
1038 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
1039 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 +01001040
Jens Axboe09d574e2012-03-15 10:45:48 +01001041 frame = gtk_frame_new("Latency buckets");
1042 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1043
1044 completion_vbox = gtk_vbox_new(FALSE, 3);
1045 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
1046 hbox = gtk_hbox_new(FALSE, 3);
1047 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
1048
1049 drawing_area = gtk_drawing_area_new();
1050 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
1051 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
1052 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
1053 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
1054 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
1055
1056 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
Jens Axboee5bd1342012-03-05 21:38:12 +01001057}
1058
Jens Axboe2e331012012-03-05 22:07:54 +01001059static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
1060{
1061 GtkWidget *box, *frame, *entry;
1062 double usr_cpu, sys_cpu;
1063 unsigned long runtime;
1064 char tmp[32];
1065
1066 runtime = ts->total_run_time;
1067 if (runtime) {
1068 double runt = (double) runtime;
1069
1070 usr_cpu = (double) ts->usr_time * 100 / runt;
1071 sys_cpu = (double) ts->sys_time * 100 / runt;
1072 } else {
1073 usr_cpu = 0;
1074 sys_cpu = 0;
1075 }
1076
1077 frame = gtk_frame_new("OS resources");
1078 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1079
1080 box = gtk_hbox_new(FALSE, 3);
1081 gtk_container_add(GTK_CONTAINER(frame), box);
1082
1083 entry = new_info_entry_in_frame(box, "User CPU");
1084 sprintf(tmp, "%3.2f%%", usr_cpu);
1085 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1086 entry = new_info_entry_in_frame(box, "System CPU");
1087 sprintf(tmp, "%3.2f%%", sys_cpu);
1088 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1089 entry = new_info_entry_in_frame(box, "Context switches");
1090 entry_set_int_value(entry, ts->ctx);
1091 entry = new_info_entry_in_frame(box, "Major faults");
1092 entry_set_int_value(entry, ts->majf);
1093 entry = new_info_entry_in_frame(box, "Minor faults");
1094 entry_set_int_value(entry, ts->minf);
1095}
Jens Axboe19998db2012-03-06 09:17:59 +01001096static void gfio_add_sc_depths_tree(GtkListStore *model,
1097 struct thread_stat *ts, unsigned int len,
1098 int submit)
1099{
1100 double io_u_dist[FIO_IO_U_MAP_NR];
1101 GtkTreeIter iter;
1102 /* Bits 0, and 3-8 */
1103 const int add_mask = 0x1f9;
1104 int i, j;
1105
1106 if (submit)
1107 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1108 else
1109 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1110
1111 gtk_list_store_append(model, &iter);
1112
1113 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1114
1115 for (i = 1, j = 0; i < len; i++) {
1116 char fbuf[32];
1117
1118 if (!(add_mask & (1UL << (i - 1))))
1119 sprintf(fbuf, "0.0%%");
1120 else {
1121 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1122 j++;
1123 }
1124
1125 gtk_list_store_set(model, &iter, i, fbuf, -1);
1126 }
1127
1128}
1129
1130static void gfio_add_total_depths_tree(GtkListStore *model,
1131 struct thread_stat *ts, unsigned int len)
1132{
1133 double io_u_dist[FIO_IO_U_MAP_NR];
1134 GtkTreeIter iter;
1135 /* Bits 1-6, and 8 */
1136 const int add_mask = 0x17e;
1137 int i, j;
1138
1139 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1140
1141 gtk_list_store_append(model, &iter);
1142
1143 gtk_list_store_set(model, &iter, 0, "Total", -1);
1144
1145 for (i = 1, j = 0; i < len; i++) {
1146 char fbuf[32];
1147
1148 if (!(add_mask & (1UL << (i - 1))))
1149 sprintf(fbuf, "0.0%%");
1150 else {
1151 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1152 j++;
1153 }
1154
1155 gtk_list_store_set(model, &iter, i, fbuf, -1);
1156 }
1157
1158}
Jens Axboe2e331012012-03-05 22:07:54 +01001159
1160static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1161{
Jens Axboe2e331012012-03-05 22:07:54 +01001162 GtkWidget *frame, *box, *tree_view;
1163 GtkTreeSelection *selection;
1164 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +01001165 GType types[FIO_IO_U_MAP_NR + 1];
1166 int i;
Jens Axboe19998db2012-03-06 09:17:59 +01001167#define NR_LABELS 10
1168 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +01001169
1170 frame = gtk_frame_new("IO depths");
1171 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1172
1173 box = gtk_hbox_new(FALSE, 3);
1174 gtk_container_add(GTK_CONTAINER(frame), box);
1175
Jens Axboe19998db2012-03-06 09:17:59 +01001176 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001177 types[i] = G_TYPE_STRING;
1178
Jens Axboe19998db2012-03-06 09:17:59 +01001179 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +01001180
1181 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1182 gtk_widget_set_can_focus(tree_view, FALSE);
1183
Jens Axboe661f7412012-03-06 13:55:45 +01001184 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1185 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1186
Jens Axboe2e331012012-03-05 22:07:54 +01001187 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1188 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1189
Jens Axboe19998db2012-03-06 09:17:59 +01001190 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001191 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1192
Jens Axboe19998db2012-03-06 09:17:59 +01001193 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1194 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1195 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001196
1197 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1198}
1199
Jens Axboef9d40b42012-03-06 09:52:49 +01001200static gboolean results_window_delete(GtkWidget *w, gpointer data)
1201{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001202 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001203
1204 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001205 ge->results_window = NULL;
1206 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001207 return TRUE;
1208}
1209
Jens Axboe17b97212012-03-14 20:17:57 +01001210static void results_close(GtkWidget *w, gpointer *data)
1211{
1212 struct gui_entry *ge = (struct gui_entry *) data;
1213
1214 gtk_widget_destroy(ge->results_window);
1215}
1216
1217static GtkActionEntry results_menu_items[] = {
1218 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1219 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1220 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1221};
1222static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1223
1224static const gchar *results_ui_string = " \
1225 <ui> \
1226 <menubar name=\"MainMenu\"> \
1227 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1228 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1229 </menu> \
1230 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1231 </menu>\
1232 </menubar> \
1233 </ui> \
1234";
1235
1236static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1237{
1238 GtkActionGroup *action_group;
1239 GtkWidget *widget;
1240 GError *error = 0;
1241
1242 ge->results_uimanager = gtk_ui_manager_new();
1243
1244 action_group = gtk_action_group_new("ResultsMenu");
1245 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1246
1247 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1248 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1249
1250 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1251
1252 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1253 return widget;
1254}
1255
Jens Axboe2f99deb2012-03-09 14:37:29 +01001256static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001257{
Jens Axboe17b97212012-03-14 20:17:57 +01001258 GtkWidget *win, *notebook, *vbox;
Jens Axboef9d40b42012-03-06 09:52:49 +01001259
Jens Axboe2f99deb2012-03-09 14:37:29 +01001260 if (ge->results_window)
1261 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001262
1263 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1264 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001265 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001266 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1267 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001268
Jens Axboe17b97212012-03-14 20:17:57 +01001269 vbox = gtk_vbox_new(FALSE, 0);
1270 gtk_container_add(GTK_CONTAINER(win), vbox);
1271
1272 ge->results_menu = get_results_menubar(win, ge);
1273 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1274
Jens Axboef9d40b42012-03-06 09:52:49 +01001275 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001276 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1277 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboe17b97212012-03-14 20:17:57 +01001278 gtk_container_add(GTK_CONTAINER(vbox), notebook);
Jens Axboef9d40b42012-03-06 09:52:49 +01001279
Jens Axboe2f99deb2012-03-09 14:37:29 +01001280 ge->results_window = win;
1281 ge->results_notebook = notebook;
1282 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001283}
1284
Jens Axboe7da23b42012-03-15 13:45:02 +01001285static void disk_util_destroy(GtkWidget *w, gpointer data)
Jens Axboe3650a3c2012-03-05 14:09:03 +01001286{
Jens Axboe7da23b42012-03-15 13:45:02 +01001287 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001288
Jens Axboe7da23b42012-03-15 13:45:02 +01001289 ge->disk_util_vbox = NULL;
1290 gtk_widget_destroy(w);
Jens Axboe781ccba2012-03-15 09:44:42 +01001291}
Jens Axboe3650a3c2012-03-05 14:09:03 +01001292
Jens Axboe7da23b42012-03-15 13:45:02 +01001293static int __gfio_disk_util_show(GtkWidget *res_notebook,
1294 struct gfio_client *gc, struct cmd_du_pdu *p)
Jens Axboe781ccba2012-03-15 09:44:42 +01001295{
Jens Axboee0681f32012-03-06 12:14:42 +01001296 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe7da23b42012-03-15 13:45:02 +01001297 struct gui_entry *ge = gc->ge;
Jens Axboe604cfe32012-03-07 19:51:36 +01001298 double util;
1299 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001300
Jens Axboe7da23b42012-03-15 13:45:02 +01001301 res_notebook = get_results_window(ge);
Jens Axboee0681f32012-03-06 12:14:42 +01001302
Jens Axboe7da23b42012-03-15 13:45:02 +01001303 if (!ge->disk_util_vbox) {
Jens Axboe60d09292012-03-15 12:37:35 +01001304 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe7da23b42012-03-15 13:45:02 +01001305 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), vbox, gtk_label_new("Disk utilization"));
1306 ge->disk_util_vbox = vbox;
1307 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
Jens Axboee0681f32012-03-06 12:14:42 +01001308 }
1309
1310 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe7da23b42012-03-15 13:45:02 +01001311 gtk_container_add(GTK_CONTAINER(ge->disk_util_vbox), vbox);
Jens Axboee0681f32012-03-06 12:14:42 +01001312
1313 frame = gtk_frame_new((char *) p->dus.name);
1314 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1315
1316 box = gtk_vbox_new(FALSE, 3);
1317 gtk_container_add(GTK_CONTAINER(frame), box);
1318
1319 frame = gtk_frame_new("Read");
1320 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1321 vbox = gtk_hbox_new(TRUE, 3);
1322 gtk_container_add(GTK_CONTAINER(frame), vbox);
1323 entry = new_info_entry_in_frame(vbox, "IOs");
1324 entry_set_int_value(entry, p->dus.ios[0]);
1325 entry = new_info_entry_in_frame(vbox, "Merges");
1326 entry_set_int_value(entry, p->dus.merges[0]);
1327 entry = new_info_entry_in_frame(vbox, "Sectors");
1328 entry_set_int_value(entry, p->dus.sectors[0]);
1329 entry = new_info_entry_in_frame(vbox, "Ticks");
1330 entry_set_int_value(entry, p->dus.ticks[0]);
1331
1332 frame = gtk_frame_new("Write");
1333 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1334 vbox = gtk_hbox_new(TRUE, 3);
1335 gtk_container_add(GTK_CONTAINER(frame), vbox);
1336 entry = new_info_entry_in_frame(vbox, "IOs");
1337 entry_set_int_value(entry, p->dus.ios[1]);
1338 entry = new_info_entry_in_frame(vbox, "Merges");
1339 entry_set_int_value(entry, p->dus.merges[1]);
1340 entry = new_info_entry_in_frame(vbox, "Sectors");
1341 entry_set_int_value(entry, p->dus.sectors[1]);
1342 entry = new_info_entry_in_frame(vbox, "Ticks");
1343 entry_set_int_value(entry, p->dus.ticks[1]);
1344
1345 frame = gtk_frame_new("Shared");
1346 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1347 vbox = gtk_hbox_new(TRUE, 3);
1348 gtk_container_add(GTK_CONTAINER(frame), vbox);
1349 entry = new_info_entry_in_frame(vbox, "IO ticks");
1350 entry_set_int_value(entry, p->dus.io_ticks);
1351 entry = new_info_entry_in_frame(vbox, "Time in queue");
1352 entry_set_int_value(entry, p->dus.time_in_queue);
1353
Jens Axboe604cfe32012-03-07 19:51:36 +01001354 util = 0.0;
1355 if (p->dus.msec)
1356 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1357 if (util > 100.0)
1358 util = 100.0;
1359
1360 sprintf(tmp, "%3.2f%%", util);
1361 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1362 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1363
Jens Axboe7da23b42012-03-15 13:45:02 +01001364 gtk_widget_show_all(ge->results_window);
1365 return 0;
1366}
1367
1368static int gfio_disk_util_show(struct gfio_client *gc)
1369{
1370 struct gui_entry *ge = gc->ge;
1371 GtkWidget *res_notebook;
1372 int i;
1373
1374 if (!gc->nr_du)
1375 return 1;
1376
1377 res_notebook = get_results_window(ge);
1378
1379 for (i = 0; i < gc->nr_du; i++) {
1380 struct cmd_du_pdu *p = &gc->du[i];
1381
1382 __gfio_disk_util_show(res_notebook, gc, p);
1383 }
1384
1385 gtk_widget_show_all(ge->results_window);
1386 return 0;
1387}
1388
1389static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1390 struct group_run_stats *rs)
1391{
1392 unsigned int nr = gc->nr_results;
1393
1394 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1395 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1396 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1397 gc->nr_results++;
1398}
1399
1400static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1401 struct thread_stat *ts,
1402 struct group_run_stats *rs)
1403{
1404 GtkWidget *box, *vbox, *entry, *scroll;
1405
1406 scroll = gtk_scrolled_window_new(NULL, NULL);
1407 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1408 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1409
1410 vbox = gtk_vbox_new(FALSE, 3);
1411
1412 box = gtk_hbox_new(FALSE, 0);
1413 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1414
1415 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1416
1417 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1418
1419 entry = new_info_entry_in_frame(box, "Name");
1420 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1421 if (strlen(ts->description)) {
1422 entry = new_info_entry_in_frame(box, "Description");
1423 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1424 }
1425 entry = new_info_entry_in_frame(box, "Group ID");
1426 entry_set_int_value(entry, ts->groupid);
1427 entry = new_info_entry_in_frame(box, "Jobs");
1428 entry_set_int_value(entry, ts->members);
1429 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1430 entry_set_int_value(entry, ts->error);
1431 entry = new_info_entry_in_frame(box, "PID");
1432 entry_set_int_value(entry, ts->pid);
1433
1434 if (ts->io_bytes[DDIR_READ])
1435 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1436 if (ts->io_bytes[DDIR_WRITE])
1437 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1438
1439 gfio_show_latency_buckets(gc, vbox, ts);
1440 gfio_show_cpu_usage(vbox, ts);
1441 gfio_show_io_depths(vbox, ts);
1442}
1443
1444static void gfio_display_end_results(struct gfio_client *gc)
1445{
1446 struct gui_entry *ge = gc->ge;
1447 GtkWidget *res_notebook;
1448 int i;
1449
1450 res_notebook = get_results_window(ge);
1451
1452 for (i = 0; i < gc->nr_results; i++) {
1453 struct end_results *e = &gc->results[i];
1454
1455 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1456 }
1457
1458 if (gfio_disk_util_show(gc))
1459 gtk_widget_show_all(ge->results_window);
1460}
1461
1462static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1463 struct group_run_stats *rs)
1464{
1465 struct gfio_client *gc = client->client_data;
1466
1467 gfio_add_end_results(gc, ts, rs);
1468
1469 gdk_threads_enter();
1470 gfio_display_end_results(gc);
1471 gdk_threads_leave();
1472}
1473
1474static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1475{
1476 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1477 struct gui *ui = &main_ui;
1478 GtkTreeIter iter;
1479 struct tm *tm;
1480 time_t sec;
1481 char tmp[64], timebuf[80];
1482
1483 sec = p->log_sec;
1484 tm = localtime(&sec);
1485 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1486 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1487
1488 gdk_threads_enter();
1489
1490 gtk_list_store_append(ui->log_model, &iter);
1491 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1492 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1493 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1494 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1495
1496 if (p->level == FIO_LOG_ERR)
1497 view_log(NULL, (gpointer) ui);
1498
1499 gdk_threads_leave();
1500}
1501
1502static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1503{
1504 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1505 struct gfio_client *gc = client->client_data;
1506 unsigned int nr = gc->nr_du;
1507
1508 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1509 memcpy(&gc->du[nr], p, sizeof(*p));
1510 gc->nr_du++;
1511
1512 gdk_threads_enter();
1513 gfio_disk_util_show(gc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001514 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001515}
1516
Jens Axboe3650a3c2012-03-05 14:09:03 +01001517extern int sum_stat_clients;
1518extern struct thread_stat client_ts;
1519extern struct group_run_stats client_gs;
1520
1521static int sum_stat_nr;
1522
Jens Axboe89e5fad2012-03-05 09:21:12 +01001523static void gfio_thread_status_op(struct fio_client *client,
1524 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001525{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001526 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1527
1528 gfio_display_ts(client, &p->ts, &p->rs);
1529
1530 if (sum_stat_clients == 1)
1531 return;
1532
1533 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1534 sum_group_stats(&client_gs, &p->rs);
1535
1536 client_ts.members++;
Jens Axboe2f122b12012-03-15 13:10:19 +01001537 client_ts.thread_number = p->ts.thread_number;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001538 client_ts.groupid = p->ts.groupid;
1539
1540 if (++sum_stat_nr == sum_stat_clients) {
1541 strcpy(client_ts.name, "All clients");
1542 gfio_display_ts(client, &client_ts, &client_gs);
1543 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001544}
1545
Jens Axboe89e5fad2012-03-05 09:21:12 +01001546static void gfio_group_stats_op(struct fio_client *client,
1547 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001548{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001549 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001550}
1551
Jens Axboe2f99deb2012-03-09 14:37:29 +01001552static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1553 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001554{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001555 struct gfio_graphs *g = data;
1556
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001557 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1558 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1559 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1560 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001561 return TRUE;
1562}
1563
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001564static void draw_graph(struct graph *g, cairo_t *cr)
1565{
1566 line_graph_draw(g, cr);
1567 cairo_stroke(cr);
1568}
1569
Jens Axboe93e2db22012-03-13 09:45:22 +01001570static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1571 gboolean keyboard_mode, GtkTooltip *tooltip,
1572 gpointer data)
1573{
1574 struct gfio_graphs *g = data;
1575 const char *text = NULL;
1576
1577 if (graph_contains_xy(g->iops_graph, x, y))
1578 text = graph_find_tooltip(g->iops_graph, x, y);
1579 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1580 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1581
1582 if (text) {
1583 gtk_tooltip_set_text(tooltip, text);
1584 return TRUE;
1585 }
1586
1587 return FALSE;
1588}
1589
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001590static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1591{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001592 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001593 cairo_t *cr;
1594
1595 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001596
1597 if (graph_has_tooltips(g->iops_graph) ||
1598 graph_has_tooltips(g->bandwidth_graph)) {
1599 g_object_set(w, "has-tooltip", TRUE, NULL);
1600 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1601 }
1602
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001603 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001604 draw_graph(g->iops_graph, cr);
1605 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001606 cairo_destroy(cr);
1607
1608 return FALSE;
1609}
1610
Jens Axboe2f99deb2012-03-09 14:37:29 +01001611/*
1612 * Client specific ETA
1613 */
1614static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001615{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001616 struct gfio_client *gc = client->client_data;
1617 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001618 static int eta_good;
1619 char eta_str[128];
1620 char output[256];
1621 char tmp[32];
1622 double perc = 0.0;
1623 int i2p = 0;
1624
Jens Axboe0050e5f2012-03-06 09:23:27 +01001625 gdk_threads_enter();
1626
Jens Axboe3e47bd22012-02-29 13:45:02 +01001627 eta_str[0] = '\0';
1628 output[0] = '\0';
1629
1630 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1631 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1632 eta_to_str(eta_str, je->eta_sec);
1633 }
1634
1635 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001636 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001637 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001638 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001639
1640#if 0
1641 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1642 if (je->m_rate || je->t_rate) {
1643 char *tr, *mr;
1644
1645 mr = num2str(je->m_rate, 4, 0, i2p);
1646 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001647 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001648 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1649 free(tr);
1650 free(mr);
1651 } else if (je->m_iops || je->t_iops)
1652 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001653
Jens Axboe2f99deb2012-03-09 14:37:29 +01001654 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1655 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1656 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1657 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001658#endif
1659
1660 if (je->eta_sec != INT_MAX && je->nr_running) {
1661 char *iops_str[2];
1662 char *rate_str[2];
1663
1664 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1665 strcpy(output, "-.-% done");
1666 else {
1667 eta_good = 1;
1668 perc *= 100.0;
1669 sprintf(output, "%3.1f%% done", perc);
1670 }
1671
1672 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1673 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1674
1675 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1676 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1677
Jens Axboe2f99deb2012-03-09 14:37:29 +01001678 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1679 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1680 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1681 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001682
Jens Axboe93e2db22012-03-13 09:45:22 +01001683 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1684 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1685 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1686 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 +01001687
1688 free(rate_str[0]);
1689 free(rate_str[1]);
1690 free(iops_str[0]);
1691 free(iops_str[1]);
1692 }
1693
1694 if (eta_str[0]) {
1695 char *dst = output + strlen(output);
1696
1697 sprintf(dst, " - %s", eta_str);
1698 }
1699
Jens Axboe9988ca72012-03-09 15:14:06 +01001700 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001701 gdk_threads_leave();
1702}
1703
1704/*
1705 * Update ETA in main window for all clients
1706 */
1707static void gfio_update_all_eta(struct jobs_eta *je)
1708{
1709 struct gui *ui = &main_ui;
1710 static int eta_good;
1711 char eta_str[128];
1712 char output[256];
1713 double perc = 0.0;
1714 int i2p = 0;
1715
1716 gdk_threads_enter();
1717
1718 eta_str[0] = '\0';
1719 output[0] = '\0';
1720
1721 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1722 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1723 eta_to_str(eta_str, je->eta_sec);
1724 }
1725
1726#if 0
1727 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1728 if (je->m_rate || je->t_rate) {
1729 char *tr, *mr;
1730
1731 mr = num2str(je->m_rate, 4, 0, i2p);
1732 tr = num2str(je->t_rate, 4, 0, i2p);
1733 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1734 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1735 free(tr);
1736 free(mr);
1737 } else if (je->m_iops || je->t_iops)
1738 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1739
1740 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1741 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1742 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1743 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1744#endif
1745
Jens Axboe3863d1a2012-03-09 17:39:05 +01001746 entry_set_int_value(ui->eta.jobs, je->nr_running);
1747
Jens Axboe2f99deb2012-03-09 14:37:29 +01001748 if (je->eta_sec != INT_MAX && je->nr_running) {
1749 char *iops_str[2];
1750 char *rate_str[2];
1751
1752 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1753 strcpy(output, "-.-% done");
1754 else {
1755 eta_good = 1;
1756 perc *= 100.0;
1757 sprintf(output, "%3.1f%% done", perc);
1758 }
1759
1760 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1761 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1762
1763 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1764 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1765
1766 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1767 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1768 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1769 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1770
Jens Axboe93e2db22012-03-13 09:45:22 +01001771 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1772 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1773 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1774 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 +01001775
Jens Axboe3e47bd22012-02-29 13:45:02 +01001776 free(rate_str[0]);
1777 free(rate_str[1]);
1778 free(iops_str[0]);
1779 free(iops_str[1]);
1780 }
1781
1782 if (eta_str[0]) {
1783 char *dst = output + strlen(output);
1784
1785 sprintf(dst, " - %s", eta_str);
1786 }
1787
Jens Axboe9988ca72012-03-09 15:14:06 +01001788 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001789 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001790}
1791
Stephen M. Camerona1820202012-02-24 08:17:31 +01001792static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1793{
Jens Axboe843ad232012-02-29 11:44:53 +01001794 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001795 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001796 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001797 const char *os, *arch;
1798 char buf[64];
1799
1800 os = fio_get_os_string(probe->os);
1801 if (!os)
1802 os = "unknown";
1803
1804 arch = fio_get_arch_string(probe->arch);
1805 if (!arch)
1806 os = "unknown";
1807
1808 if (!client->name)
1809 client->name = strdup((char *) probe->hostname);
1810
Jens Axboe0050e5f2012-03-06 09:23:27 +01001811 gdk_threads_enter();
1812
Jens Axboe2f99deb2012-03-09 14:37:29 +01001813 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1814 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1815 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001816 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001817 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001818
Jens Axboe85dd01e2012-03-12 14:33:16 +01001819 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001820
1821 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001822}
1823
Jens Axboe9988ca72012-03-09 15:14:06 +01001824static void gfio_update_thread_status(struct gui_entry *ge,
1825 char *status_message, double perc)
1826{
1827 static char message[100];
1828 const char *m = message;
1829
1830 strncpy(message, status_message, sizeof(message) - 1);
1831 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1832 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1833 gtk_widget_queue_draw(main_ui.window);
1834}
1835
1836static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001837{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001838 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001839 static char message[100];
1840 const char *m = message;
1841
1842 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001843 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1844 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1845 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001846}
1847
Jens Axboe35c0ba72012-03-14 10:56:40 +01001848static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001849{
Jens Axboee0681f32012-03-06 12:14:42 +01001850 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001851
Jens Axboe0050e5f2012-03-06 09:23:27 +01001852 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001853 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001854 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001855}
1856
Jens Axboe807f9972012-03-02 10:25:24 +01001857static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1858{
1859 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001860 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001861 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001862 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001863 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001864
Jens Axboe2f122b12012-03-15 13:10:19 +01001865 p->thread_number = le32_to_cpu(p->thread_number);
1866 p->groupid = le32_to_cpu(p->groupid);
Jens Axboedcaeb602012-03-08 19:45:37 +01001867 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001868
Jens Axboe0050e5f2012-03-06 09:23:27 +01001869 gdk_threads_enter();
1870
Jens Axboe2f99deb2012-03-09 14:37:29 +01001871 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1872
Jens Axboe3863d1a2012-03-09 17:39:05 +01001873 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1874 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1875
Jens Axboec80b74b2012-03-12 10:23:28 +01001876 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1877 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001878
Jens Axboedcaeb602012-03-08 19:45:37 +01001879 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001880 multitext_add_entry(&ge->eta.iodepth, tmp);
1881
1882 multitext_set_entry(&ge->eta.iotype, 0);
1883 multitext_set_entry(&ge->eta.ioengine, 0);
1884 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001885
Jens Axboedcaeb602012-03-08 19:45:37 +01001886 gc->job_added++;
1887
Jens Axboe85dd01e2012-03-12 14:33:16 +01001888 gfio_set_state(ge, GE_STATE_JOB_SENT);
1889
Jens Axboe0050e5f2012-03-06 09:23:27 +01001890 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001891}
1892
Jens Axboeed727a42012-03-02 12:14:40 +01001893static void gfio_client_timed_out(struct fio_client *client)
1894{
Jens Axboee0681f32012-03-06 12:14:42 +01001895 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001896 char buf[256];
1897
1898 gdk_threads_enter();
1899
Jens Axboe85dd01e2012-03-12 14:33:16 +01001900 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001901 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001902
1903 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001904 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001905
1906 gdk_threads_leave();
1907}
1908
Jens Axboe6b79c802012-03-08 10:51:36 +01001909static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1910{
1911 struct gfio_client *gc = client->client_data;
1912
1913 gdk_threads_enter();
1914
Jens Axboe85dd01e2012-03-12 14:33:16 +01001915 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001916
1917 if (gc->err_entry)
1918 entry_set_int_value(gc->err_entry, client->error);
1919
1920 gdk_threads_leave();
1921}
1922
Jens Axboe85dd01e2012-03-12 14:33:16 +01001923static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1924{
1925 struct gfio_client *gc = client->client_data;
1926
1927 gdk_threads_enter();
1928 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1929 gdk_threads_leave();
1930}
1931
1932static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1933{
1934 struct gfio_client *gc = client->client_data;
1935
1936 gdk_threads_enter();
1937 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1938 gdk_threads_leave();
1939}
1940
Jens Axboe1b427252012-03-14 15:03:03 +01001941static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1942{
Jens Axboe284b1e62012-03-14 21:55:07 +01001943 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 +01001944 free(pdu);
1945}
1946
Stephen M. Camerona1820202012-02-24 08:17:31 +01001947struct client_ops gfio_client_ops = {
Jens Axboe35c0ba72012-03-14 10:56:40 +01001948 .text = gfio_text_op,
Jens Axboe0420ba62012-02-29 11:16:52 +01001949 .disk_util = gfio_disk_util_op,
1950 .thread_status = gfio_thread_status_op,
1951 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001952 .jobs_eta = gfio_update_client_eta,
1953 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001954 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001955 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001956 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001957 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001958 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001959 .start = gfio_client_start,
1960 .job_start = gfio_client_job_start,
Jens Axboe1b427252012-03-14 15:03:03 +01001961 .iolog = gfio_client_iolog,
Jens Axboe6433ee02012-03-09 20:10:51 +01001962 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001963 .stay_connected = 1,
Jens Axboe46bcd492012-03-14 11:31:21 +01001964 .client_type = FIO_CLIENT_TYPE_GUI,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001965};
1966
Jens Axboe0fd18982012-03-14 10:34:48 +01001967/*
1968 * FIXME: need more handling here
1969 */
1970static void ge_destroy(struct gui_entry *ge)
1971{
1972 struct gfio_client *gc = ge->client;
1973
1974 if (gc && gc->client) {
1975 if (ge->state >= GE_STATE_CONNECTED)
1976 fio_client_terminate(gc->client);
1977
1978 fio_put_client(gc->client);
1979 }
1980
1981 flist_del(&ge->list);
1982 free(ge);
1983}
1984
1985static void ge_widget_destroy(GtkWidget *w, gpointer data)
1986{
Jens Axboe0fd18982012-03-14 10:34:48 +01001987}
1988
1989static void gfio_quit(struct gui *ui)
1990{
1991 struct gui_entry *ge;
1992
1993 while (!flist_empty(&ui->list)) {
1994 ge = flist_entry(ui->list.next, struct gui_entry, list);
1995 ge_destroy(ge);
1996 }
1997
1998 gtk_main_quit();
1999}
2000
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002001static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
2002 __attribute__((unused)) gpointer data)
2003{
Jens Axboe0fd18982012-03-14 10:34:48 +01002004 gfio_quit(data);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002005}
2006
Stephen M. Cameron25927252012-02-24 08:17:31 +01002007static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002008{
Jens Axboea9eccde2012-03-09 14:59:42 +01002009 struct gui *ui = arg;
2010
2011 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01002012 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01002013 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01002014 return NULL;
2015}
2016
Jens Axboe2f99deb2012-03-09 14:37:29 +01002017static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01002018{
Jens Axboe9988ca72012-03-09 15:14:06 +01002019 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01002020 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01002021
Jens Axboe2f99deb2012-03-09 14:37:29 +01002022 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01002023 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01002024 if (ret < 0) {
2025 GError *error;
2026
2027 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
2028 report_error(error);
2029 g_error_free(error);
2030 break;
2031 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01002032 break;
2033
Jens Axboe2f99deb2012-03-09 14:37:29 +01002034 free(ge->job_files[i]);
2035 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01002036 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002037 while (i < ge->nr_job_files) {
2038 free(ge->job_files[i]);
2039 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01002040 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01002041 }
2042
Jens Axboe2c77d832012-03-13 19:02:04 +01002043 free(ge->job_files);
2044 ge->job_files = NULL;
Jens Axboe3af45202012-03-13 09:59:53 +01002045 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01002046 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01002047}
2048
Jens Axboe63a130b2012-03-06 20:08:59 +01002049static void *server_thread(void *arg)
2050{
2051 is_backend = 1;
2052 gfio_server_running = 1;
2053 fio_start_server(NULL);
2054 gfio_server_running = 0;
2055 return NULL;
2056}
2057
Jens Axboe2f99deb2012-03-09 14:37:29 +01002058static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01002059{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002060 struct gui *ui = &main_ui;
2061
Jens Axboe63a130b2012-03-06 20:08:59 +01002062 if (!gfio_server_running) {
2063 gfio_server_running = 1;
2064 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01002065 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01002066 }
2067}
2068
Stephen M. Cameron25927252012-02-24 08:17:31 +01002069static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
2070 gpointer data)
2071{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002072 struct gui_entry *ge = data;
2073 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01002074
Jens Axboe78cb2fe2012-03-12 23:05:29 +01002075 if (gc)
2076 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002077}
2078
Jens Axboedf06f222012-03-02 13:32:04 +01002079static void file_open(GtkWidget *w, gpointer data);
2080
2081static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01002082{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002083 struct gui_entry *ge = data;
2084 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002085
Jens Axboe85dd01e2012-03-12 14:33:16 +01002086 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01002087 int ret;
2088
Jens Axboe2f99deb2012-03-09 14:37:29 +01002089 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01002090 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002091 if (!ge->nr_job_files)
2092 return;
2093
Jens Axboed3b70f32012-03-15 14:05:01 +01002094 gc = ge->client;
2095
Jens Axboe2f99deb2012-03-09 14:37:29 +01002096 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2097 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01002098 ret = fio_client_connect(gc->client);
2099 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01002100 if (!ge->ui->handler_running)
2101 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002102 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01002103 } else {
2104 GError *error;
2105
2106 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2107 report_error(error);
2108 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01002109 }
Jens Axboedf06f222012-03-02 13:32:04 +01002110 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002111 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002112 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002113 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01002114 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01002115}
2116
Jens Axboeb9d2f302012-03-08 20:36:28 +01002117static void send_clicked(GtkWidget *widget, gpointer data)
2118{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002119 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01002120
Jens Axboe2f99deb2012-03-09 14:37:29 +01002121 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01002122 GError *error;
2123
2124 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);
2125 report_error(error);
2126 g_error_free(error);
2127
Jens Axboe2f99deb2012-03-09 14:37:29 +01002128 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002129 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01002130}
2131
Jens Axboe0420ba62012-02-29 11:16:52 +01002132static void on_info_bar_response(GtkWidget *widget, gint response,
2133 gpointer data)
2134{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002135 struct gui *ui = &main_ui;
2136
Jens Axboe0420ba62012-02-29 11:16:52 +01002137 if (response == GTK_RESPONSE_OK) {
2138 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002139 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01002140 }
2141}
2142
Jens Axboedf06f222012-03-02 13:32:04 +01002143void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01002144{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002145 struct gui *ui = &main_ui;
2146
2147 if (ui->error_info_bar == NULL) {
2148 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01002149 GTK_RESPONSE_OK,
2150 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002151 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2152 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01002153 GTK_MESSAGE_ERROR);
2154
Jens Axboe2f99deb2012-03-09 14:37:29 +01002155 ui->error_label = gtk_label_new(error->message);
2156 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2157 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01002158
Jens Axboe2f99deb2012-03-09 14:37:29 +01002159 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2160 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01002161 } else {
2162 char buffer[256];
2163 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002164 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01002165 }
2166}
2167
Jens Axboe62bc9372012-03-07 11:45:07 +01002168struct connection_widgets
2169{
2170 GtkWidget *hentry;
2171 GtkWidget *combo;
2172 GtkWidget *button;
2173};
2174
2175static void hostname_cb(GtkEntry *entry, gpointer data)
2176{
2177 struct connection_widgets *cw = data;
2178 int uses_net = 0, is_localhost = 0;
2179 const gchar *text;
2180 gchar *ctext;
2181
2182 /*
2183 * Check whether to display the 'auto start backend' box
2184 * or not. Show it if we are a localhost and using network,
2185 * or using a socket.
2186 */
2187 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2188 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2189 uses_net = 1;
2190 g_free(ctext);
2191
2192 if (uses_net) {
2193 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2194 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2195 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2196 !strcmp(text, "ip6-loopback"))
2197 is_localhost = 1;
2198 }
2199
2200 if (!uses_net || is_localhost) {
2201 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2202 gtk_widget_set_sensitive(cw->button, 1);
2203 } else {
2204 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2205 gtk_widget_set_sensitive(cw->button, 0);
2206 }
2207}
2208
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002209static int get_connection_details(char **host, int *port, int *type,
2210 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01002211{
Jens Axboe62bc9372012-03-07 11:45:07 +01002212 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2213 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002214 char *typeentry;
2215
2216 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002217 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01002218 GTK_DIALOG_DESTROY_WITH_PARENT,
2219 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2220 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2221
2222 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01002223 /* gtk_dialog_get_content_area() is 2.14 and newer */
2224 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002225 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2226
2227 box = gtk_vbox_new(FALSE, 6);
2228 gtk_container_add(GTK_CONTAINER(frame), box);
2229
2230 hbox = gtk_hbox_new(TRUE, 10);
2231 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01002232 cw.hentry = gtk_entry_new();
2233 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2234 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002235
2236 frame = gtk_frame_new("Port");
2237 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2238 box = gtk_vbox_new(FALSE, 10);
2239 gtk_container_add(GTK_CONTAINER(frame), box);
2240
2241 hbox = gtk_hbox_new(TRUE, 4);
2242 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2243 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2244
2245 frame = gtk_frame_new("Type");
2246 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2247 box = gtk_vbox_new(FALSE, 10);
2248 gtk_container_add(GTK_CONTAINER(frame), box);
2249
2250 hbox = gtk_hbox_new(TRUE, 4);
2251 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2252
Jens Axboe62bc9372012-03-07 11:45:07 +01002253 cw.combo = gtk_combo_box_new_text();
2254 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2255 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2256 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2257 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002258
Jens Axboe62bc9372012-03-07 11:45:07 +01002259 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002260
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002261 frame = gtk_frame_new("Options");
2262 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2263 box = gtk_vbox_new(FALSE, 10);
2264 gtk_container_add(GTK_CONTAINER(frame), box);
2265
2266 hbox = gtk_hbox_new(TRUE, 4);
2267 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2268
Jens Axboe62bc9372012-03-07 11:45:07 +01002269 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2270 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2271 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.");
2272 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2273
2274 /*
2275 * Connect edit signal, so we can show/not-show the auto start button
2276 */
2277 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2278 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002279
Jens Axboea7a42ce2012-03-02 13:12:04 +01002280 gtk_widget_show_all(dialog);
2281
2282 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2283 gtk_widget_destroy(dialog);
2284 return 1;
2285 }
2286
Jens Axboe62bc9372012-03-07 11:45:07 +01002287 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002288 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2289
Jens Axboe62bc9372012-03-07 11:45:07 +01002290 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002291 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2292 *type = Fio_client_ipv4;
2293 else if (!strncmp(typeentry, "IPv6", 4))
2294 *type = Fio_client_ipv6;
2295 else
2296 *type = Fio_client_socket;
2297 g_free(typeentry);
2298
Jens Axboe62bc9372012-03-07 11:45:07 +01002299 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002300
Jens Axboea7a42ce2012-03-02 13:12:04 +01002301 gtk_widget_destroy(dialog);
2302 return 0;
2303}
2304
Jens Axboe2f99deb2012-03-09 14:37:29 +01002305static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002306{
2307 struct gfio_client *gc;
2308
2309 gc = malloc(sizeof(*gc));
2310 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002311 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002312 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002313
Jens Axboe2f99deb2012-03-09 14:37:29 +01002314 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002315
2316 client->client_data = gc;
2317}
2318
Jens Axboe2f99deb2012-03-09 14:37:29 +01002319static GtkWidget *new_client_page(struct gui_entry *ge);
2320
2321static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2322{
2323 struct gui_entry *ge;
2324
2325 ge = malloc(sizeof(*ge));
2326 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002327 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002328 INIT_FLIST_HEAD(&ge->list);
2329 flist_add_tail(&ge->list, &ui->list);
2330 ge->ui = ui;
2331 return ge;
2332}
2333
Jens Axboe2f99deb2012-03-09 14:37:29 +01002334static struct gui_entry *get_new_ge_with_tab(const char *name)
2335{
2336 struct gui_entry *ge;
2337
2338 ge = alloc_new_gui_entry(&main_ui);
2339
2340 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002341 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002342
2343 ge->page_label = gtk_label_new(name);
2344 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2345
2346 gtk_widget_show_all(main_ui.window);
2347 return ge;
2348}
2349
2350static void file_new(GtkWidget *w, gpointer data)
2351{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002352 struct gui *ui = (struct gui *) data;
2353 struct gui_entry *ge;
2354
2355 ge = get_new_ge_with_tab("Untitled");
2356 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002357}
2358
2359/*
2360 * Return the 'ge' corresponding to the tab. If the active tab is the
2361 * main tab, open a new tab.
2362 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002363static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002364{
2365 struct flist_head *entry;
2366 struct gui_entry *ge;
2367
Jens Axboe38634cb2012-03-13 12:26:41 +01002368 if (!cur_page) {
2369 if (created)
2370 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002371 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002372 }
2373
2374 if (created)
2375 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002376
2377 flist_for_each(entry, &main_ui.list) {
2378 ge = flist_entry(entry, struct gui_entry, list);
2379 if (ge->page_num == cur_page)
2380 return ge;
2381 }
2382
2383 return NULL;
2384}
2385
Jens Axboe85dd01e2012-03-12 14:33:16 +01002386static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2387{
2388 gint cur_page;
2389
2390 /*
2391 * Main tab is tab 0, so any current page other than 0 holds
2392 * a ge entry.
2393 */
2394 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2395 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002396 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002397
2398 return NULL;
2399}
2400
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002401static void file_close(GtkWidget *w, gpointer data)
2402{
2403 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002404 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002405
2406 /*
2407 * Can't close the main tab
2408 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002409 ge = get_ge_from_cur_tab(ui);
2410 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002411 gtk_widget_destroy(ge->vbox);
2412 return;
2413 }
2414
Jens Axboef5c67262012-03-13 08:20:41 +01002415 if (!flist_empty(&ui->list)) {
2416 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2417 return;
2418 }
2419
Jens Axboe0fd18982012-03-14 10:34:48 +01002420 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002421}
2422
Jens Axboe38634cb2012-03-13 12:26:41 +01002423static void file_add_recent(struct gui *ui, const gchar *uri)
2424{
Jens Axboea217ba72012-03-13 20:29:39 +01002425 GtkRecentData grd;
2426
2427 memset(&grd, 0, sizeof(grd));
2428 grd.display_name = strdup("gfio");
2429 grd.description = strdup("Fio job file");
2430 grd.mime_type = strdup(GFIO_MIME);
2431 grd.app_name = strdup(g_get_application_name());
2432 grd.app_exec = strdup("gfio %f/%u");
2433
2434 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002435}
2436
2437static gchar *get_filename_from_uri(const gchar *uri)
2438{
2439 if (strncmp(uri, "file://", 7))
2440 return strdup(uri);
2441
2442 return strdup(uri + 7);
2443}
2444
2445static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2446 int type, int port)
2447{
2448 struct fio_client *client;
2449 gchar *filename;
2450
2451 filename = get_filename_from_uri(uri);
2452
2453 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2454 ge->job_files[ge->nr_job_files] = strdup(filename);
2455 ge->nr_job_files++;
2456
2457 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2458 if (!client) {
2459 GError *error;
2460
2461 error = g_error_new(g_quark_from_string("fio"), 1,
2462 "Failed to add client %s", host);
2463 report_error(error);
2464 g_error_free(error);
2465 return 1;
2466 }
2467
2468 gfio_client_added(ge, client);
2469 file_add_recent(ge->ui, uri);
2470 return 0;
2471}
2472
Jens Axboea6790902012-03-13 15:16:11 +01002473static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002474{
Jens Axboea6790902012-03-13 15:16:11 +01002475 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002476 struct gui_entry *ge;
2477 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002478 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002479 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002480
2481 /*
2482 * Creates new tab if current tab is the main window, or the
2483 * current tab already has a client.
2484 */
2485 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002486 ge = get_ge_from_page(cur_page, &ge_is_new);
2487 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002488 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002489 ge_is_new = 1;
2490 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002491
2492 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002493
Jens Axboea6790902012-03-13 15:16:11 +01002494 if (get_connection_details(&host, &port, &type, &server_start)) {
2495 if (ge_is_new)
2496 gtk_widget_destroy(ge->vbox);
2497
2498 return 1;
2499 }
2500
2501 ret = do_file_open(ge, uri, host, type, port);
2502
2503 free(host);
2504
2505 if (!ret) {
2506 if (server_start)
2507 gfio_start_server();
2508 } else {
2509 if (ge_is_new)
2510 gtk_widget_destroy(ge->vbox);
2511 }
2512
2513 return ret;
2514}
2515
2516static void recent_open(GtkAction *action, gpointer data)
2517{
2518 struct gui *ui = (struct gui *) data;
2519 GtkRecentInfo *info;
2520 const gchar *uri;
2521
2522 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2523 uri = gtk_recent_info_get_uri(info);
2524
2525 do_file_open_with_tab(ui, uri);
2526}
2527
2528static void file_open(GtkWidget *w, gpointer data)
2529{
2530 struct gui *ui = data;
2531 GtkWidget *dialog;
2532 GSList *filenames, *fn_glist;
2533 GtkFileFilter *filter;
2534
Jens Axboe0420ba62012-02-29 11:16:52 +01002535 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002536 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002537 GTK_FILE_CHOOSER_ACTION_OPEN,
2538 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2539 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2540 NULL);
2541 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2542
2543 filter = gtk_file_filter_new();
2544 gtk_file_filter_add_pattern(filter, "*.fio");
2545 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002546 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002547 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002548 gtk_file_filter_set_name(filter, "Fio job file");
2549 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2550
2551 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2552 gtk_widget_destroy(dialog);
2553 return;
2554 }
2555
2556 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002557
2558 gtk_widget_destroy(dialog);
2559
Jens Axboe0420ba62012-02-29 11:16:52 +01002560 filenames = fn_glist;
2561 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002562 if (do_file_open_with_tab(ui, filenames->data))
2563 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002564 filenames = g_slist_next(filenames);
2565 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002566
Jens Axboe0420ba62012-02-29 11:16:52 +01002567 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002568}
2569
2570static void file_save(GtkWidget *w, gpointer data)
2571{
Jens Axboe63a130b2012-03-06 20:08:59 +01002572 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002573 GtkWidget *dialog;
2574
2575 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002576 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002577 GTK_FILE_CHOOSER_ACTION_SAVE,
2578 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2579 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2580 NULL);
2581
2582 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2583 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2584
2585 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2586 char *filename;
2587
2588 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2589 // save_job_file(filename);
2590 g_free(filename);
2591 }
2592 gtk_widget_destroy(dialog);
2593}
2594
Jens Axboe9b260bd2012-03-06 11:02:52 +01002595static void view_log_destroy(GtkWidget *w, gpointer data)
2596{
2597 struct gui *ui = (struct gui *) data;
2598
2599 gtk_widget_ref(ui->log_tree);
2600 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2601 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002602 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002603}
2604
2605static void view_log(GtkWidget *w, gpointer data)
2606{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002607 GtkWidget *win, *scroll, *vbox, *box;
2608 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002609
Jens Axboe4cbe7212012-03-06 13:36:17 +01002610 if (ui->log_view)
2611 return;
2612
2613 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002614 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002615 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002616
Jens Axboe4cbe7212012-03-06 13:36:17 +01002617 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002618
Jens Axboe4cbe7212012-03-06 13:36:17 +01002619 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2620
2621 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2622
2623 box = gtk_hbox_new(TRUE, 0);
2624 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2625 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2626 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2627
2628 vbox = gtk_vbox_new(TRUE, 5);
2629 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2630
2631 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002632 gtk_widget_show_all(win);
2633}
2634
Jens Axboe85dd01e2012-03-12 14:33:16 +01002635static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002636{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002637 struct gui *ui = (struct gui *) data;
2638 struct gui_entry *ge;
2639
2640 ge = get_ge_from_cur_tab(ui);
2641 if (ge)
2642 connect_clicked(w, ge);
2643}
2644
2645static void send_job_entry(GtkWidget *w, gpointer data)
2646{
2647 struct gui *ui = (struct gui *) data;
2648 struct gui_entry *ge;
2649
2650 ge = get_ge_from_cur_tab(ui);
2651 if (ge)
2652 send_clicked(w, ge);
2653
2654}
2655
2656static void edit_job_entry(GtkWidget *w, gpointer data)
2657{
2658}
2659
2660static void start_job_entry(GtkWidget *w, gpointer data)
2661{
2662 struct gui *ui = (struct gui *) data;
2663 struct gui_entry *ge;
2664
2665 ge = get_ge_from_cur_tab(ui);
2666 if (ge)
2667 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002668}
2669
Jens Axboe781ccba2012-03-15 09:44:42 +01002670static void view_results(GtkWidget *w, gpointer data)
2671{
2672 struct gui *ui = (struct gui *) data;
2673 struct gfio_client *gc;
2674 struct gui_entry *ge;
2675
2676 ge = get_ge_from_cur_tab(ui);
2677 if (!ge)
2678 return;
2679
2680 if (ge->results_window)
2681 return;
2682
2683 gc = ge->client;
2684 if (gc && gc->nr_results)
2685 gfio_display_end_results(gc);
2686}
2687
Jens Axboe8577f4f2012-03-09 19:28:27 +01002688static void __update_graph_limits(struct gfio_graphs *g)
2689{
2690 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2691 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2692}
2693
2694static void update_graph_limits(void)
2695{
2696 struct flist_head *entry;
2697 struct gui_entry *ge;
2698
2699 __update_graph_limits(&main_ui.graphs);
2700
2701 flist_for_each(entry, &main_ui.list) {
2702 ge = flist_entry(entry, struct gui_entry, list);
2703 __update_graph_limits(&ge->graphs);
2704 }
2705}
2706
Jens Axboe46974a72012-03-02 19:34:13 +01002707static void preferences(GtkWidget *w, gpointer data)
2708{
Jens Axboef3e84402012-03-07 13:14:32 +01002709 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002710 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002711 int i;
2712
2713 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002714 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002715 GTK_DIALOG_DESTROY_WITH_PARENT,
2716 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2717 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2718 NULL);
2719
Jens Axboe8577f4f2012-03-09 19:28:27 +01002720 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002721 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2722 vbox = gtk_vbox_new(FALSE, 6);
2723 gtk_container_add(GTK_CONTAINER(frame), vbox);
2724
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002725 hbox = gtk_hbox_new(FALSE, 5);
2726 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2727 entry = gtk_label_new("Font face to use for graph labels");
2728 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2729
Jens Axboef3e84402012-03-07 13:14:32 +01002730 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002731 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002732
Jens Axboe8577f4f2012-03-09 19:28:27 +01002733 box = gtk_vbox_new(FALSE, 6);
2734 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2735
2736 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002737 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002738 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2739 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2740
Jens Axboec05d9052012-03-11 13:05:35 +01002741 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002742
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002743 box = gtk_vbox_new(FALSE, 6);
2744 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2745
2746 hbox = gtk_hbox_new(FALSE, 5);
2747 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2748 entry = gtk_label_new("Client ETA request interval (msec)");
2749 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2750
2751 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002752 frame = gtk_frame_new("Debug logging");
2753 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2754 vbox = gtk_vbox_new(FALSE, 6);
2755 gtk_container_add(GTK_CONTAINER(frame), vbox);
2756
2757 box = gtk_hbox_new(FALSE, 6);
2758 gtk_container_add(GTK_CONTAINER(vbox), box);
2759
2760 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2761
2762 for (i = 0; i < FD_DEBUG_MAX; i++) {
2763 if (i == 7) {
2764 box = gtk_hbox_new(FALSE, 6);
2765 gtk_container_add(GTK_CONTAINER(vbox), box);
2766 }
2767
2768
2769 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2770 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2771 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2772 }
2773
Jens Axboe46974a72012-03-02 19:34:13 +01002774 gtk_widget_show_all(dialog);
2775
2776 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2777 gtk_widget_destroy(dialog);
2778 return;
2779 }
2780
2781 for (i = 0; i < FD_DEBUG_MAX; i++) {
2782 int set;
2783
2784 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2785 if (set)
2786 fio_debug |= (1UL << i);
2787 }
2788
Jens Axboef3e84402012-03-07 13:14:32 +01002789 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002790 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2791 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002792 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002793
Jens Axboe46974a72012-03-02 19:34:13 +01002794 gtk_widget_destroy(dialog);
2795}
2796
Jens Axboe0420ba62012-02-29 11:16:52 +01002797static void about_dialog(GtkWidget *w, gpointer data)
2798{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002799 const char *authors[] = {
2800 "Jens Axboe <axboe@kernel.dk>",
2801 "Stephen Carmeron <stephenmcameron@gmail.com>",
2802 NULL
2803 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002804 const char *license[] = {
2805 "Fio is free software; you can redistribute it and/or modify "
2806 "it under the terms of the GNU General Public License as published by "
2807 "the Free Software Foundation; either version 2 of the License, or "
2808 "(at your option) any later version.\n",
2809 "Fio is distributed in the hope that it will be useful, "
2810 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2811 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2812 "GNU General Public License for more details.\n",
2813 "You should have received a copy of the GNU General Public License "
2814 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2815 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2816 };
2817 char *license_trans;
2818
2819 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2820 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002821
Jens Axboe0420ba62012-02-29 11:16:52 +01002822 gtk_show_about_dialog(NULL,
2823 "program-name", "gfio",
2824 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002825 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002826 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2827 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002828 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002829 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002830 "logo-icon-name", "fio",
2831 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002832 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002833 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002834
Jens Axboe2f99deb2012-03-09 14:37:29 +01002835 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002836}
2837
2838static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002839 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002840 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002841 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002842 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002843 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002844 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002845 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2846 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2847 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002848 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01002849 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002850 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2851 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2852 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2853 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002854 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2855 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002856};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002857static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002858
2859static const gchar *ui_string = " \
2860 <ui> \
2861 <menubar name=\"MainMenu\"> \
2862 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002863 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01002864 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002865 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002866 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002867 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002868 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002869 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2870 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002871 <placeholder name=\"FileRecentFiles\"/> \
2872 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002873 <menuitem name=\"Quit\" action=\"Quit\" /> \
2874 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002875 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002876 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002877 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002878 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2879 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002880 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002881 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002882 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002883 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01002884 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2885 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002886 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2887 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002888 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2889 <menuitem name=\"About\" action=\"About\" /> \
2890 </menu> \
2891 </menubar> \
2892 </ui> \
2893";
2894
Jens Axboe4cbe7212012-03-06 13:36:17 +01002895static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2896 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002897{
Jens Axboeca664f42012-03-14 19:49:40 +01002898 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002899 GError *error = 0;
2900
2901 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002902 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002903
2904 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2905 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2906
2907 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002908
Jens Axboe0420ba62012-02-29 11:16:52 +01002909 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2910}
2911
2912void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2913 GtkWidget *vbox, GtkUIManager *ui_manager)
2914{
2915 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2916}
2917
Jens Axboec80b74b2012-03-12 10:23:28 +01002918static void combo_entry_changed(GtkComboBox *box, gpointer data)
2919{
2920 struct gui_entry *ge = (struct gui_entry *) data;
2921 gint index;
2922
2923 index = gtk_combo_box_get_active(box);
2924
2925 multitext_set_entry(&ge->eta.iotype, index);
2926 multitext_set_entry(&ge->eta.ioengine, index);
2927 multitext_set_entry(&ge->eta.iodepth, index);
2928}
2929
2930static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2931{
2932 struct gui_entry *ge = (struct gui_entry *) data;
2933
2934 multitext_free(&ge->eta.iotype);
2935 multitext_free(&ge->eta.ioengine);
2936 multitext_free(&ge->eta.iodepth);
2937}
2938
Jens Axboe2f99deb2012-03-09 14:37:29 +01002939static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002940{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002941 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002942 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01002943
Jens Axboe2f99deb2012-03-09 14:37:29 +01002944 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002945
Jens Axboe65476332012-03-13 10:37:04 +01002946 top_align = gtk_alignment_new(0, 0, 1, 0);
2947 top_vbox = gtk_vbox_new(FALSE, 3);
2948 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2949 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002950
Jens Axboe3e47bd22012-02-29 13:45:02 +01002951 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002952 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002953 probe_frame = gtk_vbox_new(FALSE, 3);
2954 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2955
2956 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002957 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2958 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2959 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2960 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2961 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002962
Jens Axboe3e47bd22012-02-29 13:45:02 +01002963 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002964 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2965
Jens Axboe3863d1a2012-03-09 17:39:05 +01002966 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002967 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2968 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2969 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2970 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2971 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002972 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2973 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2974
2975 probe_box = gtk_hbox_new(FALSE, 3);
2976 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2977 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2978 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2979 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2980 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2981
2982 /*
2983 * Only add this if we have a commit rate
2984 */
2985#if 0
2986 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002987 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002988
Jens Axboe2f99deb2012-03-09 14:37:29 +01002989 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2990 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2991
2992 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2993 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2994#endif
2995
2996 /*
2997 * Set up a drawing area and IOPS and bandwidth graphs
2998 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002999 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01003000 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01003001 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003002 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3003 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3004 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3005 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3006 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01003007 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3008 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003009 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01003010 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003011 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01003012 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003013
3014 setup_graphs(&ge->graphs);
3015
3016 /*
3017 * Set up alignments for widgets at the bottom of ui,
3018 * align bottom left, expand horizontally but not vertically
3019 */
Jens Axboe65476332012-03-13 10:37:04 +01003020 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003021 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003022 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3023 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003024
3025 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3026
3027 /*
3028 * Set up thread status progress bar
3029 */
3030 ge->thread_status_pb = gtk_progress_bar_new();
3031 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3032 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3033 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3034
3035
3036 return main_vbox;
3037}
3038
3039static GtkWidget *new_main_page(struct gui *ui)
3040{
3041 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01003042 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003043
3044 main_vbox = gtk_vbox_new(FALSE, 3);
3045
3046 /*
3047 * Set up alignments for widgets at the top of ui,
3048 * align top left, expand horizontally but not vertically
3049 */
Jens Axboe65476332012-03-13 10:37:04 +01003050 top_align = gtk_alignment_new(0, 0, 1, 0);
3051 top_vbox = gtk_vbox_new(FALSE, 0);
3052 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3053 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003054
3055 probe = gtk_frame_new("Run statistics");
3056 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3057 probe_frame = gtk_vbox_new(FALSE, 3);
3058 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01003059
3060 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003061 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01003062 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01003063 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3064 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3065 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3066 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01003067
3068 /*
3069 * Only add this if we have a commit rate
3070 */
3071#if 0
3072 probe_box = gtk_hbox_new(FALSE, 3);
3073 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3074
Jens Axboe3e47bd22012-02-29 13:45:02 +01003075 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3076 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3077
Jens Axboe3e47bd22012-02-29 13:45:02 +01003078 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3079 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01003080#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01003081
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01003082 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003083 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003084 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01003085 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01003086 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01003087 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003088 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3089 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3090 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3091 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3092 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01003093 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3094 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003095 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01003096 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003097 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01003098 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01003099 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003100
Jens Axboe2f99deb2012-03-09 14:37:29 +01003101 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003102
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003103 /*
3104 * Set up alignments for widgets at the bottom of ui,
3105 * align bottom left, expand horizontally but not vertically
3106 */
Jens Axboe65476332012-03-13 10:37:04 +01003107 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003108 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003109 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3110 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003111
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003112 /*
3113 * Set up thread status progress bar
3114 */
3115 ui->thread_status_pb = gtk_progress_bar_new();
3116 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01003117 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003118 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3119
Jens Axboe2f99deb2012-03-09 14:37:29 +01003120 return main_vbox;
3121}
3122
3123static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3124 guint page, gpointer data)
3125
3126{
Jens Axboe02421e62012-03-12 12:05:50 +01003127 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01003128 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01003129
Jens Axboe85dd01e2012-03-12 14:33:16 +01003130 if (!page) {
3131 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01003132 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003133 return TRUE;
3134 }
3135
3136 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01003137 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003138 if (ge)
3139 update_button_states(ui, ge);
3140
Jens Axboe2f99deb2012-03-09 14:37:29 +01003141 return TRUE;
3142}
3143
Jens Axboe38634cb2012-03-13 12:26:41 +01003144static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3145{
3146 time_t time_a = gtk_recent_info_get_visited(a);
3147 time_t time_b = gtk_recent_info_get_visited(b);
3148
3149 return time_b - time_a;
3150}
3151
3152static void add_recent_file_items(struct gui *ui)
3153{
3154 const gchar *gfio = g_get_application_name();
3155 GList *items, *item;
3156 int i = 0;
3157
3158 if (ui->recent_ui_id) {
3159 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3160 gtk_ui_manager_ensure_update(ui->uimanager);
3161 }
3162 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3163
3164 if (ui->actiongroup) {
3165 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3166 g_object_unref(ui->actiongroup);
3167 }
3168 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3169
3170 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3171
3172 items = gtk_recent_manager_get_items(ui->recentmanager);
3173 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3174
3175 for (item = items; item && item->data; item = g_list_next(item)) {
3176 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3177 gchar *action_name;
3178 const gchar *label;
3179 GtkAction *action;
3180
3181 if (!gtk_recent_info_has_application(info, gfio))
3182 continue;
3183
3184 /*
3185 * We only support local files for now
3186 */
3187 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3188 continue;
3189
3190 action_name = g_strdup_printf("RecentFile%u", i++);
3191 label = gtk_recent_info_get_display_name(info);
3192
3193 action = g_object_new(GTK_TYPE_ACTION,
3194 "name", action_name,
3195 "label", label, NULL);
3196
3197 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3198 gtk_recent_info_ref(info),
3199 (GDestroyNotify) gtk_recent_info_unref);
3200
3201
3202 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3203
3204 gtk_action_group_add_action(ui->actiongroup, action);
3205 g_object_unref(action);
3206
3207 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3208 "/MainMenu/FileMenu/FileRecentFiles",
3209 label, action_name,
3210 GTK_UI_MANAGER_MENUITEM, FALSE);
3211
3212 g_free(action_name);
3213
3214 if (i == 8)
3215 break;
3216 }
3217
3218 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3219 g_list_free(items);
3220}
3221
Jens Axboea6790902012-03-13 15:16:11 +01003222static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3223 gint x, gint y, GtkSelectionData *data,
3224 guint info, guint time)
3225{
3226 struct gui *ui = &main_ui;
3227 gchar **uris;
3228 GtkWidget *source;
3229 int i;
3230
3231 source = gtk_drag_get_source_widget(ctx);
3232 if (source && widget == gtk_widget_get_toplevel(source)) {
3233 gtk_drag_finish(ctx, FALSE, FALSE, time);
3234 return;
3235 }
3236
3237 uris = gtk_selection_data_get_uris(data);
3238 if (!uris) {
3239 gtk_drag_finish(ctx, FALSE, FALSE, time);
3240 return;
3241 }
3242
3243 i = 0;
3244 while (uris[i]) {
3245 if (do_file_open_with_tab(ui, uris[i]))
3246 break;
3247 i++;
3248 }
3249
3250 gtk_drag_finish(ctx, TRUE, FALSE, time);
3251 g_strfreev(uris);
3252}
3253
Jens Axboe2f99deb2012-03-09 14:37:29 +01003254static void init_ui(int *argc, char **argv[], struct gui *ui)
3255{
3256 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003257 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003258
3259 /* Magical g*thread incantation, you just need this thread stuff.
3260 * Without it, the update that happens in gfio_update_thread_status
3261 * doesn't really happen in a timely fashion, you need expose events
3262 */
3263 if (!g_thread_supported())
3264 g_thread_init(NULL);
3265 gdk_threads_init();
3266
3267 gtk_init(argc, argv);
3268 settings = gtk_settings_get_default();
3269 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3270 g_type_init();
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003271 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003272
3273 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003274 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003275 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3276
3277 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3278 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3279
3280 ui->vbox = gtk_vbox_new(FALSE, 0);
3281 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3282
Jens Axboe02421e62012-03-12 12:05:50 +01003283 ui->uimanager = gtk_ui_manager_new();
3284 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3285 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003286
Jens Axboe38634cb2012-03-13 12:26:41 +01003287 ui->recentmanager = gtk_recent_manager_get_default();
3288 add_recent_file_items(ui);
3289
Jens Axboe2f99deb2012-03-09 14:37:29 +01003290 ui->notebook = gtk_notebook_new();
3291 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003292 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003293 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003294 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3295
3296 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003297 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3298 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3299 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003300
3301 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3302
Jens Axboe9b260bd2012-03-06 11:02:52 +01003303 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003304
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003305 gtk_widget_show_all(ui->window);
3306}
3307
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003308int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003309{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003310 if (initialize_fio(envp))
3311 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003312 if (fio_init_options())
3313 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003314
Jens Axboe2f99deb2012-03-09 14:37:29 +01003315 memset(&main_ui, 0, sizeof(main_ui));
3316 INIT_FLIST_HEAD(&main_ui.list);
3317
3318 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003319
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003320 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003321 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003322 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003323 return 0;
3324}