blob: 93c8e171c131b6a4db460f65666741ba0f31a756 [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
2094 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
2095 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01002096 ret = fio_client_connect(gc->client);
2097 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01002098 if (!ge->ui->handler_running)
2099 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002100 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01002101 } else {
2102 GError *error;
2103
2104 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
2105 report_error(error);
2106 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01002107 }
Jens Axboedf06f222012-03-02 13:32:04 +01002108 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002109 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002110 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002111 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01002112 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01002113}
2114
Jens Axboeb9d2f302012-03-08 20:36:28 +01002115static void send_clicked(GtkWidget *widget, gpointer data)
2116{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002117 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01002118
Jens Axboe2f99deb2012-03-09 14:37:29 +01002119 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01002120 GError *error;
2121
2122 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);
2123 report_error(error);
2124 g_error_free(error);
2125
Jens Axboe2f99deb2012-03-09 14:37:29 +01002126 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002127 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01002128}
2129
Jens Axboe0420ba62012-02-29 11:16:52 +01002130static void on_info_bar_response(GtkWidget *widget, gint response,
2131 gpointer data)
2132{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002133 struct gui *ui = &main_ui;
2134
Jens Axboe0420ba62012-02-29 11:16:52 +01002135 if (response == GTK_RESPONSE_OK) {
2136 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002137 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01002138 }
2139}
2140
Jens Axboedf06f222012-03-02 13:32:04 +01002141void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01002142{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002143 struct gui *ui = &main_ui;
2144
2145 if (ui->error_info_bar == NULL) {
2146 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01002147 GTK_RESPONSE_OK,
2148 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002149 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2150 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01002151 GTK_MESSAGE_ERROR);
2152
Jens Axboe2f99deb2012-03-09 14:37:29 +01002153 ui->error_label = gtk_label_new(error->message);
2154 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2155 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01002156
Jens Axboe2f99deb2012-03-09 14:37:29 +01002157 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2158 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01002159 } else {
2160 char buffer[256];
2161 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002162 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01002163 }
2164}
2165
Jens Axboe62bc9372012-03-07 11:45:07 +01002166struct connection_widgets
2167{
2168 GtkWidget *hentry;
2169 GtkWidget *combo;
2170 GtkWidget *button;
2171};
2172
2173static void hostname_cb(GtkEntry *entry, gpointer data)
2174{
2175 struct connection_widgets *cw = data;
2176 int uses_net = 0, is_localhost = 0;
2177 const gchar *text;
2178 gchar *ctext;
2179
2180 /*
2181 * Check whether to display the 'auto start backend' box
2182 * or not. Show it if we are a localhost and using network,
2183 * or using a socket.
2184 */
2185 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2186 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2187 uses_net = 1;
2188 g_free(ctext);
2189
2190 if (uses_net) {
2191 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2192 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2193 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2194 !strcmp(text, "ip6-loopback"))
2195 is_localhost = 1;
2196 }
2197
2198 if (!uses_net || is_localhost) {
2199 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2200 gtk_widget_set_sensitive(cw->button, 1);
2201 } else {
2202 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2203 gtk_widget_set_sensitive(cw->button, 0);
2204 }
2205}
2206
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002207static int get_connection_details(char **host, int *port, int *type,
2208 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01002209{
Jens Axboe62bc9372012-03-07 11:45:07 +01002210 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2211 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002212 char *typeentry;
2213
2214 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002215 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01002216 GTK_DIALOG_DESTROY_WITH_PARENT,
2217 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2218 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2219
2220 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01002221 /* gtk_dialog_get_content_area() is 2.14 and newer */
2222 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002223 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2224
2225 box = gtk_vbox_new(FALSE, 6);
2226 gtk_container_add(GTK_CONTAINER(frame), box);
2227
2228 hbox = gtk_hbox_new(TRUE, 10);
2229 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01002230 cw.hentry = gtk_entry_new();
2231 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2232 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002233
2234 frame = gtk_frame_new("Port");
2235 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2236 box = gtk_vbox_new(FALSE, 10);
2237 gtk_container_add(GTK_CONTAINER(frame), box);
2238
2239 hbox = gtk_hbox_new(TRUE, 4);
2240 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2241 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2242
2243 frame = gtk_frame_new("Type");
2244 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2245 box = gtk_vbox_new(FALSE, 10);
2246 gtk_container_add(GTK_CONTAINER(frame), box);
2247
2248 hbox = gtk_hbox_new(TRUE, 4);
2249 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2250
Jens Axboe62bc9372012-03-07 11:45:07 +01002251 cw.combo = gtk_combo_box_new_text();
2252 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2253 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2254 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2255 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002256
Jens Axboe62bc9372012-03-07 11:45:07 +01002257 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002258
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002259 frame = gtk_frame_new("Options");
2260 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2261 box = gtk_vbox_new(FALSE, 10);
2262 gtk_container_add(GTK_CONTAINER(frame), box);
2263
2264 hbox = gtk_hbox_new(TRUE, 4);
2265 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2266
Jens Axboe62bc9372012-03-07 11:45:07 +01002267 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2268 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2269 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.");
2270 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2271
2272 /*
2273 * Connect edit signal, so we can show/not-show the auto start button
2274 */
2275 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2276 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002277
Jens Axboea7a42ce2012-03-02 13:12:04 +01002278 gtk_widget_show_all(dialog);
2279
2280 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2281 gtk_widget_destroy(dialog);
2282 return 1;
2283 }
2284
Jens Axboe62bc9372012-03-07 11:45:07 +01002285 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002286 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2287
Jens Axboe62bc9372012-03-07 11:45:07 +01002288 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002289 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2290 *type = Fio_client_ipv4;
2291 else if (!strncmp(typeentry, "IPv6", 4))
2292 *type = Fio_client_ipv6;
2293 else
2294 *type = Fio_client_socket;
2295 g_free(typeentry);
2296
Jens Axboe62bc9372012-03-07 11:45:07 +01002297 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002298
Jens Axboea7a42ce2012-03-02 13:12:04 +01002299 gtk_widget_destroy(dialog);
2300 return 0;
2301}
2302
Jens Axboe2f99deb2012-03-09 14:37:29 +01002303static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002304{
2305 struct gfio_client *gc;
2306
2307 gc = malloc(sizeof(*gc));
2308 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002309 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002310 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002311
Jens Axboe2f99deb2012-03-09 14:37:29 +01002312 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002313
2314 client->client_data = gc;
2315}
2316
Jens Axboe2f99deb2012-03-09 14:37:29 +01002317static GtkWidget *new_client_page(struct gui_entry *ge);
2318
2319static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2320{
2321 struct gui_entry *ge;
2322
2323 ge = malloc(sizeof(*ge));
2324 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002325 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002326 INIT_FLIST_HEAD(&ge->list);
2327 flist_add_tail(&ge->list, &ui->list);
2328 ge->ui = ui;
2329 return ge;
2330}
2331
Jens Axboe2f99deb2012-03-09 14:37:29 +01002332static struct gui_entry *get_new_ge_with_tab(const char *name)
2333{
2334 struct gui_entry *ge;
2335
2336 ge = alloc_new_gui_entry(&main_ui);
2337
2338 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002339 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002340
2341 ge->page_label = gtk_label_new(name);
2342 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2343
2344 gtk_widget_show_all(main_ui.window);
2345 return ge;
2346}
2347
2348static void file_new(GtkWidget *w, gpointer data)
2349{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002350 struct gui *ui = (struct gui *) data;
2351 struct gui_entry *ge;
2352
2353 ge = get_new_ge_with_tab("Untitled");
2354 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002355}
2356
2357/*
2358 * Return the 'ge' corresponding to the tab. If the active tab is the
2359 * main tab, open a new tab.
2360 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002361static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002362{
2363 struct flist_head *entry;
2364 struct gui_entry *ge;
2365
Jens Axboe38634cb2012-03-13 12:26:41 +01002366 if (!cur_page) {
2367 if (created)
2368 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002369 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002370 }
2371
2372 if (created)
2373 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002374
2375 flist_for_each(entry, &main_ui.list) {
2376 ge = flist_entry(entry, struct gui_entry, list);
2377 if (ge->page_num == cur_page)
2378 return ge;
2379 }
2380
2381 return NULL;
2382}
2383
Jens Axboe85dd01e2012-03-12 14:33:16 +01002384static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2385{
2386 gint cur_page;
2387
2388 /*
2389 * Main tab is tab 0, so any current page other than 0 holds
2390 * a ge entry.
2391 */
2392 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2393 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002394 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002395
2396 return NULL;
2397}
2398
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002399static void file_close(GtkWidget *w, gpointer data)
2400{
2401 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002402 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002403
2404 /*
2405 * Can't close the main tab
2406 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002407 ge = get_ge_from_cur_tab(ui);
2408 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002409 gtk_widget_destroy(ge->vbox);
2410 return;
2411 }
2412
Jens Axboef5c67262012-03-13 08:20:41 +01002413 if (!flist_empty(&ui->list)) {
2414 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2415 return;
2416 }
2417
Jens Axboe0fd18982012-03-14 10:34:48 +01002418 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002419}
2420
Jens Axboe38634cb2012-03-13 12:26:41 +01002421static void file_add_recent(struct gui *ui, const gchar *uri)
2422{
Jens Axboea217ba72012-03-13 20:29:39 +01002423 GtkRecentData grd;
2424
2425 memset(&grd, 0, sizeof(grd));
2426 grd.display_name = strdup("gfio");
2427 grd.description = strdup("Fio job file");
2428 grd.mime_type = strdup(GFIO_MIME);
2429 grd.app_name = strdup(g_get_application_name());
2430 grd.app_exec = strdup("gfio %f/%u");
2431
2432 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002433}
2434
2435static gchar *get_filename_from_uri(const gchar *uri)
2436{
2437 if (strncmp(uri, "file://", 7))
2438 return strdup(uri);
2439
2440 return strdup(uri + 7);
2441}
2442
2443static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2444 int type, int port)
2445{
2446 struct fio_client *client;
2447 gchar *filename;
2448
2449 filename = get_filename_from_uri(uri);
2450
2451 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2452 ge->job_files[ge->nr_job_files] = strdup(filename);
2453 ge->nr_job_files++;
2454
2455 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2456 if (!client) {
2457 GError *error;
2458
2459 error = g_error_new(g_quark_from_string("fio"), 1,
2460 "Failed to add client %s", host);
2461 report_error(error);
2462 g_error_free(error);
2463 return 1;
2464 }
2465
2466 gfio_client_added(ge, client);
2467 file_add_recent(ge->ui, uri);
2468 return 0;
2469}
2470
Jens Axboea6790902012-03-13 15:16:11 +01002471static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002472{
Jens Axboea6790902012-03-13 15:16:11 +01002473 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002474 struct gui_entry *ge;
2475 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002476 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002477 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002478
2479 /*
2480 * Creates new tab if current tab is the main window, or the
2481 * current tab already has a client.
2482 */
2483 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002484 ge = get_ge_from_page(cur_page, &ge_is_new);
2485 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002486 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002487 ge_is_new = 1;
2488 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002489
2490 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002491
Jens Axboea6790902012-03-13 15:16:11 +01002492 if (get_connection_details(&host, &port, &type, &server_start)) {
2493 if (ge_is_new)
2494 gtk_widget_destroy(ge->vbox);
2495
2496 return 1;
2497 }
2498
2499 ret = do_file_open(ge, uri, host, type, port);
2500
2501 free(host);
2502
2503 if (!ret) {
2504 if (server_start)
2505 gfio_start_server();
2506 } else {
2507 if (ge_is_new)
2508 gtk_widget_destroy(ge->vbox);
2509 }
2510
2511 return ret;
2512}
2513
2514static void recent_open(GtkAction *action, gpointer data)
2515{
2516 struct gui *ui = (struct gui *) data;
2517 GtkRecentInfo *info;
2518 const gchar *uri;
2519
2520 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2521 uri = gtk_recent_info_get_uri(info);
2522
2523 do_file_open_with_tab(ui, uri);
2524}
2525
2526static void file_open(GtkWidget *w, gpointer data)
2527{
2528 struct gui *ui = data;
2529 GtkWidget *dialog;
2530 GSList *filenames, *fn_glist;
2531 GtkFileFilter *filter;
2532
Jens Axboe0420ba62012-02-29 11:16:52 +01002533 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002534 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002535 GTK_FILE_CHOOSER_ACTION_OPEN,
2536 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2537 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2538 NULL);
2539 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2540
2541 filter = gtk_file_filter_new();
2542 gtk_file_filter_add_pattern(filter, "*.fio");
2543 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002544 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002545 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002546 gtk_file_filter_set_name(filter, "Fio job file");
2547 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2548
2549 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2550 gtk_widget_destroy(dialog);
2551 return;
2552 }
2553
2554 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002555
2556 gtk_widget_destroy(dialog);
2557
Jens Axboe0420ba62012-02-29 11:16:52 +01002558 filenames = fn_glist;
2559 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002560 if (do_file_open_with_tab(ui, filenames->data))
2561 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002562 filenames = g_slist_next(filenames);
2563 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002564
Jens Axboe0420ba62012-02-29 11:16:52 +01002565 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002566}
2567
2568static void file_save(GtkWidget *w, gpointer data)
2569{
Jens Axboe63a130b2012-03-06 20:08:59 +01002570 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002571 GtkWidget *dialog;
2572
2573 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002574 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002575 GTK_FILE_CHOOSER_ACTION_SAVE,
2576 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2577 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2578 NULL);
2579
2580 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2581 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2582
2583 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2584 char *filename;
2585
2586 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2587 // save_job_file(filename);
2588 g_free(filename);
2589 }
2590 gtk_widget_destroy(dialog);
2591}
2592
Jens Axboe9b260bd2012-03-06 11:02:52 +01002593static void view_log_destroy(GtkWidget *w, gpointer data)
2594{
2595 struct gui *ui = (struct gui *) data;
2596
2597 gtk_widget_ref(ui->log_tree);
2598 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2599 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002600 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002601}
2602
2603static void view_log(GtkWidget *w, gpointer data)
2604{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002605 GtkWidget *win, *scroll, *vbox, *box;
2606 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002607
Jens Axboe4cbe7212012-03-06 13:36:17 +01002608 if (ui->log_view)
2609 return;
2610
2611 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002612 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002613 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002614
Jens Axboe4cbe7212012-03-06 13:36:17 +01002615 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002616
Jens Axboe4cbe7212012-03-06 13:36:17 +01002617 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2618
2619 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2620
2621 box = gtk_hbox_new(TRUE, 0);
2622 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2623 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2624 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2625
2626 vbox = gtk_vbox_new(TRUE, 5);
2627 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2628
2629 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002630 gtk_widget_show_all(win);
2631}
2632
Jens Axboe85dd01e2012-03-12 14:33:16 +01002633static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002634{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002635 struct gui *ui = (struct gui *) data;
2636 struct gui_entry *ge;
2637
2638 ge = get_ge_from_cur_tab(ui);
2639 if (ge)
2640 connect_clicked(w, ge);
2641}
2642
2643static void send_job_entry(GtkWidget *w, gpointer data)
2644{
2645 struct gui *ui = (struct gui *) data;
2646 struct gui_entry *ge;
2647
2648 ge = get_ge_from_cur_tab(ui);
2649 if (ge)
2650 send_clicked(w, ge);
2651
2652}
2653
2654static void edit_job_entry(GtkWidget *w, gpointer data)
2655{
2656}
2657
2658static void start_job_entry(GtkWidget *w, gpointer data)
2659{
2660 struct gui *ui = (struct gui *) data;
2661 struct gui_entry *ge;
2662
2663 ge = get_ge_from_cur_tab(ui);
2664 if (ge)
2665 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002666}
2667
Jens Axboe781ccba2012-03-15 09:44:42 +01002668static void view_results(GtkWidget *w, gpointer data)
2669{
2670 struct gui *ui = (struct gui *) data;
2671 struct gfio_client *gc;
2672 struct gui_entry *ge;
2673
2674 ge = get_ge_from_cur_tab(ui);
2675 if (!ge)
2676 return;
2677
2678 if (ge->results_window)
2679 return;
2680
2681 gc = ge->client;
2682 if (gc && gc->nr_results)
2683 gfio_display_end_results(gc);
2684}
2685
Jens Axboe8577f4f2012-03-09 19:28:27 +01002686static void __update_graph_limits(struct gfio_graphs *g)
2687{
2688 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2689 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2690}
2691
2692static void update_graph_limits(void)
2693{
2694 struct flist_head *entry;
2695 struct gui_entry *ge;
2696
2697 __update_graph_limits(&main_ui.graphs);
2698
2699 flist_for_each(entry, &main_ui.list) {
2700 ge = flist_entry(entry, struct gui_entry, list);
2701 __update_graph_limits(&ge->graphs);
2702 }
2703}
2704
Jens Axboe46974a72012-03-02 19:34:13 +01002705static void preferences(GtkWidget *w, gpointer data)
2706{
Jens Axboef3e84402012-03-07 13:14:32 +01002707 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002708 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002709 int i;
2710
2711 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002712 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002713 GTK_DIALOG_DESTROY_WITH_PARENT,
2714 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2715 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2716 NULL);
2717
Jens Axboe8577f4f2012-03-09 19:28:27 +01002718 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002719 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2720 vbox = gtk_vbox_new(FALSE, 6);
2721 gtk_container_add(GTK_CONTAINER(frame), vbox);
2722
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002723 hbox = gtk_hbox_new(FALSE, 5);
2724 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2725 entry = gtk_label_new("Font face to use for graph labels");
2726 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2727
Jens Axboef3e84402012-03-07 13:14:32 +01002728 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002729 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002730
Jens Axboe8577f4f2012-03-09 19:28:27 +01002731 box = gtk_vbox_new(FALSE, 6);
2732 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2733
2734 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002735 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002736 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2737 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2738
Jens Axboec05d9052012-03-11 13:05:35 +01002739 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002740
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002741 box = gtk_vbox_new(FALSE, 6);
2742 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2743
2744 hbox = gtk_hbox_new(FALSE, 5);
2745 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2746 entry = gtk_label_new("Client ETA request interval (msec)");
2747 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2748
2749 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002750 frame = gtk_frame_new("Debug logging");
2751 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2752 vbox = gtk_vbox_new(FALSE, 6);
2753 gtk_container_add(GTK_CONTAINER(frame), vbox);
2754
2755 box = gtk_hbox_new(FALSE, 6);
2756 gtk_container_add(GTK_CONTAINER(vbox), box);
2757
2758 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2759
2760 for (i = 0; i < FD_DEBUG_MAX; i++) {
2761 if (i == 7) {
2762 box = gtk_hbox_new(FALSE, 6);
2763 gtk_container_add(GTK_CONTAINER(vbox), box);
2764 }
2765
2766
2767 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2768 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2769 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2770 }
2771
Jens Axboe46974a72012-03-02 19:34:13 +01002772 gtk_widget_show_all(dialog);
2773
2774 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2775 gtk_widget_destroy(dialog);
2776 return;
2777 }
2778
2779 for (i = 0; i < FD_DEBUG_MAX; i++) {
2780 int set;
2781
2782 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2783 if (set)
2784 fio_debug |= (1UL << i);
2785 }
2786
Jens Axboef3e84402012-03-07 13:14:32 +01002787 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002788 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2789 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002790 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002791
Jens Axboe46974a72012-03-02 19:34:13 +01002792 gtk_widget_destroy(dialog);
2793}
2794
Jens Axboe0420ba62012-02-29 11:16:52 +01002795static void about_dialog(GtkWidget *w, gpointer data)
2796{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002797 const char *authors[] = {
2798 "Jens Axboe <axboe@kernel.dk>",
2799 "Stephen Carmeron <stephenmcameron@gmail.com>",
2800 NULL
2801 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002802 const char *license[] = {
2803 "Fio is free software; you can redistribute it and/or modify "
2804 "it under the terms of the GNU General Public License as published by "
2805 "the Free Software Foundation; either version 2 of the License, or "
2806 "(at your option) any later version.\n",
2807 "Fio is distributed in the hope that it will be useful, "
2808 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2809 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2810 "GNU General Public License for more details.\n",
2811 "You should have received a copy of the GNU General Public License "
2812 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2813 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2814 };
2815 char *license_trans;
2816
2817 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2818 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002819
Jens Axboe0420ba62012-02-29 11:16:52 +01002820 gtk_show_about_dialog(NULL,
2821 "program-name", "gfio",
2822 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002823 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002824 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2825 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002826 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002827 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002828 "logo-icon-name", "fio",
2829 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002830 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002831 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002832
Jens Axboe2f99deb2012-03-09 14:37:29 +01002833 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002834}
2835
2836static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002837 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002838 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002839 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002840 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002841 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002842 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002843 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2844 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2845 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002846 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01002847 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002848 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2849 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2850 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2851 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002852 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2853 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002854};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002855static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002856
2857static const gchar *ui_string = " \
2858 <ui> \
2859 <menubar name=\"MainMenu\"> \
2860 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002861 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01002862 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002863 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002864 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002865 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002866 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002867 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2868 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002869 <placeholder name=\"FileRecentFiles\"/> \
2870 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002871 <menuitem name=\"Quit\" action=\"Quit\" /> \
2872 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002873 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002874 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002875 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002876 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2877 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002878 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002879 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002880 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002881 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01002882 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2883 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002884 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2885 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002886 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2887 <menuitem name=\"About\" action=\"About\" /> \
2888 </menu> \
2889 </menubar> \
2890 </ui> \
2891";
2892
Jens Axboe4cbe7212012-03-06 13:36:17 +01002893static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2894 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002895{
Jens Axboeca664f42012-03-14 19:49:40 +01002896 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002897 GError *error = 0;
2898
2899 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002900 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002901
2902 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2903 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2904
2905 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002906
Jens Axboe0420ba62012-02-29 11:16:52 +01002907 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2908}
2909
2910void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2911 GtkWidget *vbox, GtkUIManager *ui_manager)
2912{
2913 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2914}
2915
Jens Axboec80b74b2012-03-12 10:23:28 +01002916static void combo_entry_changed(GtkComboBox *box, gpointer data)
2917{
2918 struct gui_entry *ge = (struct gui_entry *) data;
2919 gint index;
2920
2921 index = gtk_combo_box_get_active(box);
2922
2923 multitext_set_entry(&ge->eta.iotype, index);
2924 multitext_set_entry(&ge->eta.ioengine, index);
2925 multitext_set_entry(&ge->eta.iodepth, index);
2926}
2927
2928static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2929{
2930 struct gui_entry *ge = (struct gui_entry *) data;
2931
2932 multitext_free(&ge->eta.iotype);
2933 multitext_free(&ge->eta.ioengine);
2934 multitext_free(&ge->eta.iodepth);
2935}
2936
Jens Axboe2f99deb2012-03-09 14:37:29 +01002937static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002938{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002939 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002940 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01002941
Jens Axboe2f99deb2012-03-09 14:37:29 +01002942 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002943
Jens Axboe65476332012-03-13 10:37:04 +01002944 top_align = gtk_alignment_new(0, 0, 1, 0);
2945 top_vbox = gtk_vbox_new(FALSE, 3);
2946 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2947 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002948
Jens Axboe3e47bd22012-02-29 13:45:02 +01002949 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002950 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002951 probe_frame = gtk_vbox_new(FALSE, 3);
2952 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2953
2954 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002955 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2956 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2957 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2958 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2959 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002960
Jens Axboe3e47bd22012-02-29 13:45:02 +01002961 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002962 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2963
Jens Axboe3863d1a2012-03-09 17:39:05 +01002964 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002965 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2966 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2967 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2968 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2969 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002970 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2971 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2972
2973 probe_box = gtk_hbox_new(FALSE, 3);
2974 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2975 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2976 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2977 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2978 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2979
2980 /*
2981 * Only add this if we have a commit rate
2982 */
2983#if 0
2984 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002985 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002986
Jens Axboe2f99deb2012-03-09 14:37:29 +01002987 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2988 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2989
2990 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2991 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2992#endif
2993
2994 /*
2995 * Set up a drawing area and IOPS and bandwidth graphs
2996 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002997 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002998 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002999 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003000 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3001 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
3002 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
3003 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
3004 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01003005 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3006 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003007 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01003008 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003009 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01003010 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003011
3012 setup_graphs(&ge->graphs);
3013
3014 /*
3015 * Set up alignments for widgets at the bottom of ui,
3016 * align bottom left, expand horizontally but not vertically
3017 */
Jens Axboe65476332012-03-13 10:37:04 +01003018 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003019 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003020 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
3021 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003022
3023 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
3024
3025 /*
3026 * Set up thread status progress bar
3027 */
3028 ge->thread_status_pb = gtk_progress_bar_new();
3029 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
3030 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
3031 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
3032
3033
3034 return main_vbox;
3035}
3036
3037static GtkWidget *new_main_page(struct gui *ui)
3038{
3039 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01003040 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003041
3042 main_vbox = gtk_vbox_new(FALSE, 3);
3043
3044 /*
3045 * Set up alignments for widgets at the top of ui,
3046 * align top left, expand horizontally but not vertically
3047 */
Jens Axboe65476332012-03-13 10:37:04 +01003048 top_align = gtk_alignment_new(0, 0, 1, 0);
3049 top_vbox = gtk_vbox_new(FALSE, 0);
3050 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
3051 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003052
3053 probe = gtk_frame_new("Run statistics");
3054 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
3055 probe_frame = gtk_vbox_new(FALSE, 3);
3056 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01003057
3058 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003059 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01003060 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01003061 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
3062 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
3063 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
3064 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01003065
3066 /*
3067 * Only add this if we have a commit rate
3068 */
3069#if 0
3070 probe_box = gtk_hbox_new(FALSE, 3);
3071 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
3072
Jens Axboe3e47bd22012-02-29 13:45:02 +01003073 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
3074 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
3075
Jens Axboe3e47bd22012-02-29 13:45:02 +01003076 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
3077 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01003078#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01003079
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01003080 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003081 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003082 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01003083 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01003084 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01003085 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003086 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
3087 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3088 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3089 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3090 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01003091 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3092 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003093 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01003094 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003095 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01003096 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01003097 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003098
Jens Axboe2f99deb2012-03-09 14:37:29 +01003099 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003100
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003101 /*
3102 * Set up alignments for widgets at the bottom of ui,
3103 * align bottom left, expand horizontally but not vertically
3104 */
Jens Axboe65476332012-03-13 10:37:04 +01003105 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003106 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003107 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3108 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003109
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003110 /*
3111 * Set up thread status progress bar
3112 */
3113 ui->thread_status_pb = gtk_progress_bar_new();
3114 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01003115 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003116 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3117
Jens Axboe2f99deb2012-03-09 14:37:29 +01003118 return main_vbox;
3119}
3120
3121static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3122 guint page, gpointer data)
3123
3124{
Jens Axboe02421e62012-03-12 12:05:50 +01003125 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01003126 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01003127
Jens Axboe85dd01e2012-03-12 14:33:16 +01003128 if (!page) {
3129 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01003130 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003131 return TRUE;
3132 }
3133
3134 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01003135 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003136 if (ge)
3137 update_button_states(ui, ge);
3138
Jens Axboe2f99deb2012-03-09 14:37:29 +01003139 return TRUE;
3140}
3141
Jens Axboe38634cb2012-03-13 12:26:41 +01003142static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3143{
3144 time_t time_a = gtk_recent_info_get_visited(a);
3145 time_t time_b = gtk_recent_info_get_visited(b);
3146
3147 return time_b - time_a;
3148}
3149
3150static void add_recent_file_items(struct gui *ui)
3151{
3152 const gchar *gfio = g_get_application_name();
3153 GList *items, *item;
3154 int i = 0;
3155
3156 if (ui->recent_ui_id) {
3157 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3158 gtk_ui_manager_ensure_update(ui->uimanager);
3159 }
3160 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3161
3162 if (ui->actiongroup) {
3163 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3164 g_object_unref(ui->actiongroup);
3165 }
3166 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3167
3168 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3169
3170 items = gtk_recent_manager_get_items(ui->recentmanager);
3171 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3172
3173 for (item = items; item && item->data; item = g_list_next(item)) {
3174 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3175 gchar *action_name;
3176 const gchar *label;
3177 GtkAction *action;
3178
3179 if (!gtk_recent_info_has_application(info, gfio))
3180 continue;
3181
3182 /*
3183 * We only support local files for now
3184 */
3185 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3186 continue;
3187
3188 action_name = g_strdup_printf("RecentFile%u", i++);
3189 label = gtk_recent_info_get_display_name(info);
3190
3191 action = g_object_new(GTK_TYPE_ACTION,
3192 "name", action_name,
3193 "label", label, NULL);
3194
3195 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3196 gtk_recent_info_ref(info),
3197 (GDestroyNotify) gtk_recent_info_unref);
3198
3199
3200 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3201
3202 gtk_action_group_add_action(ui->actiongroup, action);
3203 g_object_unref(action);
3204
3205 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3206 "/MainMenu/FileMenu/FileRecentFiles",
3207 label, action_name,
3208 GTK_UI_MANAGER_MENUITEM, FALSE);
3209
3210 g_free(action_name);
3211
3212 if (i == 8)
3213 break;
3214 }
3215
3216 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3217 g_list_free(items);
3218}
3219
Jens Axboea6790902012-03-13 15:16:11 +01003220static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3221 gint x, gint y, GtkSelectionData *data,
3222 guint info, guint time)
3223{
3224 struct gui *ui = &main_ui;
3225 gchar **uris;
3226 GtkWidget *source;
3227 int i;
3228
3229 source = gtk_drag_get_source_widget(ctx);
3230 if (source && widget == gtk_widget_get_toplevel(source)) {
3231 gtk_drag_finish(ctx, FALSE, FALSE, time);
3232 return;
3233 }
3234
3235 uris = gtk_selection_data_get_uris(data);
3236 if (!uris) {
3237 gtk_drag_finish(ctx, FALSE, FALSE, time);
3238 return;
3239 }
3240
3241 i = 0;
3242 while (uris[i]) {
3243 if (do_file_open_with_tab(ui, uris[i]))
3244 break;
3245 i++;
3246 }
3247
3248 gtk_drag_finish(ctx, TRUE, FALSE, time);
3249 g_strfreev(uris);
3250}
3251
Jens Axboe2f99deb2012-03-09 14:37:29 +01003252static void init_ui(int *argc, char **argv[], struct gui *ui)
3253{
3254 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003255 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003256
3257 /* Magical g*thread incantation, you just need this thread stuff.
3258 * Without it, the update that happens in gfio_update_thread_status
3259 * doesn't really happen in a timely fashion, you need expose events
3260 */
3261 if (!g_thread_supported())
3262 g_thread_init(NULL);
3263 gdk_threads_init();
3264
3265 gtk_init(argc, argv);
3266 settings = gtk_settings_get_default();
3267 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3268 g_type_init();
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003269 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003270
3271 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003272 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003273 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3274
3275 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3276 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3277
3278 ui->vbox = gtk_vbox_new(FALSE, 0);
3279 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3280
Jens Axboe02421e62012-03-12 12:05:50 +01003281 ui->uimanager = gtk_ui_manager_new();
3282 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3283 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003284
Jens Axboe38634cb2012-03-13 12:26:41 +01003285 ui->recentmanager = gtk_recent_manager_get_default();
3286 add_recent_file_items(ui);
3287
Jens Axboe2f99deb2012-03-09 14:37:29 +01003288 ui->notebook = gtk_notebook_new();
3289 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003290 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003291 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003292 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3293
3294 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003295 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3296 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3297 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003298
3299 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3300
Jens Axboe9b260bd2012-03-06 11:02:52 +01003301 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003302
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003303 gtk_widget_show_all(ui->window);
3304}
3305
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003306int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003307{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003308 if (initialize_fio(envp))
3309 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003310 if (fio_init_options())
3311 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003312
Jens Axboe2f99deb2012-03-09 14:37:29 +01003313 memset(&main_ui, 0, sizeof(main_ui));
3314 INIT_FLIST_HEAD(&main_ui.list);
3315
3316 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003317
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003318 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003319 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003320 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003321 return 0;
3322}