blob: 9837c1c8d3f2d79e7e209032bee69bea43f7bb6e [file] [log] [blame]
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
Jens Axboec0187f32012-03-06 15:39:15 +01005 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01006 *
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
Stephen M. Cameron8232e282012-02-24 08:17:31 +010024#include <locale.h>
Stephen M. Cameron60f6b332012-02-24 08:17:32 +010025#include <malloc.h>
Jens Axboe6b79c802012-03-08 10:51:36 +010026#include <string.h>
Stephen M. Cameron8232e282012-02-24 08:17:31 +010027
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +010028#include <glib.h>
Jens Axboe2fd3bb02012-03-07 08:07:39 +010029#include <cairo.h>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +010030#include <gtk/gtk.h>
31
Stephen M. Cameron8232e282012-02-24 08:17:31 +010032#include "fio.h"
Jens Axboe2fd3bb02012-03-07 08:07:39 +010033#include "graph.h"
Stephen M. Cameron8232e282012-02-24 08:17:31 +010034
Jens Axboe38634cb2012-03-13 12:26:41 +010035#define GFIO_MIME "text/fio"
36
Jens Axboe63a130b2012-03-06 20:08:59 +010037static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010038static const char *gfio_graph_font;
Jens Axboe8577f4f2012-03-09 19:28:27 +010039static unsigned int gfio_graph_limit = 100;
Stephen M. Cameron814479d2012-03-15 07:58:14 +010040static GdkColor white;
Jens Axboe63a130b2012-03-06 20:08:59 +010041
Jens Axboe6b79c802012-03-08 10:51:36 +010042static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010043
Stephen M. Cameronf3074002012-02-24 08:17:30 +010044#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
45
46typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
47
Jens Axboe3e47bd22012-02-29 13:45:02 +010048static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010049static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010050static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010051
52static struct button_spec {
53 const char *buttontext;
54 clickfunction f;
55 const char *tooltiptext;
Jens Axboe3e47bd22012-02-29 13:45:02 +010056 const int start_insensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010057} buttonspeclist[] = {
Jens Axboe3e47bd22012-02-29 13:45:02 +010058#define CONNECT_BUTTON 0
Jens Axboeb9d2f302012-03-08 20:36:28 +010059#define SEND_BUTTON 1
60#define START_JOB_BUTTON 2
Jens Axboe3e47bd22012-02-29 13:45:02 +010061 { "Connect", connect_clicked, "Connect to host", 0 },
Jens Axboeb9d2f302012-03-08 20:36:28 +010062 { "Send", send_clicked, "Send job description to host", 1 },
63 { "Start Job", start_job_clicked,
Jens Axboe2f99deb2012-03-09 14:37:29 +010064 "Start the current job on the server", 1 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010065};
66
Jens Axboe843ad232012-02-29 11:44:53 +010067struct probe_widget {
68 GtkWidget *hostname;
69 GtkWidget *os;
70 GtkWidget *arch;
71 GtkWidget *fio_ver;
72};
73
Jens Axboec80b74b2012-03-12 10:23:28 +010074struct multitext_widget {
75 GtkWidget *entry;
76 char **text;
77 unsigned int cur_text;
78 unsigned int max_text;
79};
80
Jens Axboe3e47bd22012-02-29 13:45:02 +010081struct eta_widget {
Jens Axboe3863d1a2012-03-09 17:39:05 +010082 GtkWidget *names;
Jens Axboec80b74b2012-03-12 10:23:28 +010083 struct multitext_widget iotype;
84 struct multitext_widget ioengine;
85 struct multitext_widget iodepth;
Jens Axboe3e47bd22012-02-29 13:45:02 +010086 GtkWidget *jobs;
87 GtkWidget *files;
88 GtkWidget *read_bw;
89 GtkWidget *read_iops;
90 GtkWidget *cr_bw;
91 GtkWidget *cr_iops;
92 GtkWidget *write_bw;
93 GtkWidget *write_iops;
94 GtkWidget *cw_bw;
95 GtkWidget *cw_iops;
96};
97
Jens Axboe2f99deb2012-03-09 14:37:29 +010098struct gfio_graphs {
99#define DRAWING_AREA_XDIM 1000
100#define DRAWING_AREA_YDIM 400
101 GtkWidget *drawing_area;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100102 struct graph *iops_graph;
103 struct graph *bandwidth_graph;
104};
105
106/*
107 * Main window widgets and data
108 */
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100109struct gui {
Jens Axboe02421e62012-03-12 12:05:50 +0100110 GtkUIManager *uimanager;
Jens Axboe38634cb2012-03-13 12:26:41 +0100111 GtkRecentManager *recentmanager;
112 GtkActionGroup *actiongroup;
113 guint recent_ui_id;
Jens Axboe02421e62012-03-12 12:05:50 +0100114 GtkWidget *menu;
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100115 GtkWidget *window;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100116 GtkWidget *vbox;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100117 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100118 GtkWidget *buttonbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100119 GtkWidget *notebook;
120 GtkWidget *error_info_bar;
121 GtkWidget *error_label;
122 GtkListStore *log_model;
123 GtkWidget *log_tree;
124 GtkWidget *log_view;
125 struct gfio_graphs graphs;
126 struct probe_widget probe;
127 struct eta_widget eta;
128 pthread_t server_t;
129
Jens Axboea9eccde2012-03-09 14:59:42 +0100130 pthread_t t;
131 int handler_running;
132
Jens Axboe2f99deb2012-03-09 14:37:29 +0100133 struct flist_head list;
134} main_ui;
135
Jens Axboe85dd01e2012-03-12 14:33:16 +0100136enum {
137 GE_STATE_NEW = 1,
138 GE_STATE_CONNECTED,
139 GE_STATE_JOB_SENT,
140 GE_STATE_JOB_STARTED,
141 GE_STATE_JOB_RUNNING,
142 GE_STATE_JOB_DONE,
143};
144
Jens Axboe2f99deb2012-03-09 14:37:29 +0100145/*
146 * Notebook entry
147 */
148struct gui_entry {
149 struct flist_head list;
150 struct gui *ui;
151
152 GtkWidget *vbox;
Jens Axboec80b74b2012-03-12 10:23:28 +0100153 GtkWidget *job_notebook;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100154 GtkWidget *thread_status_pb;
155 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100156 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Jens Axboe2f99deb2012-03-09 14:37:29 +0100157 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100158 GtkWidget *error_info_bar;
159 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100160 GtkWidget *results_notebook;
161 GtkWidget *results_window;
Jens Axboe17b97212012-03-14 20:17:57 +0100162 GtkUIManager *results_uimanager;
163 GtkWidget *results_vbox;
164 GtkWidget *results_menu;
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 Axboeb9d2f302012-03-08 20:36:28 +0100175 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100176 int nr_job_files;
177 char **job_files;
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100178 struct graph *clat_graph;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100179};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100180
Jens Axboe781ccba2012-03-15 09:44:42 +0100181struct end_results {
182 struct group_run_stats gs;
183 struct thread_stat ts;
184};
185
Jens Axboee0681f32012-03-06 12:14:42 +0100186struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100187 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100188 struct fio_client *client;
Jens Axboee0681f32012-03-06 12:14:42 +0100189 GtkWidget *results_widget;
190 GtkWidget *disk_util_frame;
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 Axboee0681f32012-03-06 12:14:42 +0100197};
198
Jens Axboe9988ca72012-03-09 15:14:06 +0100199static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
200static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +0100201void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +0100202
Jens Axboe2f99deb2012-03-09 14:37:29 +0100203static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100204{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100205 struct graph *g;
206
207 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100208 graph_title(g, "IOPS (IOs/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100209 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100210 graph_add_label(g, "Read IOPS");
211 graph_add_label(g, "Write IOPS");
212 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
213 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +0100214 line_graph_set_data_count_limit(g, gfio_graph_limit);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100215 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100216 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100217}
218
Jens Axboe2f99deb2012-03-09 14:37:29 +0100219static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100220{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100221 struct graph *g;
222
223 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100224 graph_title(g, "Bandwidth (bytes/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100225 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100226 graph_add_label(g, "Read Bandwidth");
227 graph_add_label(g, "Write Bandwidth");
228 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
229 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100230 graph_set_base_offset(g, 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100231 line_graph_set_data_count_limit(g, 100);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100232 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100233 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100234}
235
Jens Axboe2f99deb2012-03-09 14:37:29 +0100236static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100237{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100238 g->iops_graph = setup_iops_graph();
239 g->bandwidth_graph = setup_bandwidth_graph();
240}
241
Jens Axboec80b74b2012-03-12 10:23:28 +0100242static void multitext_add_entry(struct multitext_widget *mt, const char *text)
243{
244 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
245 mt->text[mt->max_text] = strdup(text);
246 mt->max_text++;
247}
248
249static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
250{
251 if (index >= mt->max_text)
252 return;
Jens Axboeda185432012-03-12 11:05:46 +0100253 if (!mt->text || !mt->text[index])
Jens Axboec80b74b2012-03-12 10:23:28 +0100254 return;
255
256 mt->cur_text = index;
257 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
258}
259
260static void multitext_update_entry(struct multitext_widget *mt,
261 unsigned int index, const char *text)
262{
Jens Axboeda185432012-03-12 11:05:46 +0100263 if (!mt->text)
264 return;
265
Jens Axboec80b74b2012-03-12 10:23:28 +0100266 if (mt->text[index])
267 free(mt->text[index]);
268
269 mt->text[index] = strdup(text);
270 if (mt->cur_text == index)
271 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
272}
273
274static void multitext_free(struct multitext_widget *mt)
275{
276 int i;
277
278 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
279
280 for (i = 0; i < mt->max_text; i++) {
281 if (mt->text[i])
282 free(mt->text[i]);
283 }
284
285 free(mt->text);
286 mt->cur_text = -1;
287 mt->max_text = 0;
288}
289
Jens Axboe2f99deb2012-03-09 14:37:29 +0100290static void clear_ge_ui_info(struct gui_entry *ge)
291{
292 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
293 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
294 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
295 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100296#if 0
297 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100298 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100299#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100300 multitext_update_entry(&ge->eta.iotype, 0, "");
301 multitext_update_entry(&ge->eta.ioengine, 0, "");
302 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100303 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
304 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
305 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
306 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
307 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
308 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100309}
310
Jens Axboe3863d1a2012-03-09 17:39:05 +0100311static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
312{
313 GtkWidget *entry, *frame;
314
315 frame = gtk_frame_new(label);
316 entry = gtk_combo_box_new_text();
317 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
318 gtk_container_add(GTK_CONTAINER(frame), entry);
319
320 return entry;
321}
322
Jens Axboe3650a3c2012-03-05 14:09:03 +0100323static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
324{
325 GtkWidget *entry, *frame;
326
327 frame = gtk_frame_new(label);
328 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100329 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100330 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
331 gtk_container_add(GTK_CONTAINER(frame), entry);
332
333 return entry;
334}
335
336static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
337{
338 GtkWidget *label_widget;
339 GtkWidget *frame;
340
341 frame = gtk_frame_new(label);
342 label_widget = gtk_label_new(NULL);
343 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
344 gtk_container_add(GTK_CONTAINER(frame), label_widget);
345
346 return label_widget;
347}
348
349static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
350{
351 GtkWidget *button, *box;
352
353 box = gtk_hbox_new(FALSE, 3);
354 gtk_container_add(GTK_CONTAINER(hbox), box);
355
356 button = gtk_spin_button_new_with_range(min, max, 1.0);
357 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
358
359 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
360 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
361
362 return button;
363}
364
Jens Axboe3650a3c2012-03-05 14:09:03 +0100365static void label_set_int_value(GtkWidget *entry, unsigned int val)
366{
367 char tmp[80];
368
369 sprintf(tmp, "%u", val);
370 gtk_label_set_text(GTK_LABEL(entry), tmp);
371}
372
373static void entry_set_int_value(GtkWidget *entry, unsigned int val)
374{
375 char tmp[80];
376
377 sprintf(tmp, "%u", val);
378 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
379}
380
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100381static void show_info_dialog(struct gui *ui, const char *title,
382 const char *message)
383{
384 GtkWidget *dialog, *content, *label;
385
386 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
387 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
388 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
389
390 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
391 label = gtk_label_new(message);
392 gtk_container_add(GTK_CONTAINER(content), label);
393 gtk_widget_show_all(dialog);
394 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
395 gtk_dialog_run(GTK_DIALOG(dialog));
396 gtk_widget_destroy(dialog);
397}
398
Jens Axboe781ccba2012-03-15 09:44:42 +0100399static void set_menu_entry_text(struct gui *ui, const char *path,
400 const char *text)
401{
402 GtkWidget *w;
403
404 w = gtk_ui_manager_get_widget(ui->uimanager, path);
405 if (w)
406 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
407 else
408 fprintf(stderr, "gfio: can't find path %s\n", path);
409}
410
411
412static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
413{
414 GtkWidget *w;
415
416 w = gtk_ui_manager_get_widget(ui->uimanager, path);
417 if (w)
418 gtk_widget_set_sensitive(w, show);
419 else
420 fprintf(stderr, "gfio: can't find path %s\n", path);
421}
422
423static void set_job_menu_visible(struct gui *ui, int visible)
424{
425 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
426}
427
428static void set_view_results_visible(struct gui *ui, int visible)
429{
430 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
431}
432
Jens Axboe85dd01e2012-03-12 14:33:16 +0100433/*
434 * Update sensitivity of job buttons and job menu items, based on the
435 * state of the client.
436 */
437static void update_button_states(struct gui *ui, struct gui_entry *ge)
438{
439 unsigned int connect_state, send_state, start_state, edit_state;
440 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100441
442 switch (ge->state) {
443 default: {
444 char tmp[80];
445
446 sprintf(tmp, "Bad client state: %u\n", ge->state);
447 show_info_dialog(ui, "Error", tmp);
448 /* fall through to new state */
449 }
450
451 case GE_STATE_NEW:
452 connect_state = 1;
453 edit_state = 0;
454 connect_str = "Connect";
455 send_state = 0;
456 start_state = 0;
457 break;
458 case GE_STATE_CONNECTED:
459 connect_state = 1;
460 edit_state = 0;
461 connect_str = "Disconnect";
462 send_state = 1;
463 start_state = 0;
464 break;
465 case GE_STATE_JOB_SENT:
466 connect_state = 1;
467 edit_state = 0;
468 connect_str = "Disconnect";
469 send_state = 0;
470 start_state = 1;
471 break;
472 case GE_STATE_JOB_STARTED:
473 connect_state = 1;
474 edit_state = 1;
475 connect_str = "Disconnect";
476 send_state = 0;
477 start_state = 1;
478 break;
479 case GE_STATE_JOB_RUNNING:
480 connect_state = 1;
481 edit_state = 0;
482 connect_str = "Disconnect";
483 send_state = 0;
484 start_state = 0;
485 break;
486 case GE_STATE_JOB_DONE:
487 connect_state = 1;
488 edit_state = 0;
489 connect_str = "Connect";
490 send_state = 0;
491 start_state = 0;
492 break;
493 }
494
495 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
496 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
497 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
498 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
499
Jens Axboe781ccba2012-03-15 09:44:42 +0100500 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
501 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100502
Jens Axboe781ccba2012-03-15 09:44:42 +0100503 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
504 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
505 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100506
Jens Axboe781ccba2012-03-15 09:44:42 +0100507 if (ge->client && ge->client->nr_results)
508 set_view_results_visible(ui, 1);
509 else
510 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100511}
512
513static void gfio_set_state(struct gui_entry *ge, unsigned int state)
514{
515 ge->state = state;
516 update_button_states(ge->ui, ge);
517}
518
Jens Axboea2697902012-03-05 16:43:49 +0100519#define ALIGN_LEFT 1
520#define ALIGN_RIGHT 2
521#define INVISIBLE 4
522#define UNSORTABLE 8
523
524GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
525{
526 GtkCellRenderer *renderer;
527 GtkTreeViewColumn *col;
528 double xalign = 0.0; /* left as default */
529 PangoAlignment align;
530 gboolean visible;
531
532 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
533 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
534 PANGO_ALIGN_CENTER;
535 visible = !(flags & INVISIBLE);
536
537 renderer = gtk_cell_renderer_text_new();
538 col = gtk_tree_view_column_new();
539
540 gtk_tree_view_column_set_title(col, title);
541 if (!(flags & UNSORTABLE))
542 gtk_tree_view_column_set_sort_column_id(col, index);
543 gtk_tree_view_column_set_resizable(col, TRUE);
544 gtk_tree_view_column_pack_start(col, renderer, TRUE);
545 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
546 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
547 switch (align) {
548 case PANGO_ALIGN_LEFT:
549 xalign = 0.0;
550 break;
551 case PANGO_ALIGN_CENTER:
552 xalign = 0.5;
553 break;
554 case PANGO_ALIGN_RIGHT:
555 xalign = 1.0;
556 break;
557 }
558 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
559 gtk_tree_view_column_set_visible(col, visible);
560 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
561 return col;
562}
563
Jens Axboe9b260bd2012-03-06 11:02:52 +0100564static void gfio_ui_setup_log(struct gui *ui)
565{
566 GtkTreeSelection *selection;
567 GtkListStore *model;
568 GtkWidget *tree_view;
569
570 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
571
572 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
573 gtk_widget_set_can_focus(tree_view, FALSE);
574
575 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
576 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100577 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
578 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100579
580 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
581 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
582 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100583 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100584
585 ui->log_model = model;
586 ui->log_tree = tree_view;
587}
588
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100589static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
590 fio_fp64_t *plist,
591 unsigned int len,
592 double xdim, double ydim)
593{
594 struct graph *g;
595 int i;
596
597 g = graph_new(xdim, ydim, gfio_graph_font);
598 graph_title(g, title);
599 graph_x_title(g, "Percentile");
600
601 for (i = 0; i < len; i++) {
602 char fbuf[8];
603
604 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
605 graph_add_label(g, fbuf);
606 graph_add_data(g, fbuf, (double) ovals[i]);
607 }
608
609 return g;
610}
611
Jens Axboea2697902012-03-05 16:43:49 +0100612static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
613 fio_fp64_t *plist,
614 unsigned int len,
615 const char *base,
616 unsigned int scale)
617{
618 GType types[FIO_IO_U_LIST_MAX_LEN];
619 GtkWidget *tree_view;
620 GtkTreeSelection *selection;
621 GtkListStore *model;
622 GtkTreeIter iter;
623 int i;
624
625 for (i = 0; i < len; i++)
626 types[i] = G_TYPE_INT;
627
628 model = gtk_list_store_newv(len, types);
629
630 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
631 gtk_widget_set_can_focus(tree_view, FALSE);
632
Jens Axboe661f7412012-03-06 13:55:45 +0100633 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
634 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
635
Jens Axboea2697902012-03-05 16:43:49 +0100636 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
637 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
638
639 for (i = 0; i < len; i++) {
640 char fbuf[8];
641
642 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
643 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
644 }
645
646 gtk_list_store_append(model, &iter);
647
Jens Axboee0681f32012-03-06 12:14:42 +0100648 for (i = 0; i < len; i++) {
649 if (scale)
650 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100651 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100652 }
Jens Axboea2697902012-03-05 16:43:49 +0100653
654 return tree_view;
655}
656
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100657static int on_expose_clat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
658{
659 struct graph *g = p;
660 cairo_t *cr;
661
662 cr = gdk_cairo_create(w->window);
663#if 0
664 if (graph_has_tooltips(g)) {
665 g_object_set(w, "has-tooltip", TRUE, NULL);
666 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
667 }
668#endif
669 cairo_set_source_rgb(cr, 0, 0, 0);
670 bar_graph_draw(g, cr);
671 cairo_destroy(cr);
672
673 return FALSE;
674}
675
676static gint on_config_clat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
677 gpointer data)
678{
679 struct graph *g = data;
680
681 graph_set_size(g, w->allocation.width, w->allocation.height);
682 graph_set_size(g, w->allocation.width, w->allocation.height);
683 graph_set_position(g, 0, 0);
684 return TRUE;
685}
686
687static void gfio_show_clat_percentiles(struct gfio_client *gc,
688 GtkWidget *vbox, struct thread_stat *ts,
Jens Axboea2697902012-03-05 16:43:49 +0100689 int ddir)
690{
691 unsigned int *io_u_plat = ts->io_u_plat[ddir];
692 unsigned long nr = ts->clat_stat[ddir].samples;
693 fio_fp64_t *plist = ts->percentile_list;
694 unsigned int *ovals, len, minv, maxv, scale_down;
695 const char *base;
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100696 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
Jens Axboea2697902012-03-05 16:43:49 +0100697 char tmp[64];
698
699 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
700 if (!len)
701 goto out;
702
703 /*
704 * We default to usecs, but if the value range is such that we
705 * should scale down to msecs, do that.
706 */
707 if (minv > 2000 && maxv > 99999) {
708 scale_down = 1;
709 base = "msec";
710 } else {
711 scale_down = 0;
712 base = "usec";
713 }
714
Jens Axboea2697902012-03-05 16:43:49 +0100715 sprintf(tmp, "Completion percentiles (%s)", base);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100716 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
717 gc->ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
718
Jens Axboea2697902012-03-05 16:43:49 +0100719 frame = gtk_frame_new(tmp);
720 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
721
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100722 completion_vbox = gtk_vbox_new(FALSE, 3);
723 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
Jens Axboea2697902012-03-05 16:43:49 +0100724 hbox = gtk_hbox_new(FALSE, 3);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100725 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
726 drawing_area = gtk_drawing_area_new();
727 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
728 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
729 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
730 g_signal_connect(G_OBJECT(drawing_area), "expose_event",
731 G_CALLBACK(on_expose_clat_drawing_area), gc->ge->clat_graph);
732 g_signal_connect(G_OBJECT(drawing_area), "configure_event",
733 G_CALLBACK(on_config_clat_drawing_area), gc->ge->clat_graph);
Jens Axboea2697902012-03-05 16:43:49 +0100734
735 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
736out:
737 if (ovals)
738 free(ovals);
739}
740
Jens Axboe3650a3c2012-03-05 14:09:03 +0100741static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
742 unsigned long max, double mean, double dev)
743{
744 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100745 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100746 char *minp, *maxp;
747 char tmp[64];
748
749 if (!usec_to_msec(&min, &max, &mean, &dev))
750 base = "(msec)";
751
752 minp = num2str(min, 6, 1, 0);
753 maxp = num2str(max, 6, 1, 0);
754
Jens Axboe3650a3c2012-03-05 14:09:03 +0100755 sprintf(tmp, "%s %s", name, base);
756 frame = gtk_frame_new(tmp);
757 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
758
Jens Axboe3650a3c2012-03-05 14:09:03 +0100759 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100760 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100761
762 label = new_info_label_in_frame(hbox, "Minimum");
763 gtk_label_set_text(GTK_LABEL(label), minp);
764 label = new_info_label_in_frame(hbox, "Maximum");
765 gtk_label_set_text(GTK_LABEL(label), maxp);
766 label = new_info_label_in_frame(hbox, "Average");
767 sprintf(tmp, "%5.02f", mean);
768 gtk_label_set_text(GTK_LABEL(label), tmp);
769 label = new_info_label_in_frame(hbox, "Standard deviation");
770 sprintf(tmp, "%5.02f", dev);
771 gtk_label_set_text(GTK_LABEL(label), tmp);
772
773 free(minp);
774 free(maxp);
775
776}
777
Jens Axboeca850992012-03-05 20:04:43 +0100778#define GFIO_CLAT 1
779#define GFIO_SLAT 2
780#define GFIO_LAT 4
781
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100782static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
783 struct group_run_stats *rs,
Jens Axboe3650a3c2012-03-05 14:09:03 +0100784 struct thread_stat *ts, int ddir)
785{
786 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100787 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100788 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100789 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100790 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100791 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100792 char *io_p, *bw_p, *iops_p;
793 int i2p;
794
795 if (!ts->runtime[ddir])
796 return;
797
798 i2p = is_power_of_2(rs->kb_base);
799 runt = ts->runtime[ddir];
800
801 bw = (1000 * ts->io_bytes[ddir]) / runt;
802 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
803 bw_p = num2str(bw, 6, 1, i2p);
804
805 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
806 iops_p = num2str(iops, 6, 1, 0);
807
808 box = gtk_hbox_new(FALSE, 3);
809 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
810
811 frame = gtk_frame_new(ddir_label[ddir]);
812 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
813
Jens Axboe0b761302012-03-05 20:44:11 +0100814 main_vbox = gtk_vbox_new(FALSE, 3);
815 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100816
817 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100818 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100819
820 label = new_info_label_in_frame(box, "IO");
821 gtk_label_set_text(GTK_LABEL(label), io_p);
822 label = new_info_label_in_frame(box, "Bandwidth");
823 gtk_label_set_text(GTK_LABEL(label), bw_p);
824 label = new_info_label_in_frame(box, "IOPS");
825 gtk_label_set_text(GTK_LABEL(label), iops_p);
826 label = new_info_label_in_frame(box, "Runtime (msec)");
827 label_set_int_value(label, ts->runtime[ddir]);
828
Jens Axboee0681f32012-03-06 12:14:42 +0100829 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100830 double p_of_agg = 100.0;
831 const char *bw_str = "KB";
832 char tmp[32];
833
834 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100835 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100836 if (p_of_agg > 100.0)
837 p_of_agg = 100.0;
838 }
839
Jens Axboee0681f32012-03-06 12:14:42 +0100840 if (mean[0] > 999999.9) {
841 min[0] /= 1000.0;
842 max[0] /= 1000.0;
843 mean[0] /= 1000.0;
844 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100845 bw_str = "MB";
846 }
847
Jens Axboe0b761302012-03-05 20:44:11 +0100848 sprintf(tmp, "Bandwidth (%s)", bw_str);
849 frame = gtk_frame_new(tmp);
850 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100851
Jens Axboe0b761302012-03-05 20:44:11 +0100852 box = gtk_hbox_new(FALSE, 3);
853 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100854
Jens Axboe0b761302012-03-05 20:44:11 +0100855 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100856 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100857 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100858 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100859 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100860 sprintf(tmp, "%3.2f%%", p_of_agg);
861 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100862 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100863 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100864 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100865 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100866 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100867 gtk_label_set_text(GTK_LABEL(label), tmp);
868 }
869
Jens Axboee0681f32012-03-06 12:14:42 +0100870 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100871 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100872 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100873 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100874 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100875 flags |= GFIO_LAT;
876
877 if (flags) {
878 frame = gtk_frame_new("Latency");
879 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
880
881 vbox = gtk_vbox_new(FALSE, 3);
882 gtk_container_add(GTK_CONTAINER(frame), vbox);
883
884 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100885 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100886 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100887 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100888 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100889 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100890 }
891
892 if (ts->clat_percentiles)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100893 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
Jens Axboe2b089892012-03-06 08:09:17 +0100894
895
Jens Axboe3650a3c2012-03-05 14:09:03 +0100896 free(io_p);
897 free(bw_p);
898 free(iops_p);
899}
900
Jens Axboee5bd1342012-03-05 21:38:12 +0100901static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
902 const char **labels)
903{
904 GtkWidget *tree_view;
905 GtkTreeSelection *selection;
906 GtkListStore *model;
907 GtkTreeIter iter;
908 GType *types;
909 int i, skipped;
910
911 /*
912 * Check if all are empty, in which case don't bother
913 */
914 for (i = 0, skipped = 0; i < num; i++)
915 if (lat[i] <= 0.0)
916 skipped++;
917
918 if (skipped == num)
919 return NULL;
920
921 types = malloc(num * sizeof(GType));
922
923 for (i = 0; i < num; i++)
924 types[i] = G_TYPE_STRING;
925
926 model = gtk_list_store_newv(num, types);
927 free(types);
928 types = NULL;
929
930 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
931 gtk_widget_set_can_focus(tree_view, FALSE);
932
Jens Axboe661f7412012-03-06 13:55:45 +0100933 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
934 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
935
Jens Axboee5bd1342012-03-05 21:38:12 +0100936 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
937 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
938
939 for (i = 0; i < num; i++)
940 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
941
942 gtk_list_store_append(model, &iter);
943
944 for (i = 0; i < num; i++) {
945 char fbuf[32];
946
947 if (lat[i] <= 0.0)
948 sprintf(fbuf, "0.00");
949 else
950 sprintf(fbuf, "%3.2f%%", lat[i]);
951
952 gtk_list_store_set(model, &iter, i, fbuf, -1);
953 }
954
955 return tree_view;
956}
957
958static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
959{
960 GtkWidget *box, *frame, *tree_view;
961 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
962 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
963 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
964 "250", "500", "750", "1000", };
965 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
966 "250", "500", "750", "1000", "2000",
967 ">= 2000", };
968
969 stat_calc_lat_u(ts, io_u_lat_u);
970 stat_calc_lat_m(ts, io_u_lat_m);
971
972 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
973 if (tree_view) {
974 frame = gtk_frame_new("Latency buckets (usec)");
975 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
976
977 box = gtk_hbox_new(FALSE, 3);
978 gtk_container_add(GTK_CONTAINER(frame), box);
979 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
980 }
981
982 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
983 if (tree_view) {
984 frame = gtk_frame_new("Latency buckets (msec)");
985 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
986
987 box = gtk_hbox_new(FALSE, 3);
988 gtk_container_add(GTK_CONTAINER(frame), box);
989 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
990 }
991}
992
Jens Axboe2e331012012-03-05 22:07:54 +0100993static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
994{
995 GtkWidget *box, *frame, *entry;
996 double usr_cpu, sys_cpu;
997 unsigned long runtime;
998 char tmp[32];
999
1000 runtime = ts->total_run_time;
1001 if (runtime) {
1002 double runt = (double) runtime;
1003
1004 usr_cpu = (double) ts->usr_time * 100 / runt;
1005 sys_cpu = (double) ts->sys_time * 100 / runt;
1006 } else {
1007 usr_cpu = 0;
1008 sys_cpu = 0;
1009 }
1010
1011 frame = gtk_frame_new("OS resources");
1012 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1013
1014 box = gtk_hbox_new(FALSE, 3);
1015 gtk_container_add(GTK_CONTAINER(frame), box);
1016
1017 entry = new_info_entry_in_frame(box, "User CPU");
1018 sprintf(tmp, "%3.2f%%", usr_cpu);
1019 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1020 entry = new_info_entry_in_frame(box, "System CPU");
1021 sprintf(tmp, "%3.2f%%", sys_cpu);
1022 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1023 entry = new_info_entry_in_frame(box, "Context switches");
1024 entry_set_int_value(entry, ts->ctx);
1025 entry = new_info_entry_in_frame(box, "Major faults");
1026 entry_set_int_value(entry, ts->majf);
1027 entry = new_info_entry_in_frame(box, "Minor faults");
1028 entry_set_int_value(entry, ts->minf);
1029}
Jens Axboe19998db2012-03-06 09:17:59 +01001030static void gfio_add_sc_depths_tree(GtkListStore *model,
1031 struct thread_stat *ts, unsigned int len,
1032 int submit)
1033{
1034 double io_u_dist[FIO_IO_U_MAP_NR];
1035 GtkTreeIter iter;
1036 /* Bits 0, and 3-8 */
1037 const int add_mask = 0x1f9;
1038 int i, j;
1039
1040 if (submit)
1041 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
1042 else
1043 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
1044
1045 gtk_list_store_append(model, &iter);
1046
1047 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
1048
1049 for (i = 1, j = 0; i < len; i++) {
1050 char fbuf[32];
1051
1052 if (!(add_mask & (1UL << (i - 1))))
1053 sprintf(fbuf, "0.0%%");
1054 else {
1055 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1056 j++;
1057 }
1058
1059 gtk_list_store_set(model, &iter, i, fbuf, -1);
1060 }
1061
1062}
1063
1064static void gfio_add_total_depths_tree(GtkListStore *model,
1065 struct thread_stat *ts, unsigned int len)
1066{
1067 double io_u_dist[FIO_IO_U_MAP_NR];
1068 GtkTreeIter iter;
1069 /* Bits 1-6, and 8 */
1070 const int add_mask = 0x17e;
1071 int i, j;
1072
1073 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1074
1075 gtk_list_store_append(model, &iter);
1076
1077 gtk_list_store_set(model, &iter, 0, "Total", -1);
1078
1079 for (i = 1, j = 0; i < len; i++) {
1080 char fbuf[32];
1081
1082 if (!(add_mask & (1UL << (i - 1))))
1083 sprintf(fbuf, "0.0%%");
1084 else {
1085 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1086 j++;
1087 }
1088
1089 gtk_list_store_set(model, &iter, i, fbuf, -1);
1090 }
1091
1092}
Jens Axboe2e331012012-03-05 22:07:54 +01001093
1094static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1095{
Jens Axboe2e331012012-03-05 22:07:54 +01001096 GtkWidget *frame, *box, *tree_view;
1097 GtkTreeSelection *selection;
1098 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +01001099 GType types[FIO_IO_U_MAP_NR + 1];
1100 int i;
Jens Axboe19998db2012-03-06 09:17:59 +01001101#define NR_LABELS 10
1102 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +01001103
1104 frame = gtk_frame_new("IO depths");
1105 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1106
1107 box = gtk_hbox_new(FALSE, 3);
1108 gtk_container_add(GTK_CONTAINER(frame), box);
1109
Jens Axboe19998db2012-03-06 09:17:59 +01001110 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001111 types[i] = G_TYPE_STRING;
1112
Jens Axboe19998db2012-03-06 09:17:59 +01001113 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +01001114
1115 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1116 gtk_widget_set_can_focus(tree_view, FALSE);
1117
Jens Axboe661f7412012-03-06 13:55:45 +01001118 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1119 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1120
Jens Axboe2e331012012-03-05 22:07:54 +01001121 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1122 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1123
Jens Axboe19998db2012-03-06 09:17:59 +01001124 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001125 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1126
Jens Axboe19998db2012-03-06 09:17:59 +01001127 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1128 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1129 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001130
1131 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1132}
1133
Jens Axboef9d40b42012-03-06 09:52:49 +01001134static gboolean results_window_delete(GtkWidget *w, gpointer data)
1135{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001136 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001137
1138 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001139 ge->results_window = NULL;
1140 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001141 return TRUE;
1142}
1143
Jens Axboe17b97212012-03-14 20:17:57 +01001144static void results_close(GtkWidget *w, gpointer *data)
1145{
1146 struct gui_entry *ge = (struct gui_entry *) data;
1147
1148 gtk_widget_destroy(ge->results_window);
1149}
1150
1151static GtkActionEntry results_menu_items[] = {
1152 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1153 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1154 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1155};
1156static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1157
1158static const gchar *results_ui_string = " \
1159 <ui> \
1160 <menubar name=\"MainMenu\"> \
1161 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1162 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1163 </menu> \
1164 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1165 </menu>\
1166 </menubar> \
1167 </ui> \
1168";
1169
1170static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1171{
1172 GtkActionGroup *action_group;
1173 GtkWidget *widget;
1174 GError *error = 0;
1175
1176 ge->results_uimanager = gtk_ui_manager_new();
1177
1178 action_group = gtk_action_group_new("ResultsMenu");
1179 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1180
1181 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1182 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1183
1184 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1185
1186 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1187 return widget;
1188}
1189
Jens Axboe2f99deb2012-03-09 14:37:29 +01001190static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001191{
Jens Axboe17b97212012-03-14 20:17:57 +01001192 GtkWidget *win, *notebook, *vbox;
Jens Axboef9d40b42012-03-06 09:52:49 +01001193
Jens Axboe2f99deb2012-03-09 14:37:29 +01001194 if (ge->results_window)
1195 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001196
1197 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1198 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001199 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001200 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1201 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001202
Jens Axboe17b97212012-03-14 20:17:57 +01001203 vbox = gtk_vbox_new(FALSE, 0);
1204 gtk_container_add(GTK_CONTAINER(win), vbox);
1205
1206 ge->results_menu = get_results_menubar(win, ge);
1207 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1208
Jens Axboef9d40b42012-03-06 09:52:49 +01001209 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001210 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1211 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboe17b97212012-03-14 20:17:57 +01001212 gtk_container_add(GTK_CONTAINER(vbox), notebook);
Jens Axboef9d40b42012-03-06 09:52:49 +01001213
Jens Axboe2f99deb2012-03-09 14:37:29 +01001214 ge->results_window = win;
1215 ge->results_notebook = notebook;
1216 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001217}
1218
Jens Axboe781ccba2012-03-15 09:44:42 +01001219static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1220 struct group_run_stats *rs)
Jens Axboe3650a3c2012-03-05 14:09:03 +01001221{
Jens Axboe781ccba2012-03-15 09:44:42 +01001222 unsigned int nr = gc->nr_results;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001223
Jens Axboe781ccba2012-03-15 09:44:42 +01001224 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1225 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1226 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1227 gc->nr_results++;
1228}
Jens Axboe3650a3c2012-03-05 14:09:03 +01001229
Jens Axboe781ccba2012-03-15 09:44:42 +01001230static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1231 struct thread_stat *ts,
1232 struct group_run_stats *rs)
1233{
1234 GtkWidget *box, *vbox, *entry, *scroll;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001235
Jens Axboeb01329d2012-03-07 20:31:28 +01001236 scroll = gtk_scrolled_window_new(NULL, NULL);
1237 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1238 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1239
Jens Axboe3650a3c2012-03-05 14:09:03 +01001240 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001241
Jens Axboeb01329d2012-03-07 20:31:28 +01001242 box = gtk_hbox_new(FALSE, 0);
1243 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001244
Jens Axboeb01329d2012-03-07 20:31:28 +01001245 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1246
Jens Axboe781ccba2012-03-15 09:44:42 +01001247 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +01001248
Jens Axboee0681f32012-03-06 12:14:42 +01001249 gc->results_widget = vbox;
1250
Jens Axboe3650a3c2012-03-05 14:09:03 +01001251 entry = new_info_entry_in_frame(box, "Name");
1252 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1253 if (strlen(ts->description)) {
1254 entry = new_info_entry_in_frame(box, "Description");
1255 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1256 }
1257 entry = new_info_entry_in_frame(box, "Group ID");
1258 entry_set_int_value(entry, ts->groupid);
1259 entry = new_info_entry_in_frame(box, "Jobs");
1260 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +01001261 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +01001262 entry_set_int_value(entry, ts->error);
1263 entry = new_info_entry_in_frame(box, "PID");
1264 entry_set_int_value(entry, ts->pid);
1265
1266 if (ts->io_bytes[DDIR_READ])
Stephen M. Cameronc57f2542012-03-15 08:10:40 +01001267 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001268 if (ts->io_bytes[DDIR_WRITE])
Stephen M. Cameronc57f2542012-03-15 08:10:40 +01001269 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001270
Jens Axboee5bd1342012-03-05 21:38:12 +01001271 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +01001272 gfio_show_cpu_usage(vbox, ts);
1273 gfio_show_io_depths(vbox, ts);
Jens Axboe781ccba2012-03-15 09:44:42 +01001274}
1275
1276static void gfio_display_end_results(struct gfio_client *gc)
1277{
1278 GtkWidget *res_win;
1279 int i;
1280
1281 res_win = get_results_window(gc->ge);
1282
1283 for (i = 0; i < gc->nr_results; i++) {
1284 struct end_results *e = &gc->results[i];
1285
1286 __gfio_display_end_results(res_win, gc, &e->ts, &e->gs);
1287 }
Jens Axboee5bd1342012-03-05 21:38:12 +01001288
Jens Axboe2f99deb2012-03-09 14:37:29 +01001289 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe781ccba2012-03-15 09:44:42 +01001290}
1291
1292static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1293 struct group_run_stats *rs)
1294{
1295 struct gfio_client *gc = client->client_data;
1296
1297 gfio_add_end_results(gc, ts, rs);
1298
1299 gdk_threads_enter();
1300 gfio_display_end_results(gc);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001301 gdk_threads_leave();
1302}
1303
Jens Axboe084d1c62012-03-03 20:28:07 +01001304static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001305{
Jens Axboe9b260bd2012-03-06 11:02:52 +01001306 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001307 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001308 GtkTreeIter iter;
1309 struct tm *tm;
1310 time_t sec;
1311 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001312
Jens Axboe9b260bd2012-03-06 11:02:52 +01001313 sec = p->log_sec;
1314 tm = localtime(&sec);
1315 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1316 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1317
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001318 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +01001319
Jens Axboe2f99deb2012-03-09 14:37:29 +01001320 gtk_list_store_append(ui->log_model, &iter);
1321 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1322 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1323 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1324 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001325
Jens Axboe6b79c802012-03-08 10:51:36 +01001326 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001327 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +01001328
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001329 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001330}
1331
1332static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1333{
Jens Axboee0681f32012-03-06 12:14:42 +01001334 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1335 struct gfio_client *gc = client->client_data;
1336 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +01001337 double util;
1338 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001339
Jens Axboe0050e5f2012-03-06 09:23:27 +01001340 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +01001341
Jens Axboe45dcb2e2012-03-07 16:16:50 +01001342 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +01001343 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +01001344
1345 if (!gc->disk_util_frame) {
1346 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1347 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1348 }
1349
1350 vbox = gtk_vbox_new(FALSE, 3);
1351 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1352
1353 frame = gtk_frame_new((char *) p->dus.name);
1354 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1355
1356 box = gtk_vbox_new(FALSE, 3);
1357 gtk_container_add(GTK_CONTAINER(frame), box);
1358
1359 frame = gtk_frame_new("Read");
1360 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1361 vbox = gtk_hbox_new(TRUE, 3);
1362 gtk_container_add(GTK_CONTAINER(frame), vbox);
1363 entry = new_info_entry_in_frame(vbox, "IOs");
1364 entry_set_int_value(entry, p->dus.ios[0]);
1365 entry = new_info_entry_in_frame(vbox, "Merges");
1366 entry_set_int_value(entry, p->dus.merges[0]);
1367 entry = new_info_entry_in_frame(vbox, "Sectors");
1368 entry_set_int_value(entry, p->dus.sectors[0]);
1369 entry = new_info_entry_in_frame(vbox, "Ticks");
1370 entry_set_int_value(entry, p->dus.ticks[0]);
1371
1372 frame = gtk_frame_new("Write");
1373 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1374 vbox = gtk_hbox_new(TRUE, 3);
1375 gtk_container_add(GTK_CONTAINER(frame), vbox);
1376 entry = new_info_entry_in_frame(vbox, "IOs");
1377 entry_set_int_value(entry, p->dus.ios[1]);
1378 entry = new_info_entry_in_frame(vbox, "Merges");
1379 entry_set_int_value(entry, p->dus.merges[1]);
1380 entry = new_info_entry_in_frame(vbox, "Sectors");
1381 entry_set_int_value(entry, p->dus.sectors[1]);
1382 entry = new_info_entry_in_frame(vbox, "Ticks");
1383 entry_set_int_value(entry, p->dus.ticks[1]);
1384
1385 frame = gtk_frame_new("Shared");
1386 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1387 vbox = gtk_hbox_new(TRUE, 3);
1388 gtk_container_add(GTK_CONTAINER(frame), vbox);
1389 entry = new_info_entry_in_frame(vbox, "IO ticks");
1390 entry_set_int_value(entry, p->dus.io_ticks);
1391 entry = new_info_entry_in_frame(vbox, "Time in queue");
1392 entry_set_int_value(entry, p->dus.time_in_queue);
1393
Jens Axboe604cfe32012-03-07 19:51:36 +01001394 util = 0.0;
1395 if (p->dus.msec)
1396 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1397 if (util > 100.0)
1398 util = 100.0;
1399
1400 sprintf(tmp, "%3.2f%%", util);
1401 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1402 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1403
Jens Axboee0681f32012-03-06 12:14:42 +01001404 gtk_widget_show_all(gc->results_widget);
1405out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001406 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001407}
1408
Jens Axboe3650a3c2012-03-05 14:09:03 +01001409extern int sum_stat_clients;
1410extern struct thread_stat client_ts;
1411extern struct group_run_stats client_gs;
1412
1413static int sum_stat_nr;
1414
Jens Axboe89e5fad2012-03-05 09:21:12 +01001415static void gfio_thread_status_op(struct fio_client *client,
1416 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001417{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001418 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1419
1420 gfio_display_ts(client, &p->ts, &p->rs);
1421
1422 if (sum_stat_clients == 1)
1423 return;
1424
1425 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1426 sum_group_stats(&client_gs, &p->rs);
1427
1428 client_ts.members++;
1429 client_ts.groupid = p->ts.groupid;
1430
1431 if (++sum_stat_nr == sum_stat_clients) {
1432 strcpy(client_ts.name, "All clients");
1433 gfio_display_ts(client, &client_ts, &client_gs);
1434 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001435}
1436
Jens Axboe89e5fad2012-03-05 09:21:12 +01001437static void gfio_group_stats_op(struct fio_client *client,
1438 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001439{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001440 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001441}
1442
Jens Axboe2f99deb2012-03-09 14:37:29 +01001443static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1444 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001445{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001446 struct gfio_graphs *g = data;
1447
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001448 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1449 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1450 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1451 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001452 return TRUE;
1453}
1454
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001455static void draw_graph(struct graph *g, cairo_t *cr)
1456{
1457 line_graph_draw(g, cr);
1458 cairo_stroke(cr);
1459}
1460
Jens Axboe93e2db22012-03-13 09:45:22 +01001461static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1462 gboolean keyboard_mode, GtkTooltip *tooltip,
1463 gpointer data)
1464{
1465 struct gfio_graphs *g = data;
1466 const char *text = NULL;
1467
1468 if (graph_contains_xy(g->iops_graph, x, y))
1469 text = graph_find_tooltip(g->iops_graph, x, y);
1470 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1471 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1472
1473 if (text) {
1474 gtk_tooltip_set_text(tooltip, text);
1475 return TRUE;
1476 }
1477
1478 return FALSE;
1479}
1480
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001481static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1482{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001483 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001484 cairo_t *cr;
1485
1486 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001487
1488 if (graph_has_tooltips(g->iops_graph) ||
1489 graph_has_tooltips(g->bandwidth_graph)) {
1490 g_object_set(w, "has-tooltip", TRUE, NULL);
1491 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1492 }
1493
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001494 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001495 draw_graph(g->iops_graph, cr);
1496 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001497 cairo_destroy(cr);
1498
1499 return FALSE;
1500}
1501
Jens Axboe2f99deb2012-03-09 14:37:29 +01001502/*
1503 * Client specific ETA
1504 */
1505static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001506{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001507 struct gfio_client *gc = client->client_data;
1508 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001509 static int eta_good;
1510 char eta_str[128];
1511 char output[256];
1512 char tmp[32];
1513 double perc = 0.0;
1514 int i2p = 0;
1515
Jens Axboe0050e5f2012-03-06 09:23:27 +01001516 gdk_threads_enter();
1517
Jens Axboe3e47bd22012-02-29 13:45:02 +01001518 eta_str[0] = '\0';
1519 output[0] = '\0';
1520
1521 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1522 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1523 eta_to_str(eta_str, je->eta_sec);
1524 }
1525
1526 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001527 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001528 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001529 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001530
1531#if 0
1532 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1533 if (je->m_rate || je->t_rate) {
1534 char *tr, *mr;
1535
1536 mr = num2str(je->m_rate, 4, 0, i2p);
1537 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001538 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001539 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1540 free(tr);
1541 free(mr);
1542 } else if (je->m_iops || je->t_iops)
1543 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001544
Jens Axboe2f99deb2012-03-09 14:37:29 +01001545 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1546 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1547 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1548 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001549#endif
1550
1551 if (je->eta_sec != INT_MAX && je->nr_running) {
1552 char *iops_str[2];
1553 char *rate_str[2];
1554
1555 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1556 strcpy(output, "-.-% done");
1557 else {
1558 eta_good = 1;
1559 perc *= 100.0;
1560 sprintf(output, "%3.1f%% done", perc);
1561 }
1562
1563 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1564 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1565
1566 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1567 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1568
Jens Axboe2f99deb2012-03-09 14:37:29 +01001569 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1570 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1571 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1572 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001573
Jens Axboe93e2db22012-03-13 09:45:22 +01001574 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1575 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1576 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1577 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 +01001578
1579 free(rate_str[0]);
1580 free(rate_str[1]);
1581 free(iops_str[0]);
1582 free(iops_str[1]);
1583 }
1584
1585 if (eta_str[0]) {
1586 char *dst = output + strlen(output);
1587
1588 sprintf(dst, " - %s", eta_str);
1589 }
1590
Jens Axboe9988ca72012-03-09 15:14:06 +01001591 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001592 gdk_threads_leave();
1593}
1594
1595/*
1596 * Update ETA in main window for all clients
1597 */
1598static void gfio_update_all_eta(struct jobs_eta *je)
1599{
1600 struct gui *ui = &main_ui;
1601 static int eta_good;
1602 char eta_str[128];
1603 char output[256];
1604 double perc = 0.0;
1605 int i2p = 0;
1606
1607 gdk_threads_enter();
1608
1609 eta_str[0] = '\0';
1610 output[0] = '\0';
1611
1612 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1613 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1614 eta_to_str(eta_str, je->eta_sec);
1615 }
1616
1617#if 0
1618 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1619 if (je->m_rate || je->t_rate) {
1620 char *tr, *mr;
1621
1622 mr = num2str(je->m_rate, 4, 0, i2p);
1623 tr = num2str(je->t_rate, 4, 0, i2p);
1624 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1625 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1626 free(tr);
1627 free(mr);
1628 } else if (je->m_iops || je->t_iops)
1629 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1630
1631 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1632 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1633 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1634 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1635#endif
1636
Jens Axboe3863d1a2012-03-09 17:39:05 +01001637 entry_set_int_value(ui->eta.jobs, je->nr_running);
1638
Jens Axboe2f99deb2012-03-09 14:37:29 +01001639 if (je->eta_sec != INT_MAX && je->nr_running) {
1640 char *iops_str[2];
1641 char *rate_str[2];
1642
1643 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1644 strcpy(output, "-.-% done");
1645 else {
1646 eta_good = 1;
1647 perc *= 100.0;
1648 sprintf(output, "%3.1f%% done", perc);
1649 }
1650
1651 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1652 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1653
1654 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1655 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1656
1657 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1658 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1659 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1660 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1661
Jens Axboe93e2db22012-03-13 09:45:22 +01001662 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1663 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1664 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1665 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 +01001666
Jens Axboe3e47bd22012-02-29 13:45:02 +01001667 free(rate_str[0]);
1668 free(rate_str[1]);
1669 free(iops_str[0]);
1670 free(iops_str[1]);
1671 }
1672
1673 if (eta_str[0]) {
1674 char *dst = output + strlen(output);
1675
1676 sprintf(dst, " - %s", eta_str);
1677 }
1678
Jens Axboe9988ca72012-03-09 15:14:06 +01001679 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001680 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001681}
1682
Stephen M. Camerona1820202012-02-24 08:17:31 +01001683static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1684{
Jens Axboe843ad232012-02-29 11:44:53 +01001685 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001686 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001687 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001688 const char *os, *arch;
1689 char buf[64];
1690
1691 os = fio_get_os_string(probe->os);
1692 if (!os)
1693 os = "unknown";
1694
1695 arch = fio_get_arch_string(probe->arch);
1696 if (!arch)
1697 os = "unknown";
1698
1699 if (!client->name)
1700 client->name = strdup((char *) probe->hostname);
1701
Jens Axboe0050e5f2012-03-06 09:23:27 +01001702 gdk_threads_enter();
1703
Jens Axboe2f99deb2012-03-09 14:37:29 +01001704 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1705 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1706 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001707 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001708 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001709
Jens Axboe85dd01e2012-03-12 14:33:16 +01001710 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001711
1712 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001713}
1714
Jens Axboe9988ca72012-03-09 15:14:06 +01001715static void gfio_update_thread_status(struct gui_entry *ge,
1716 char *status_message, double perc)
1717{
1718 static char message[100];
1719 const char *m = message;
1720
1721 strncpy(message, status_message, sizeof(message) - 1);
1722 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1723 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1724 gtk_widget_queue_draw(main_ui.window);
1725}
1726
1727static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001728{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001729 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001730 static char message[100];
1731 const char *m = message;
1732
1733 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001734 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1735 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1736 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001737}
1738
Jens Axboe35c0ba72012-03-14 10:56:40 +01001739static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001740{
Jens Axboee0681f32012-03-06 12:14:42 +01001741 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001742
Jens Axboe0050e5f2012-03-06 09:23:27 +01001743 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001744 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001745 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001746}
1747
Jens Axboe807f9972012-03-02 10:25:24 +01001748static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1749{
1750 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001751 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001752 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001753 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001754 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001755
Jens Axboedcaeb602012-03-08 19:45:37 +01001756 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001757
Jens Axboe0050e5f2012-03-06 09:23:27 +01001758 gdk_threads_enter();
1759
Jens Axboe2f99deb2012-03-09 14:37:29 +01001760 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1761
Jens Axboe3863d1a2012-03-09 17:39:05 +01001762 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1763 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1764
Jens Axboec80b74b2012-03-12 10:23:28 +01001765 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1766 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001767
Jens Axboedcaeb602012-03-08 19:45:37 +01001768 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001769 multitext_add_entry(&ge->eta.iodepth, tmp);
1770
1771 multitext_set_entry(&ge->eta.iotype, 0);
1772 multitext_set_entry(&ge->eta.ioengine, 0);
1773 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001774
Jens Axboedcaeb602012-03-08 19:45:37 +01001775 gc->job_added++;
1776
Jens Axboe85dd01e2012-03-12 14:33:16 +01001777 gfio_set_state(ge, GE_STATE_JOB_SENT);
1778
Jens Axboe0050e5f2012-03-06 09:23:27 +01001779 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001780}
1781
Jens Axboeed727a42012-03-02 12:14:40 +01001782static void gfio_client_timed_out(struct fio_client *client)
1783{
Jens Axboee0681f32012-03-06 12:14:42 +01001784 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001785 char buf[256];
1786
1787 gdk_threads_enter();
1788
Jens Axboe85dd01e2012-03-12 14:33:16 +01001789 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001790 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001791
1792 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001793 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001794
1795 gdk_threads_leave();
1796}
1797
Jens Axboe6b79c802012-03-08 10:51:36 +01001798static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1799{
1800 struct gfio_client *gc = client->client_data;
1801
1802 gdk_threads_enter();
1803
Jens Axboe85dd01e2012-03-12 14:33:16 +01001804 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001805
1806 if (gc->err_entry)
1807 entry_set_int_value(gc->err_entry, client->error);
1808
1809 gdk_threads_leave();
1810}
1811
Jens Axboe85dd01e2012-03-12 14:33:16 +01001812static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1813{
1814 struct gfio_client *gc = client->client_data;
1815
1816 gdk_threads_enter();
1817 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1818 gdk_threads_leave();
1819}
1820
1821static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1822{
1823 struct gfio_client *gc = client->client_data;
1824
1825 gdk_threads_enter();
1826 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1827 gdk_threads_leave();
1828}
1829
Jens Axboe1b427252012-03-14 15:03:03 +01001830static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1831{
Jens Axboe284b1e62012-03-14 21:55:07 +01001832 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 +01001833 free(pdu);
1834}
1835
Stephen M. Camerona1820202012-02-24 08:17:31 +01001836struct client_ops gfio_client_ops = {
Jens Axboe35c0ba72012-03-14 10:56:40 +01001837 .text = gfio_text_op,
Jens Axboe0420ba62012-02-29 11:16:52 +01001838 .disk_util = gfio_disk_util_op,
1839 .thread_status = gfio_thread_status_op,
1840 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001841 .jobs_eta = gfio_update_client_eta,
1842 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001843 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001844 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001845 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001846 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001847 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001848 .start = gfio_client_start,
1849 .job_start = gfio_client_job_start,
Jens Axboe1b427252012-03-14 15:03:03 +01001850 .iolog = gfio_client_iolog,
Jens Axboe6433ee02012-03-09 20:10:51 +01001851 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001852 .stay_connected = 1,
Jens Axboe46bcd492012-03-14 11:31:21 +01001853 .client_type = FIO_CLIENT_TYPE_GUI,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001854};
1855
Jens Axboe0fd18982012-03-14 10:34:48 +01001856/*
1857 * FIXME: need more handling here
1858 */
1859static void ge_destroy(struct gui_entry *ge)
1860{
1861 struct gfio_client *gc = ge->client;
1862
1863 if (gc && gc->client) {
1864 if (ge->state >= GE_STATE_CONNECTED)
1865 fio_client_terminate(gc->client);
1866
1867 fio_put_client(gc->client);
1868 }
1869
1870 flist_del(&ge->list);
1871 free(ge);
1872}
1873
1874static void ge_widget_destroy(GtkWidget *w, gpointer data)
1875{
Jens Axboe0fd18982012-03-14 10:34:48 +01001876}
1877
1878static void gfio_quit(struct gui *ui)
1879{
1880 struct gui_entry *ge;
1881
1882 while (!flist_empty(&ui->list)) {
1883 ge = flist_entry(ui->list.next, struct gui_entry, list);
1884 ge_destroy(ge);
1885 }
1886
1887 gtk_main_quit();
1888}
1889
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001890static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1891 __attribute__((unused)) gpointer data)
1892{
Jens Axboe0fd18982012-03-14 10:34:48 +01001893 gfio_quit(data);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001894}
1895
Stephen M. Cameron25927252012-02-24 08:17:31 +01001896static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001897{
Jens Axboea9eccde2012-03-09 14:59:42 +01001898 struct gui *ui = arg;
1899
1900 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001901 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001902 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001903 return NULL;
1904}
1905
Jens Axboe2f99deb2012-03-09 14:37:29 +01001906static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001907{
Jens Axboe9988ca72012-03-09 15:14:06 +01001908 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001909 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001910
Jens Axboe2f99deb2012-03-09 14:37:29 +01001911 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001912 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001913 if (ret < 0) {
1914 GError *error;
1915
1916 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1917 report_error(error);
1918 g_error_free(error);
1919 break;
1920 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001921 break;
1922
Jens Axboe2f99deb2012-03-09 14:37:29 +01001923 free(ge->job_files[i]);
1924 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001925 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001926 while (i < ge->nr_job_files) {
1927 free(ge->job_files[i]);
1928 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001929 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001930 }
1931
Jens Axboe2c77d832012-03-13 19:02:04 +01001932 free(ge->job_files);
1933 ge->job_files = NULL;
Jens Axboe3af45202012-03-13 09:59:53 +01001934 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01001935 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001936}
1937
Jens Axboe63a130b2012-03-06 20:08:59 +01001938static void *server_thread(void *arg)
1939{
1940 is_backend = 1;
1941 gfio_server_running = 1;
1942 fio_start_server(NULL);
1943 gfio_server_running = 0;
1944 return NULL;
1945}
1946
Jens Axboe2f99deb2012-03-09 14:37:29 +01001947static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001948{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001949 struct gui *ui = &main_ui;
1950
Jens Axboe63a130b2012-03-06 20:08:59 +01001951 if (!gfio_server_running) {
1952 gfio_server_running = 1;
1953 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001954 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001955 }
1956}
1957
Stephen M. Cameron25927252012-02-24 08:17:31 +01001958static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1959 gpointer data)
1960{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001961 struct gui_entry *ge = data;
1962 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001963
Jens Axboe78cb2fe2012-03-12 23:05:29 +01001964 if (gc)
1965 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001966}
1967
Jens Axboedf06f222012-03-02 13:32:04 +01001968static void file_open(GtkWidget *w, gpointer data);
1969
1970static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001971{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001972 struct gui_entry *ge = data;
1973 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001974
Jens Axboe85dd01e2012-03-12 14:33:16 +01001975 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01001976 int ret;
1977
Jens Axboe2f99deb2012-03-09 14:37:29 +01001978 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01001979 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001980 if (!ge->nr_job_files)
1981 return;
1982
1983 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1984 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001985 ret = fio_client_connect(gc->client);
1986 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001987 if (!ge->ui->handler_running)
1988 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001989 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01001990 } else {
1991 GError *error;
1992
1993 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1994 report_error(error);
1995 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001996 }
Jens Axboedf06f222012-03-02 13:32:04 +01001997 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001998 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001999 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002000 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01002001 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01002002}
2003
Jens Axboeb9d2f302012-03-08 20:36:28 +01002004static void send_clicked(GtkWidget *widget, gpointer data)
2005{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002006 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01002007
Jens Axboe2f99deb2012-03-09 14:37:29 +01002008 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01002009 GError *error;
2010
2011 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);
2012 report_error(error);
2013 g_error_free(error);
2014
Jens Axboe2f99deb2012-03-09 14:37:29 +01002015 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002016 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01002017}
2018
Jens Axboe2f99deb2012-03-09 14:37:29 +01002019static GtkWidget *add_button(GtkWidget *buttonbox,
2020 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002021{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002022 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
2023
2024 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
2025 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
2026 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
2027 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
2028
2029 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002030}
2031
Jens Axboe2f99deb2012-03-09 14:37:29 +01002032static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
2033 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002034{
2035 int i;
2036
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002037 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002038 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01002039}
2040
Jens Axboe0420ba62012-02-29 11:16:52 +01002041static void on_info_bar_response(GtkWidget *widget, gint response,
2042 gpointer data)
2043{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002044 struct gui *ui = &main_ui;
2045
Jens Axboe0420ba62012-02-29 11:16:52 +01002046 if (response == GTK_RESPONSE_OK) {
2047 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002048 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01002049 }
2050}
2051
Jens Axboedf06f222012-03-02 13:32:04 +01002052void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01002053{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002054 struct gui *ui = &main_ui;
2055
2056 if (ui->error_info_bar == NULL) {
2057 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01002058 GTK_RESPONSE_OK,
2059 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002060 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2061 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01002062 GTK_MESSAGE_ERROR);
2063
Jens Axboe2f99deb2012-03-09 14:37:29 +01002064 ui->error_label = gtk_label_new(error->message);
2065 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2066 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01002067
Jens Axboe2f99deb2012-03-09 14:37:29 +01002068 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2069 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01002070 } else {
2071 char buffer[256];
2072 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002073 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01002074 }
2075}
2076
Jens Axboe62bc9372012-03-07 11:45:07 +01002077struct connection_widgets
2078{
2079 GtkWidget *hentry;
2080 GtkWidget *combo;
2081 GtkWidget *button;
2082};
2083
2084static void hostname_cb(GtkEntry *entry, gpointer data)
2085{
2086 struct connection_widgets *cw = data;
2087 int uses_net = 0, is_localhost = 0;
2088 const gchar *text;
2089 gchar *ctext;
2090
2091 /*
2092 * Check whether to display the 'auto start backend' box
2093 * or not. Show it if we are a localhost and using network,
2094 * or using a socket.
2095 */
2096 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2097 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2098 uses_net = 1;
2099 g_free(ctext);
2100
2101 if (uses_net) {
2102 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2103 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2104 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2105 !strcmp(text, "ip6-loopback"))
2106 is_localhost = 1;
2107 }
2108
2109 if (!uses_net || is_localhost) {
2110 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2111 gtk_widget_set_sensitive(cw->button, 1);
2112 } else {
2113 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2114 gtk_widget_set_sensitive(cw->button, 0);
2115 }
2116}
2117
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002118static int get_connection_details(char **host, int *port, int *type,
2119 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01002120{
Jens Axboe62bc9372012-03-07 11:45:07 +01002121 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2122 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002123 char *typeentry;
2124
2125 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002126 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01002127 GTK_DIALOG_DESTROY_WITH_PARENT,
2128 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2129 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2130
2131 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01002132 /* gtk_dialog_get_content_area() is 2.14 and newer */
2133 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002134 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2135
2136 box = gtk_vbox_new(FALSE, 6);
2137 gtk_container_add(GTK_CONTAINER(frame), box);
2138
2139 hbox = gtk_hbox_new(TRUE, 10);
2140 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01002141 cw.hentry = gtk_entry_new();
2142 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2143 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002144
2145 frame = gtk_frame_new("Port");
2146 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2147 box = gtk_vbox_new(FALSE, 10);
2148 gtk_container_add(GTK_CONTAINER(frame), box);
2149
2150 hbox = gtk_hbox_new(TRUE, 4);
2151 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2152 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2153
2154 frame = gtk_frame_new("Type");
2155 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2156 box = gtk_vbox_new(FALSE, 10);
2157 gtk_container_add(GTK_CONTAINER(frame), box);
2158
2159 hbox = gtk_hbox_new(TRUE, 4);
2160 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2161
Jens Axboe62bc9372012-03-07 11:45:07 +01002162 cw.combo = gtk_combo_box_new_text();
2163 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2164 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2165 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2166 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002167
Jens Axboe62bc9372012-03-07 11:45:07 +01002168 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002169
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002170 frame = gtk_frame_new("Options");
2171 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2172 box = gtk_vbox_new(FALSE, 10);
2173 gtk_container_add(GTK_CONTAINER(frame), box);
2174
2175 hbox = gtk_hbox_new(TRUE, 4);
2176 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2177
Jens Axboe62bc9372012-03-07 11:45:07 +01002178 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2179 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2180 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.");
2181 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2182
2183 /*
2184 * Connect edit signal, so we can show/not-show the auto start button
2185 */
2186 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2187 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002188
Jens Axboea7a42ce2012-03-02 13:12:04 +01002189 gtk_widget_show_all(dialog);
2190
2191 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2192 gtk_widget_destroy(dialog);
2193 return 1;
2194 }
2195
Jens Axboe62bc9372012-03-07 11:45:07 +01002196 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002197 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2198
Jens Axboe62bc9372012-03-07 11:45:07 +01002199 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002200 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2201 *type = Fio_client_ipv4;
2202 else if (!strncmp(typeentry, "IPv6", 4))
2203 *type = Fio_client_ipv6;
2204 else
2205 *type = Fio_client_socket;
2206 g_free(typeentry);
2207
Jens Axboe62bc9372012-03-07 11:45:07 +01002208 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002209
Jens Axboea7a42ce2012-03-02 13:12:04 +01002210 gtk_widget_destroy(dialog);
2211 return 0;
2212}
2213
Jens Axboe2f99deb2012-03-09 14:37:29 +01002214static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002215{
2216 struct gfio_client *gc;
2217
2218 gc = malloc(sizeof(*gc));
2219 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002220 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002221 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002222
Jens Axboe2f99deb2012-03-09 14:37:29 +01002223 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002224
2225 client->client_data = gc;
2226}
2227
Jens Axboe2f99deb2012-03-09 14:37:29 +01002228static GtkWidget *new_client_page(struct gui_entry *ge);
2229
2230static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2231{
2232 struct gui_entry *ge;
2233
2234 ge = malloc(sizeof(*ge));
2235 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002236 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002237 INIT_FLIST_HEAD(&ge->list);
2238 flist_add_tail(&ge->list, &ui->list);
2239 ge->ui = ui;
2240 return ge;
2241}
2242
Jens Axboe2f99deb2012-03-09 14:37:29 +01002243static struct gui_entry *get_new_ge_with_tab(const char *name)
2244{
2245 struct gui_entry *ge;
2246
2247 ge = alloc_new_gui_entry(&main_ui);
2248
2249 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002250 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002251
2252 ge->page_label = gtk_label_new(name);
2253 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2254
2255 gtk_widget_show_all(main_ui.window);
2256 return ge;
2257}
2258
2259static void file_new(GtkWidget *w, gpointer data)
2260{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002261 struct gui *ui = (struct gui *) data;
2262 struct gui_entry *ge;
2263
2264 ge = get_new_ge_with_tab("Untitled");
2265 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002266}
2267
2268/*
2269 * Return the 'ge' corresponding to the tab. If the active tab is the
2270 * main tab, open a new tab.
2271 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002272static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002273{
2274 struct flist_head *entry;
2275 struct gui_entry *ge;
2276
Jens Axboe38634cb2012-03-13 12:26:41 +01002277 if (!cur_page) {
2278 if (created)
2279 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002280 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002281 }
2282
2283 if (created)
2284 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002285
2286 flist_for_each(entry, &main_ui.list) {
2287 ge = flist_entry(entry, struct gui_entry, list);
2288 if (ge->page_num == cur_page)
2289 return ge;
2290 }
2291
2292 return NULL;
2293}
2294
Jens Axboe85dd01e2012-03-12 14:33:16 +01002295static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2296{
2297 gint cur_page;
2298
2299 /*
2300 * Main tab is tab 0, so any current page other than 0 holds
2301 * a ge entry.
2302 */
2303 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2304 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002305 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002306
2307 return NULL;
2308}
2309
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002310static void file_close(GtkWidget *w, gpointer data)
2311{
2312 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002313 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002314
2315 /*
2316 * Can't close the main tab
2317 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002318 ge = get_ge_from_cur_tab(ui);
2319 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002320 gtk_widget_destroy(ge->vbox);
2321 return;
2322 }
2323
Jens Axboef5c67262012-03-13 08:20:41 +01002324 if (!flist_empty(&ui->list)) {
2325 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2326 return;
2327 }
2328
Jens Axboe0fd18982012-03-14 10:34:48 +01002329 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002330}
2331
Jens Axboe38634cb2012-03-13 12:26:41 +01002332static void file_add_recent(struct gui *ui, const gchar *uri)
2333{
Jens Axboea217ba72012-03-13 20:29:39 +01002334 GtkRecentData grd;
2335
2336 memset(&grd, 0, sizeof(grd));
2337 grd.display_name = strdup("gfio");
2338 grd.description = strdup("Fio job file");
2339 grd.mime_type = strdup(GFIO_MIME);
2340 grd.app_name = strdup(g_get_application_name());
2341 grd.app_exec = strdup("gfio %f/%u");
2342
2343 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002344}
2345
2346static gchar *get_filename_from_uri(const gchar *uri)
2347{
2348 if (strncmp(uri, "file://", 7))
2349 return strdup(uri);
2350
2351 return strdup(uri + 7);
2352}
2353
2354static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2355 int type, int port)
2356{
2357 struct fio_client *client;
2358 gchar *filename;
2359
2360 filename = get_filename_from_uri(uri);
2361
2362 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2363 ge->job_files[ge->nr_job_files] = strdup(filename);
2364 ge->nr_job_files++;
2365
2366 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2367 if (!client) {
2368 GError *error;
2369
2370 error = g_error_new(g_quark_from_string("fio"), 1,
2371 "Failed to add client %s", host);
2372 report_error(error);
2373 g_error_free(error);
2374 return 1;
2375 }
2376
2377 gfio_client_added(ge, client);
2378 file_add_recent(ge->ui, uri);
2379 return 0;
2380}
2381
Jens Axboea6790902012-03-13 15:16:11 +01002382static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002383{
Jens Axboea6790902012-03-13 15:16:11 +01002384 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002385 struct gui_entry *ge;
2386 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002387 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002388 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002389
2390 /*
2391 * Creates new tab if current tab is the main window, or the
2392 * current tab already has a client.
2393 */
2394 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002395 ge = get_ge_from_page(cur_page, &ge_is_new);
2396 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002397 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002398 ge_is_new = 1;
2399 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002400
2401 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002402
Jens Axboea6790902012-03-13 15:16:11 +01002403 if (get_connection_details(&host, &port, &type, &server_start)) {
2404 if (ge_is_new)
2405 gtk_widget_destroy(ge->vbox);
2406
2407 return 1;
2408 }
2409
2410 ret = do_file_open(ge, uri, host, type, port);
2411
2412 free(host);
2413
2414 if (!ret) {
2415 if (server_start)
2416 gfio_start_server();
2417 } else {
2418 if (ge_is_new)
2419 gtk_widget_destroy(ge->vbox);
2420 }
2421
2422 return ret;
2423}
2424
2425static void recent_open(GtkAction *action, gpointer data)
2426{
2427 struct gui *ui = (struct gui *) data;
2428 GtkRecentInfo *info;
2429 const gchar *uri;
2430
2431 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2432 uri = gtk_recent_info_get_uri(info);
2433
2434 do_file_open_with_tab(ui, uri);
2435}
2436
2437static void file_open(GtkWidget *w, gpointer data)
2438{
2439 struct gui *ui = data;
2440 GtkWidget *dialog;
2441 GSList *filenames, *fn_glist;
2442 GtkFileFilter *filter;
2443
Jens Axboe0420ba62012-02-29 11:16:52 +01002444 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002445 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002446 GTK_FILE_CHOOSER_ACTION_OPEN,
2447 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2448 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2449 NULL);
2450 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2451
2452 filter = gtk_file_filter_new();
2453 gtk_file_filter_add_pattern(filter, "*.fio");
2454 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002455 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002456 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002457 gtk_file_filter_set_name(filter, "Fio job file");
2458 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2459
2460 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2461 gtk_widget_destroy(dialog);
2462 return;
2463 }
2464
2465 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002466
2467 gtk_widget_destroy(dialog);
2468
Jens Axboe0420ba62012-02-29 11:16:52 +01002469 filenames = fn_glist;
2470 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002471 if (do_file_open_with_tab(ui, filenames->data))
2472 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002473 filenames = g_slist_next(filenames);
2474 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002475
Jens Axboe0420ba62012-02-29 11:16:52 +01002476 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002477}
2478
2479static void file_save(GtkWidget *w, gpointer data)
2480{
Jens Axboe63a130b2012-03-06 20:08:59 +01002481 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002482 GtkWidget *dialog;
2483
2484 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002485 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002486 GTK_FILE_CHOOSER_ACTION_SAVE,
2487 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2488 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2489 NULL);
2490
2491 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2492 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2493
2494 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2495 char *filename;
2496
2497 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2498 // save_job_file(filename);
2499 g_free(filename);
2500 }
2501 gtk_widget_destroy(dialog);
2502}
2503
Jens Axboe9b260bd2012-03-06 11:02:52 +01002504static void view_log_destroy(GtkWidget *w, gpointer data)
2505{
2506 struct gui *ui = (struct gui *) data;
2507
2508 gtk_widget_ref(ui->log_tree);
2509 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2510 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002511 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002512}
2513
2514static void view_log(GtkWidget *w, gpointer data)
2515{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002516 GtkWidget *win, *scroll, *vbox, *box;
2517 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002518
Jens Axboe4cbe7212012-03-06 13:36:17 +01002519 if (ui->log_view)
2520 return;
2521
2522 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002523 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002524 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002525
Jens Axboe4cbe7212012-03-06 13:36:17 +01002526 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002527
Jens Axboe4cbe7212012-03-06 13:36:17 +01002528 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2529
2530 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2531
2532 box = gtk_hbox_new(TRUE, 0);
2533 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2534 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2535 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2536
2537 vbox = gtk_vbox_new(TRUE, 5);
2538 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2539
2540 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002541 gtk_widget_show_all(win);
2542}
2543
Jens Axboe85dd01e2012-03-12 14:33:16 +01002544static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002545{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002546 struct gui *ui = (struct gui *) data;
2547 struct gui_entry *ge;
2548
2549 ge = get_ge_from_cur_tab(ui);
2550 if (ge)
2551 connect_clicked(w, ge);
2552}
2553
2554static void send_job_entry(GtkWidget *w, gpointer data)
2555{
2556 struct gui *ui = (struct gui *) data;
2557 struct gui_entry *ge;
2558
2559 ge = get_ge_from_cur_tab(ui);
2560 if (ge)
2561 send_clicked(w, ge);
2562
2563}
2564
2565static void edit_job_entry(GtkWidget *w, gpointer data)
2566{
2567}
2568
2569static void start_job_entry(GtkWidget *w, gpointer data)
2570{
2571 struct gui *ui = (struct gui *) data;
2572 struct gui_entry *ge;
2573
2574 ge = get_ge_from_cur_tab(ui);
2575 if (ge)
2576 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002577}
2578
Jens Axboe781ccba2012-03-15 09:44:42 +01002579static void view_results(GtkWidget *w, gpointer data)
2580{
2581 struct gui *ui = (struct gui *) data;
2582 struct gfio_client *gc;
2583 struct gui_entry *ge;
2584
2585 ge = get_ge_from_cur_tab(ui);
2586 if (!ge)
2587 return;
2588
2589 if (ge->results_window)
2590 return;
2591
2592 gc = ge->client;
2593 if (gc && gc->nr_results)
2594 gfio_display_end_results(gc);
2595}
2596
2597
Jens Axboe8577f4f2012-03-09 19:28:27 +01002598static void __update_graph_limits(struct gfio_graphs *g)
2599{
2600 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2601 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2602}
2603
2604static void update_graph_limits(void)
2605{
2606 struct flist_head *entry;
2607 struct gui_entry *ge;
2608
2609 __update_graph_limits(&main_ui.graphs);
2610
2611 flist_for_each(entry, &main_ui.list) {
2612 ge = flist_entry(entry, struct gui_entry, list);
2613 __update_graph_limits(&ge->graphs);
2614 }
2615}
2616
Jens Axboe46974a72012-03-02 19:34:13 +01002617static void preferences(GtkWidget *w, gpointer data)
2618{
Jens Axboef3e84402012-03-07 13:14:32 +01002619 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002620 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002621 int i;
2622
2623 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002624 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002625 GTK_DIALOG_DESTROY_WITH_PARENT,
2626 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2627 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2628 NULL);
2629
Jens Axboe8577f4f2012-03-09 19:28:27 +01002630 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002631 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2632 vbox = gtk_vbox_new(FALSE, 6);
2633 gtk_container_add(GTK_CONTAINER(frame), vbox);
2634
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002635 hbox = gtk_hbox_new(FALSE, 5);
2636 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2637 entry = gtk_label_new("Font face to use for graph labels");
2638 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2639
Jens Axboef3e84402012-03-07 13:14:32 +01002640 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002641 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002642
Jens Axboe8577f4f2012-03-09 19:28:27 +01002643 box = gtk_vbox_new(FALSE, 6);
2644 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2645
2646 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002647 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002648 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2649 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2650
Jens Axboec05d9052012-03-11 13:05:35 +01002651 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002652
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002653 box = gtk_vbox_new(FALSE, 6);
2654 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2655
2656 hbox = gtk_hbox_new(FALSE, 5);
2657 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2658 entry = gtk_label_new("Client ETA request interval (msec)");
2659 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2660
2661 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002662 frame = gtk_frame_new("Debug logging");
2663 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2664 vbox = gtk_vbox_new(FALSE, 6);
2665 gtk_container_add(GTK_CONTAINER(frame), vbox);
2666
2667 box = gtk_hbox_new(FALSE, 6);
2668 gtk_container_add(GTK_CONTAINER(vbox), box);
2669
2670 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2671
2672 for (i = 0; i < FD_DEBUG_MAX; i++) {
2673 if (i == 7) {
2674 box = gtk_hbox_new(FALSE, 6);
2675 gtk_container_add(GTK_CONTAINER(vbox), box);
2676 }
2677
2678
2679 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2680 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2681 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2682 }
2683
Jens Axboe46974a72012-03-02 19:34:13 +01002684 gtk_widget_show_all(dialog);
2685
2686 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2687 gtk_widget_destroy(dialog);
2688 return;
2689 }
2690
2691 for (i = 0; i < FD_DEBUG_MAX; i++) {
2692 int set;
2693
2694 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2695 if (set)
2696 fio_debug |= (1UL << i);
2697 }
2698
Jens Axboef3e84402012-03-07 13:14:32 +01002699 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002700 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2701 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002702 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002703
Jens Axboe46974a72012-03-02 19:34:13 +01002704 gtk_widget_destroy(dialog);
2705}
2706
Jens Axboe0420ba62012-02-29 11:16:52 +01002707static void about_dialog(GtkWidget *w, gpointer data)
2708{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002709 const char *authors[] = {
2710 "Jens Axboe <axboe@kernel.dk>",
2711 "Stephen Carmeron <stephenmcameron@gmail.com>",
2712 NULL
2713 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002714 const char *license[] = {
2715 "Fio is free software; you can redistribute it and/or modify "
2716 "it under the terms of the GNU General Public License as published by "
2717 "the Free Software Foundation; either version 2 of the License, or "
2718 "(at your option) any later version.\n",
2719 "Fio is distributed in the hope that it will be useful, "
2720 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2721 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2722 "GNU General Public License for more details.\n",
2723 "You should have received a copy of the GNU General Public License "
2724 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2725 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2726 };
2727 char *license_trans;
2728
2729 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2730 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002731
Jens Axboe0420ba62012-02-29 11:16:52 +01002732 gtk_show_about_dialog(NULL,
2733 "program-name", "gfio",
2734 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002735 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002736 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2737 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002738 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002739 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002740 "logo-icon-name", "fio",
2741 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002742 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002743 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002744
Jens Axboe2f99deb2012-03-09 14:37:29 +01002745 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002746}
2747
2748static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002749 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002750 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002751 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002752 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002753 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002754 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002755 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2756 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2757 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002758 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01002759 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002760 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2761 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2762 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2763 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002764 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2765 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002766};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002767static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002768
2769static const gchar *ui_string = " \
2770 <ui> \
2771 <menubar name=\"MainMenu\"> \
2772 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002773 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002774 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002775 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002776 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2777 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002778 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002779 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2780 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002781 <placeholder name=\"FileRecentFiles\"/> \
2782 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002783 <menuitem name=\"Quit\" action=\"Quit\" /> \
2784 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002785 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002786 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002787 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002788 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2789 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002790 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002791 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002792 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002793 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01002794 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2795 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002796 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2797 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002798 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2799 <menuitem name=\"About\" action=\"About\" /> \
2800 </menu> \
2801 </menubar> \
2802 </ui> \
2803";
2804
Jens Axboe4cbe7212012-03-06 13:36:17 +01002805static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2806 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002807{
Jens Axboeca664f42012-03-14 19:49:40 +01002808 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002809 GError *error = 0;
2810
2811 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002812 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002813
2814 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2815 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2816
2817 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002818
Jens Axboe0420ba62012-02-29 11:16:52 +01002819 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2820}
2821
2822void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2823 GtkWidget *vbox, GtkUIManager *ui_manager)
2824{
2825 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2826}
2827
Jens Axboec80b74b2012-03-12 10:23:28 +01002828static void combo_entry_changed(GtkComboBox *box, gpointer data)
2829{
2830 struct gui_entry *ge = (struct gui_entry *) data;
2831 gint index;
2832
2833 index = gtk_combo_box_get_active(box);
2834
2835 multitext_set_entry(&ge->eta.iotype, index);
2836 multitext_set_entry(&ge->eta.ioengine, index);
2837 multitext_set_entry(&ge->eta.iodepth, index);
2838}
2839
2840static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2841{
2842 struct gui_entry *ge = (struct gui_entry *) data;
2843
2844 multitext_free(&ge->eta.iotype);
2845 multitext_free(&ge->eta.ioengine);
2846 multitext_free(&ge->eta.iodepth);
2847}
2848
Jens Axboe2f99deb2012-03-09 14:37:29 +01002849static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002850{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002851 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002852 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01002853
Jens Axboe2f99deb2012-03-09 14:37:29 +01002854 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002855
Jens Axboe65476332012-03-13 10:37:04 +01002856 top_align = gtk_alignment_new(0, 0, 1, 0);
2857 top_vbox = gtk_vbox_new(FALSE, 3);
2858 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2859 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002860
Jens Axboe3e47bd22012-02-29 13:45:02 +01002861 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002862 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002863 probe_frame = gtk_vbox_new(FALSE, 3);
2864 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2865
2866 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002867 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2868 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2869 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2870 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2871 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002872
Jens Axboe3e47bd22012-02-29 13:45:02 +01002873 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002874 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2875
Jens Axboe3863d1a2012-03-09 17:39:05 +01002876 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002877 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2878 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2879 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2880 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2881 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002882 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2883 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2884
2885 probe_box = gtk_hbox_new(FALSE, 3);
2886 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2887 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2888 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2889 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2890 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2891
2892 /*
2893 * Only add this if we have a commit rate
2894 */
2895#if 0
2896 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002897 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002898
Jens Axboe2f99deb2012-03-09 14:37:29 +01002899 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2900 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2901
2902 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2903 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2904#endif
2905
2906 /*
2907 * Set up a drawing area and IOPS and bandwidth graphs
2908 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002909 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002910 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002911 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002912 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2913 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2914 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2915 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2916 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002917 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2918 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002919 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002920 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002921 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002922 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002923
2924 setup_graphs(&ge->graphs);
2925
2926 /*
2927 * Set up alignments for widgets at the bottom of ui,
2928 * align bottom left, expand horizontally but not vertically
2929 */
Jens Axboe65476332012-03-13 10:37:04 +01002930 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002931 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002932 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2933 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002934
2935 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2936
2937 /*
2938 * Set up thread status progress bar
2939 */
2940 ge->thread_status_pb = gtk_progress_bar_new();
2941 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2942 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2943 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2944
2945
2946 return main_vbox;
2947}
2948
2949static GtkWidget *new_main_page(struct gui *ui)
2950{
2951 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002952 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002953
2954 main_vbox = gtk_vbox_new(FALSE, 3);
2955
2956 /*
2957 * Set up alignments for widgets at the top of ui,
2958 * align top left, expand horizontally but not vertically
2959 */
Jens Axboe65476332012-03-13 10:37:04 +01002960 top_align = gtk_alignment_new(0, 0, 1, 0);
2961 top_vbox = gtk_vbox_new(FALSE, 0);
2962 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2963 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002964
2965 probe = gtk_frame_new("Run statistics");
2966 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2967 probe_frame = gtk_vbox_new(FALSE, 3);
2968 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002969
2970 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002971 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01002972 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01002973 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2974 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2975 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2976 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002977
2978 /*
2979 * Only add this if we have a commit rate
2980 */
2981#if 0
2982 probe_box = gtk_hbox_new(FALSE, 3);
2983 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2984
Jens Axboe3e47bd22012-02-29 13:45:02 +01002985 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2986 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2987
Jens Axboe3e47bd22012-02-29 13:45:02 +01002988 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2989 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002990#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002991
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002992 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002993 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002994 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002995 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002996 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002997 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002998 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2999 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
3000 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
3001 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
3002 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01003003 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
3004 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003005 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01003006 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01003007 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01003008 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01003009 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01003010
Jens Axboe2f99deb2012-03-09 14:37:29 +01003011 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01003012
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003013 /*
3014 * Set up alignments for widgets at the bottom of ui,
3015 * align bottom left, expand horizontally but not vertically
3016 */
Jens Axboe65476332012-03-13 10:37:04 +01003017 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003018 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01003019 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
3020 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01003021
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003022 /*
3023 * Set up thread status progress bar
3024 */
3025 ui->thread_status_pb = gtk_progress_bar_new();
3026 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01003027 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003028 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
3029
Jens Axboe2f99deb2012-03-09 14:37:29 +01003030 return main_vbox;
3031}
3032
3033static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
3034 guint page, gpointer data)
3035
3036{
Jens Axboe02421e62012-03-12 12:05:50 +01003037 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01003038 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01003039
Jens Axboe85dd01e2012-03-12 14:33:16 +01003040 if (!page) {
3041 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01003042 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003043 return TRUE;
3044 }
3045
3046 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01003047 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01003048 if (ge)
3049 update_button_states(ui, ge);
3050
Jens Axboe2f99deb2012-03-09 14:37:29 +01003051 return TRUE;
3052}
3053
Jens Axboe38634cb2012-03-13 12:26:41 +01003054static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3055{
3056 time_t time_a = gtk_recent_info_get_visited(a);
3057 time_t time_b = gtk_recent_info_get_visited(b);
3058
3059 return time_b - time_a;
3060}
3061
3062static void add_recent_file_items(struct gui *ui)
3063{
3064 const gchar *gfio = g_get_application_name();
3065 GList *items, *item;
3066 int i = 0;
3067
3068 if (ui->recent_ui_id) {
3069 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3070 gtk_ui_manager_ensure_update(ui->uimanager);
3071 }
3072 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3073
3074 if (ui->actiongroup) {
3075 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3076 g_object_unref(ui->actiongroup);
3077 }
3078 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3079
3080 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3081
3082 items = gtk_recent_manager_get_items(ui->recentmanager);
3083 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3084
3085 for (item = items; item && item->data; item = g_list_next(item)) {
3086 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3087 gchar *action_name;
3088 const gchar *label;
3089 GtkAction *action;
3090
3091 if (!gtk_recent_info_has_application(info, gfio))
3092 continue;
3093
3094 /*
3095 * We only support local files for now
3096 */
3097 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3098 continue;
3099
3100 action_name = g_strdup_printf("RecentFile%u", i++);
3101 label = gtk_recent_info_get_display_name(info);
3102
3103 action = g_object_new(GTK_TYPE_ACTION,
3104 "name", action_name,
3105 "label", label, NULL);
3106
3107 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3108 gtk_recent_info_ref(info),
3109 (GDestroyNotify) gtk_recent_info_unref);
3110
3111
3112 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3113
3114 gtk_action_group_add_action(ui->actiongroup, action);
3115 g_object_unref(action);
3116
3117 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3118 "/MainMenu/FileMenu/FileRecentFiles",
3119 label, action_name,
3120 GTK_UI_MANAGER_MENUITEM, FALSE);
3121
3122 g_free(action_name);
3123
3124 if (i == 8)
3125 break;
3126 }
3127
3128 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3129 g_list_free(items);
3130}
3131
Jens Axboea6790902012-03-13 15:16:11 +01003132static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3133 gint x, gint y, GtkSelectionData *data,
3134 guint info, guint time)
3135{
3136 struct gui *ui = &main_ui;
3137 gchar **uris;
3138 GtkWidget *source;
3139 int i;
3140
3141 source = gtk_drag_get_source_widget(ctx);
3142 if (source && widget == gtk_widget_get_toplevel(source)) {
3143 gtk_drag_finish(ctx, FALSE, FALSE, time);
3144 return;
3145 }
3146
3147 uris = gtk_selection_data_get_uris(data);
3148 if (!uris) {
3149 gtk_drag_finish(ctx, FALSE, FALSE, time);
3150 return;
3151 }
3152
3153 i = 0;
3154 while (uris[i]) {
3155 if (do_file_open_with_tab(ui, uris[i]))
3156 break;
3157 i++;
3158 }
3159
3160 gtk_drag_finish(ctx, TRUE, FALSE, time);
3161 g_strfreev(uris);
3162}
3163
Jens Axboe2f99deb2012-03-09 14:37:29 +01003164static void init_ui(int *argc, char **argv[], struct gui *ui)
3165{
3166 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003167 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003168
3169 /* Magical g*thread incantation, you just need this thread stuff.
3170 * Without it, the update that happens in gfio_update_thread_status
3171 * doesn't really happen in a timely fashion, you need expose events
3172 */
3173 if (!g_thread_supported())
3174 g_thread_init(NULL);
3175 gdk_threads_init();
3176
3177 gtk_init(argc, argv);
3178 settings = gtk_settings_get_default();
3179 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3180 g_type_init();
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003181 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003182
3183 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003184 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003185 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3186
3187 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3188 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3189
3190 ui->vbox = gtk_vbox_new(FALSE, 0);
3191 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3192
Jens Axboe02421e62012-03-12 12:05:50 +01003193 ui->uimanager = gtk_ui_manager_new();
3194 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3195 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003196
Jens Axboe38634cb2012-03-13 12:26:41 +01003197 ui->recentmanager = gtk_recent_manager_get_default();
3198 add_recent_file_items(ui);
3199
Jens Axboe2f99deb2012-03-09 14:37:29 +01003200 ui->notebook = gtk_notebook_new();
3201 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003202 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003203 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003204 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3205
3206 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003207 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3208 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3209 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003210
3211 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3212
Jens Axboe9b260bd2012-03-06 11:02:52 +01003213 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003214
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003215 gtk_widget_show_all(ui->window);
3216}
3217
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003218int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003219{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003220 if (initialize_fio(envp))
3221 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003222 if (fio_init_options())
3223 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003224
Jens Axboe2f99deb2012-03-09 14:37:29 +01003225 memset(&main_ui, 0, sizeof(main_ui));
3226 INIT_FLIST_HEAD(&main_ui.list);
3227
3228 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003229
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003230 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003231 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003232 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003233 return 0;
3234}