blob: 02211f4aaded9a99fa845cdf05f8e52cca3331ac [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 Axboe63a130b2012-03-06 20:08:59 +010035static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010036static const char *gfio_graph_font;
Jens Axboe8577f4f2012-03-09 19:28:27 +010037static unsigned int gfio_graph_limit = 100;
Jens Axboe63a130b2012-03-06 20:08:59 +010038
Jens Axboe6b79c802012-03-08 10:51:36 +010039static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010040
Stephen M. Cameronf3074002012-02-24 08:17:30 +010041#define ARRAYSIZE(x) (sizeof((x)) / (sizeof((x)[0])))
42
43typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
44
Jens Axboe3e47bd22012-02-29 13:45:02 +010045static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010046static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010047static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010048
49static struct button_spec {
50 const char *buttontext;
51 clickfunction f;
52 const char *tooltiptext;
Jens Axboe3e47bd22012-02-29 13:45:02 +010053 const int start_insensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010054} buttonspeclist[] = {
Jens Axboe3e47bd22012-02-29 13:45:02 +010055#define CONNECT_BUTTON 0
Jens Axboeb9d2f302012-03-08 20:36:28 +010056#define SEND_BUTTON 1
57#define START_JOB_BUTTON 2
Jens Axboe3e47bd22012-02-29 13:45:02 +010058 { "Connect", connect_clicked, "Connect to host", 0 },
Jens Axboeb9d2f302012-03-08 20:36:28 +010059 { "Send", send_clicked, "Send job description to host", 1 },
60 { "Start Job", start_job_clicked,
Jens Axboe2f99deb2012-03-09 14:37:29 +010061 "Start the current job on the server", 1 },
Stephen M. Cameronf3074002012-02-24 08:17:30 +010062};
63
Jens Axboe843ad232012-02-29 11:44:53 +010064struct probe_widget {
65 GtkWidget *hostname;
66 GtkWidget *os;
67 GtkWidget *arch;
68 GtkWidget *fio_ver;
69};
70
Jens Axboec80b74b2012-03-12 10:23:28 +010071struct multitext_widget {
72 GtkWidget *entry;
73 char **text;
74 unsigned int cur_text;
75 unsigned int max_text;
76};
77
Jens Axboe3e47bd22012-02-29 13:45:02 +010078struct eta_widget {
Jens Axboe3863d1a2012-03-09 17:39:05 +010079 GtkWidget *names;
Jens Axboec80b74b2012-03-12 10:23:28 +010080 struct multitext_widget iotype;
81 struct multitext_widget ioengine;
82 struct multitext_widget iodepth;
Jens Axboe3e47bd22012-02-29 13:45:02 +010083 GtkWidget *jobs;
84 GtkWidget *files;
85 GtkWidget *read_bw;
86 GtkWidget *read_iops;
87 GtkWidget *cr_bw;
88 GtkWidget *cr_iops;
89 GtkWidget *write_bw;
90 GtkWidget *write_iops;
91 GtkWidget *cw_bw;
92 GtkWidget *cw_iops;
93};
94
Jens Axboe2f99deb2012-03-09 14:37:29 +010095struct gfio_graphs {
96#define DRAWING_AREA_XDIM 1000
97#define DRAWING_AREA_YDIM 400
98 GtkWidget *drawing_area;
Jens Axboe2f99deb2012-03-09 14:37:29 +010099 struct graph *iops_graph;
100 struct graph *bandwidth_graph;
101};
102
103/*
104 * Main window widgets and data
105 */
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100106struct gui {
Jens Axboe02421e62012-03-12 12:05:50 +0100107 GtkUIManager *uimanager;
108 GtkWidget *menu;
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100109 GtkWidget *window;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +0100110 GtkWidget *vbox;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100111 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100112 GtkWidget *buttonbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100113 GtkWidget *notebook;
114 GtkWidget *error_info_bar;
115 GtkWidget *error_label;
116 GtkListStore *log_model;
117 GtkWidget *log_tree;
118 GtkWidget *log_view;
119 struct gfio_graphs graphs;
120 struct probe_widget probe;
121 struct eta_widget eta;
122 pthread_t server_t;
123
Jens Axboea9eccde2012-03-09 14:59:42 +0100124 pthread_t t;
125 int handler_running;
126
Jens Axboe2f99deb2012-03-09 14:37:29 +0100127 struct flist_head list;
128} main_ui;
129
Jens Axboe85dd01e2012-03-12 14:33:16 +0100130enum {
131 GE_STATE_NEW = 1,
132 GE_STATE_CONNECTED,
133 GE_STATE_JOB_SENT,
134 GE_STATE_JOB_STARTED,
135 GE_STATE_JOB_RUNNING,
136 GE_STATE_JOB_DONE,
137};
138
Jens Axboe2f99deb2012-03-09 14:37:29 +0100139/*
140 * Notebook entry
141 */
142struct gui_entry {
143 struct flist_head list;
144 struct gui *ui;
145
146 GtkWidget *vbox;
Jens Axboec80b74b2012-03-12 10:23:28 +0100147 GtkWidget *job_notebook;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100148 GtkWidget *thread_status_pb;
149 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100150 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Jens Axboe2f99deb2012-03-09 14:37:29 +0100151 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100152 GtkWidget *error_info_bar;
153 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100154 GtkWidget *results_notebook;
155 GtkWidget *results_window;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100156 GtkListStore *log_model;
157 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100158 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100159 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100160 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100161 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100162 GtkWidget *page_label;
163 gint page_num;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100164 unsigned int state;
Jens Axboe0420ba62012-02-29 11:16:52 +0100165
Jens Axboeb9d2f302012-03-08 20:36:28 +0100166 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100167 int nr_job_files;
168 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100169};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100170
Jens Axboee0681f32012-03-06 12:14:42 +0100171struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100172 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100173 struct fio_client *client;
Jens Axboee0681f32012-03-06 12:14:42 +0100174 GtkWidget *results_widget;
175 GtkWidget *disk_util_frame;
Jens Axboe6b79c802012-03-08 10:51:36 +0100176 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100177 unsigned int job_added;
178 struct thread_options o;
Jens Axboee0681f32012-03-06 12:14:42 +0100179};
180
Jens Axboe9988ca72012-03-09 15:14:06 +0100181static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
182static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +0100183void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +0100184
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100185static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
186{
187 switch (power_of_ten) {
188 case 9: graph_y_title(g, "Billions of IOs / sec");
189 break;
190 case 6: graph_y_title(g, "Millions of IOs / sec");
191 break;
192 case 3: graph_y_title(g, "Thousands of IOs / sec");
193 break;
194 case 0:
195 default: graph_y_title(g, "IOs / sec");
196 break;
197 }
198}
199
Jens Axboe2f99deb2012-03-09 14:37:29 +0100200static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100201{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100202 struct graph *g;
203
204 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
205 graph_title(g, "IOPS");
206 graph_x_title(g, "Time (secs)");
207 graph_y_title(g, "IOs / sec");
208 graph_add_label(g, "Read IOPS");
209 graph_add_label(g, "Write IOPS");
210 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
211 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +0100212 line_graph_set_data_count_limit(g, gfio_graph_limit);
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100213 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
Stephen M. Camerondef0ac22012-03-12 07:32:57 +0100214 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100215 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100216}
217
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100218static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
219{
220 switch (power_of_ten) {
221 case 9: graph_y_title(g, "Petabytes / sec");
222 break;
223 case 6: graph_y_title(g, "Gigabytes / sec");
224 break;
225 case 3: graph_y_title(g, "Megabytes / sec");
226 break;
227 case 0:
228 default: graph_y_title(g, "Kilobytes / sec");
229 break;
230 }
231}
232
Jens Axboe2f99deb2012-03-09 14:37:29 +0100233static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100234{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100235 struct graph *g;
236
237 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
238 graph_title(g, "Bandwidth");
239 graph_x_title(g, "Time (secs)");
240 graph_y_title(g, "Kbytes / sec");
241 graph_add_label(g, "Read Bandwidth");
242 graph_add_label(g, "Write Bandwidth");
243 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
244 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
245 line_graph_set_data_count_limit(g, 100);
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100246 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
Stephen M. Camerondef0ac22012-03-12 07:32:57 +0100247 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
248
Jens Axboe2f99deb2012-03-09 14:37:29 +0100249 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100250}
251
Jens Axboe2f99deb2012-03-09 14:37:29 +0100252static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100253{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100254 g->iops_graph = setup_iops_graph();
255 g->bandwidth_graph = setup_bandwidth_graph();
256}
257
Jens Axboec80b74b2012-03-12 10:23:28 +0100258static void multitext_add_entry(struct multitext_widget *mt, const char *text)
259{
260 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
261 mt->text[mt->max_text] = strdup(text);
262 mt->max_text++;
263}
264
265static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
266{
267 if (index >= mt->max_text)
268 return;
Jens Axboeda185432012-03-12 11:05:46 +0100269 if (!mt->text || !mt->text[index])
Jens Axboec80b74b2012-03-12 10:23:28 +0100270 return;
271
272 mt->cur_text = index;
273 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
274}
275
276static void multitext_update_entry(struct multitext_widget *mt,
277 unsigned int index, const char *text)
278{
Jens Axboeda185432012-03-12 11:05:46 +0100279 if (!mt->text)
280 return;
281
Jens Axboec80b74b2012-03-12 10:23:28 +0100282 if (mt->text[index])
283 free(mt->text[index]);
284
285 mt->text[index] = strdup(text);
286 if (mt->cur_text == index)
287 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
288}
289
290static void multitext_free(struct multitext_widget *mt)
291{
292 int i;
293
294 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
295
296 for (i = 0; i < mt->max_text; i++) {
297 if (mt->text[i])
298 free(mt->text[i]);
299 }
300
301 free(mt->text);
302 mt->cur_text = -1;
303 mt->max_text = 0;
304}
305
Jens Axboe2f99deb2012-03-09 14:37:29 +0100306static void clear_ge_ui_info(struct gui_entry *ge)
307{
308 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
309 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
310 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
311 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100312#if 0
313 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100314 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100315#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100316 multitext_update_entry(&ge->eta.iotype, 0, "");
317 multitext_update_entry(&ge->eta.ioengine, 0, "");
318 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100319 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
320 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
321 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
322 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
323 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
324 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100325}
326
Jens Axboe3863d1a2012-03-09 17:39:05 +0100327static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
328{
329 GtkWidget *entry, *frame;
330
331 frame = gtk_frame_new(label);
332 entry = gtk_combo_box_new_text();
333 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
334 gtk_container_add(GTK_CONTAINER(frame), entry);
335
336 return entry;
337}
338
Jens Axboe3650a3c2012-03-05 14:09:03 +0100339static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
340{
341 GtkWidget *entry, *frame;
342
343 frame = gtk_frame_new(label);
344 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100345 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100346 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
347 gtk_container_add(GTK_CONTAINER(frame), entry);
348
349 return entry;
350}
351
352static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
353{
354 GtkWidget *label_widget;
355 GtkWidget *frame;
356
357 frame = gtk_frame_new(label);
358 label_widget = gtk_label_new(NULL);
359 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
360 gtk_container_add(GTK_CONTAINER(frame), label_widget);
361
362 return label_widget;
363}
364
365static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
366{
367 GtkWidget *button, *box;
368
369 box = gtk_hbox_new(FALSE, 3);
370 gtk_container_add(GTK_CONTAINER(hbox), box);
371
372 button = gtk_spin_button_new_with_range(min, max, 1.0);
373 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
374
375 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
376 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
377
378 return button;
379}
380
Jens Axboe3650a3c2012-03-05 14:09:03 +0100381static void label_set_int_value(GtkWidget *entry, unsigned int val)
382{
383 char tmp[80];
384
385 sprintf(tmp, "%u", val);
386 gtk_label_set_text(GTK_LABEL(entry), tmp);
387}
388
389static void entry_set_int_value(GtkWidget *entry, unsigned int val)
390{
391 char tmp[80];
392
393 sprintf(tmp, "%u", val);
394 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
395}
396
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100397static void show_info_dialog(struct gui *ui, const char *title,
398 const char *message)
399{
400 GtkWidget *dialog, *content, *label;
401
402 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
403 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
404 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
405
406 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
407 label = gtk_label_new(message);
408 gtk_container_add(GTK_CONTAINER(content), label);
409 gtk_widget_show_all(dialog);
410 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
411 gtk_dialog_run(GTK_DIALOG(dialog));
412 gtk_widget_destroy(dialog);
413}
414
Jens Axboe85dd01e2012-03-12 14:33:16 +0100415/*
416 * Update sensitivity of job buttons and job menu items, based on the
417 * state of the client.
418 */
419static void update_button_states(struct gui *ui, struct gui_entry *ge)
420{
421 unsigned int connect_state, send_state, start_state, edit_state;
422 const char *connect_str = NULL;
423 GtkWidget *w;
424
425 switch (ge->state) {
426 default: {
427 char tmp[80];
428
429 sprintf(tmp, "Bad client state: %u\n", ge->state);
430 show_info_dialog(ui, "Error", tmp);
431 /* fall through to new state */
432 }
433
434 case GE_STATE_NEW:
435 connect_state = 1;
436 edit_state = 0;
437 connect_str = "Connect";
438 send_state = 0;
439 start_state = 0;
440 break;
441 case GE_STATE_CONNECTED:
442 connect_state = 1;
443 edit_state = 0;
444 connect_str = "Disconnect";
445 send_state = 1;
446 start_state = 0;
447 break;
448 case GE_STATE_JOB_SENT:
449 connect_state = 1;
450 edit_state = 0;
451 connect_str = "Disconnect";
452 send_state = 0;
453 start_state = 1;
454 break;
455 case GE_STATE_JOB_STARTED:
456 connect_state = 1;
457 edit_state = 1;
458 connect_str = "Disconnect";
459 send_state = 0;
460 start_state = 1;
461 break;
462 case GE_STATE_JOB_RUNNING:
463 connect_state = 1;
464 edit_state = 0;
465 connect_str = "Disconnect";
466 send_state = 0;
467 start_state = 0;
468 break;
469 case GE_STATE_JOB_DONE:
470 connect_state = 1;
471 edit_state = 0;
472 connect_str = "Connect";
473 send_state = 0;
474 start_state = 0;
475 break;
476 }
477
478 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
479 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
480 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
481 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
482
483 /*
484 * So the below doesn't work at all, how to set those menu items
485 * invisibible...
486 */
487 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Connect");
Jens Axboe21ba9042012-03-13 10:58:59 +0100488 gtk_widget_set_sensitive(w, connect_state);
489 gtk_menu_item_set_label(GTK_MENU_ITEM(w), connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100490
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100491 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Edit job");
Jens Axboe21ba9042012-03-13 10:58:59 +0100492 gtk_widget_set_sensitive(w, edit_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100493
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100494 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Send job");
Jens Axboe21ba9042012-03-13 10:58:59 +0100495 gtk_widget_set_sensitive(w, send_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100496
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100497 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Start job");
Jens Axboe21ba9042012-03-13 10:58:59 +0100498 gtk_widget_set_sensitive(w, start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100499}
500
501static void gfio_set_state(struct gui_entry *ge, unsigned int state)
502{
503 ge->state = state;
504 update_button_states(ge->ui, ge);
505}
506
Jens Axboea2697902012-03-05 16:43:49 +0100507#define ALIGN_LEFT 1
508#define ALIGN_RIGHT 2
509#define INVISIBLE 4
510#define UNSORTABLE 8
511
512GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
513{
514 GtkCellRenderer *renderer;
515 GtkTreeViewColumn *col;
516 double xalign = 0.0; /* left as default */
517 PangoAlignment align;
518 gboolean visible;
519
520 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
521 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
522 PANGO_ALIGN_CENTER;
523 visible = !(flags & INVISIBLE);
524
525 renderer = gtk_cell_renderer_text_new();
526 col = gtk_tree_view_column_new();
527
528 gtk_tree_view_column_set_title(col, title);
529 if (!(flags & UNSORTABLE))
530 gtk_tree_view_column_set_sort_column_id(col, index);
531 gtk_tree_view_column_set_resizable(col, TRUE);
532 gtk_tree_view_column_pack_start(col, renderer, TRUE);
533 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
534 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
535 switch (align) {
536 case PANGO_ALIGN_LEFT:
537 xalign = 0.0;
538 break;
539 case PANGO_ALIGN_CENTER:
540 xalign = 0.5;
541 break;
542 case PANGO_ALIGN_RIGHT:
543 xalign = 1.0;
544 break;
545 }
546 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
547 gtk_tree_view_column_set_visible(col, visible);
548 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
549 return col;
550}
551
Jens Axboe9b260bd2012-03-06 11:02:52 +0100552static void gfio_ui_setup_log(struct gui *ui)
553{
554 GtkTreeSelection *selection;
555 GtkListStore *model;
556 GtkWidget *tree_view;
557
558 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
559
560 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
561 gtk_widget_set_can_focus(tree_view, FALSE);
562
563 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
564 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100565 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
566 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100567
568 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
569 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
570 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100571 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100572
573 ui->log_model = model;
574 ui->log_tree = tree_view;
575}
576
Jens Axboea2697902012-03-05 16:43:49 +0100577static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
578 fio_fp64_t *plist,
579 unsigned int len,
580 const char *base,
581 unsigned int scale)
582{
583 GType types[FIO_IO_U_LIST_MAX_LEN];
584 GtkWidget *tree_view;
585 GtkTreeSelection *selection;
586 GtkListStore *model;
587 GtkTreeIter iter;
588 int i;
589
590 for (i = 0; i < len; i++)
591 types[i] = G_TYPE_INT;
592
593 model = gtk_list_store_newv(len, types);
594
595 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
596 gtk_widget_set_can_focus(tree_view, FALSE);
597
Jens Axboe661f7412012-03-06 13:55:45 +0100598 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
599 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
600
Jens Axboea2697902012-03-05 16:43:49 +0100601 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
602 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
603
604 for (i = 0; i < len; i++) {
605 char fbuf[8];
606
607 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
608 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
609 }
610
611 gtk_list_store_append(model, &iter);
612
Jens Axboee0681f32012-03-06 12:14:42 +0100613 for (i = 0; i < len; i++) {
614 if (scale)
615 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100616 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100617 }
Jens Axboea2697902012-03-05 16:43:49 +0100618
619 return tree_view;
620}
621
622static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
623 int ddir)
624{
625 unsigned int *io_u_plat = ts->io_u_plat[ddir];
626 unsigned long nr = ts->clat_stat[ddir].samples;
627 fio_fp64_t *plist = ts->percentile_list;
628 unsigned int *ovals, len, minv, maxv, scale_down;
629 const char *base;
630 GtkWidget *tree_view, *frame, *hbox;
631 char tmp[64];
632
633 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
634 if (!len)
635 goto out;
636
637 /*
638 * We default to usecs, but if the value range is such that we
639 * should scale down to msecs, do that.
640 */
641 if (minv > 2000 && maxv > 99999) {
642 scale_down = 1;
643 base = "msec";
644 } else {
645 scale_down = 0;
646 base = "usec";
647 }
648
649 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
650
651 sprintf(tmp, "Completion percentiles (%s)", base);
652 frame = gtk_frame_new(tmp);
653 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
654
655 hbox = gtk_hbox_new(FALSE, 3);
656 gtk_container_add(GTK_CONTAINER(frame), hbox);
657
658 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
659out:
660 if (ovals)
661 free(ovals);
662}
663
Jens Axboe3650a3c2012-03-05 14:09:03 +0100664static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
665 unsigned long max, double mean, double dev)
666{
667 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100668 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100669 char *minp, *maxp;
670 char tmp[64];
671
672 if (!usec_to_msec(&min, &max, &mean, &dev))
673 base = "(msec)";
674
675 minp = num2str(min, 6, 1, 0);
676 maxp = num2str(max, 6, 1, 0);
677
Jens Axboe3650a3c2012-03-05 14:09:03 +0100678 sprintf(tmp, "%s %s", name, base);
679 frame = gtk_frame_new(tmp);
680 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
681
Jens Axboe3650a3c2012-03-05 14:09:03 +0100682 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100683 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100684
685 label = new_info_label_in_frame(hbox, "Minimum");
686 gtk_label_set_text(GTK_LABEL(label), minp);
687 label = new_info_label_in_frame(hbox, "Maximum");
688 gtk_label_set_text(GTK_LABEL(label), maxp);
689 label = new_info_label_in_frame(hbox, "Average");
690 sprintf(tmp, "%5.02f", mean);
691 gtk_label_set_text(GTK_LABEL(label), tmp);
692 label = new_info_label_in_frame(hbox, "Standard deviation");
693 sprintf(tmp, "%5.02f", dev);
694 gtk_label_set_text(GTK_LABEL(label), tmp);
695
696 free(minp);
697 free(maxp);
698
699}
700
Jens Axboeca850992012-03-05 20:04:43 +0100701#define GFIO_CLAT 1
702#define GFIO_SLAT 2
703#define GFIO_LAT 4
704
Jens Axboe3650a3c2012-03-05 14:09:03 +0100705static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
706 struct thread_stat *ts, int ddir)
707{
708 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100709 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100710 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100711 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100712 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100713 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100714 char *io_p, *bw_p, *iops_p;
715 int i2p;
716
717 if (!ts->runtime[ddir])
718 return;
719
720 i2p = is_power_of_2(rs->kb_base);
721 runt = ts->runtime[ddir];
722
723 bw = (1000 * ts->io_bytes[ddir]) / runt;
724 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
725 bw_p = num2str(bw, 6, 1, i2p);
726
727 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
728 iops_p = num2str(iops, 6, 1, 0);
729
730 box = gtk_hbox_new(FALSE, 3);
731 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
732
733 frame = gtk_frame_new(ddir_label[ddir]);
734 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
735
Jens Axboe0b761302012-03-05 20:44:11 +0100736 main_vbox = gtk_vbox_new(FALSE, 3);
737 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100738
739 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100740 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100741
742 label = new_info_label_in_frame(box, "IO");
743 gtk_label_set_text(GTK_LABEL(label), io_p);
744 label = new_info_label_in_frame(box, "Bandwidth");
745 gtk_label_set_text(GTK_LABEL(label), bw_p);
746 label = new_info_label_in_frame(box, "IOPS");
747 gtk_label_set_text(GTK_LABEL(label), iops_p);
748 label = new_info_label_in_frame(box, "Runtime (msec)");
749 label_set_int_value(label, ts->runtime[ddir]);
750
Jens Axboee0681f32012-03-06 12:14:42 +0100751 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100752 double p_of_agg = 100.0;
753 const char *bw_str = "KB";
754 char tmp[32];
755
756 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100757 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100758 if (p_of_agg > 100.0)
759 p_of_agg = 100.0;
760 }
761
Jens Axboee0681f32012-03-06 12:14:42 +0100762 if (mean[0] > 999999.9) {
763 min[0] /= 1000.0;
764 max[0] /= 1000.0;
765 mean[0] /= 1000.0;
766 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100767 bw_str = "MB";
768 }
769
Jens Axboe0b761302012-03-05 20:44:11 +0100770 sprintf(tmp, "Bandwidth (%s)", bw_str);
771 frame = gtk_frame_new(tmp);
772 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100773
Jens Axboe0b761302012-03-05 20:44:11 +0100774 box = gtk_hbox_new(FALSE, 3);
775 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100776
Jens Axboe0b761302012-03-05 20:44:11 +0100777 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100778 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100779 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100780 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100781 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100782 sprintf(tmp, "%3.2f%%", p_of_agg);
783 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100784 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100785 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100786 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100787 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100788 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100789 gtk_label_set_text(GTK_LABEL(label), tmp);
790 }
791
Jens Axboee0681f32012-03-06 12:14:42 +0100792 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100793 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100794 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100795 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100796 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100797 flags |= GFIO_LAT;
798
799 if (flags) {
800 frame = gtk_frame_new("Latency");
801 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
802
803 vbox = gtk_vbox_new(FALSE, 3);
804 gtk_container_add(GTK_CONTAINER(frame), vbox);
805
806 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100807 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100808 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100809 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100810 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100811 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100812 }
813
814 if (ts->clat_percentiles)
815 gfio_show_clat_percentiles(main_vbox, ts, ddir);
816
817
Jens Axboe3650a3c2012-03-05 14:09:03 +0100818 free(io_p);
819 free(bw_p);
820 free(iops_p);
821}
822
Jens Axboee5bd1342012-03-05 21:38:12 +0100823static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
824 const char **labels)
825{
826 GtkWidget *tree_view;
827 GtkTreeSelection *selection;
828 GtkListStore *model;
829 GtkTreeIter iter;
830 GType *types;
831 int i, skipped;
832
833 /*
834 * Check if all are empty, in which case don't bother
835 */
836 for (i = 0, skipped = 0; i < num; i++)
837 if (lat[i] <= 0.0)
838 skipped++;
839
840 if (skipped == num)
841 return NULL;
842
843 types = malloc(num * sizeof(GType));
844
845 for (i = 0; i < num; i++)
846 types[i] = G_TYPE_STRING;
847
848 model = gtk_list_store_newv(num, types);
849 free(types);
850 types = NULL;
851
852 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
853 gtk_widget_set_can_focus(tree_view, FALSE);
854
Jens Axboe661f7412012-03-06 13:55:45 +0100855 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
856 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
857
Jens Axboee5bd1342012-03-05 21:38:12 +0100858 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
859 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
860
861 for (i = 0; i < num; i++)
862 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
863
864 gtk_list_store_append(model, &iter);
865
866 for (i = 0; i < num; i++) {
867 char fbuf[32];
868
869 if (lat[i] <= 0.0)
870 sprintf(fbuf, "0.00");
871 else
872 sprintf(fbuf, "%3.2f%%", lat[i]);
873
874 gtk_list_store_set(model, &iter, i, fbuf, -1);
875 }
876
877 return tree_view;
878}
879
880static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
881{
882 GtkWidget *box, *frame, *tree_view;
883 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
884 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
885 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
886 "250", "500", "750", "1000", };
887 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
888 "250", "500", "750", "1000", "2000",
889 ">= 2000", };
890
891 stat_calc_lat_u(ts, io_u_lat_u);
892 stat_calc_lat_m(ts, io_u_lat_m);
893
894 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
895 if (tree_view) {
896 frame = gtk_frame_new("Latency buckets (usec)");
897 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
898
899 box = gtk_hbox_new(FALSE, 3);
900 gtk_container_add(GTK_CONTAINER(frame), box);
901 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
902 }
903
904 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
905 if (tree_view) {
906 frame = gtk_frame_new("Latency buckets (msec)");
907 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
908
909 box = gtk_hbox_new(FALSE, 3);
910 gtk_container_add(GTK_CONTAINER(frame), box);
911 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
912 }
913}
914
Jens Axboe2e331012012-03-05 22:07:54 +0100915static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
916{
917 GtkWidget *box, *frame, *entry;
918 double usr_cpu, sys_cpu;
919 unsigned long runtime;
920 char tmp[32];
921
922 runtime = ts->total_run_time;
923 if (runtime) {
924 double runt = (double) runtime;
925
926 usr_cpu = (double) ts->usr_time * 100 / runt;
927 sys_cpu = (double) ts->sys_time * 100 / runt;
928 } else {
929 usr_cpu = 0;
930 sys_cpu = 0;
931 }
932
933 frame = gtk_frame_new("OS resources");
934 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
935
936 box = gtk_hbox_new(FALSE, 3);
937 gtk_container_add(GTK_CONTAINER(frame), box);
938
939 entry = new_info_entry_in_frame(box, "User CPU");
940 sprintf(tmp, "%3.2f%%", usr_cpu);
941 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
942 entry = new_info_entry_in_frame(box, "System CPU");
943 sprintf(tmp, "%3.2f%%", sys_cpu);
944 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
945 entry = new_info_entry_in_frame(box, "Context switches");
946 entry_set_int_value(entry, ts->ctx);
947 entry = new_info_entry_in_frame(box, "Major faults");
948 entry_set_int_value(entry, ts->majf);
949 entry = new_info_entry_in_frame(box, "Minor faults");
950 entry_set_int_value(entry, ts->minf);
951}
Jens Axboe19998db2012-03-06 09:17:59 +0100952static void gfio_add_sc_depths_tree(GtkListStore *model,
953 struct thread_stat *ts, unsigned int len,
954 int submit)
955{
956 double io_u_dist[FIO_IO_U_MAP_NR];
957 GtkTreeIter iter;
958 /* Bits 0, and 3-8 */
959 const int add_mask = 0x1f9;
960 int i, j;
961
962 if (submit)
963 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
964 else
965 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
966
967 gtk_list_store_append(model, &iter);
968
969 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
970
971 for (i = 1, j = 0; i < len; i++) {
972 char fbuf[32];
973
974 if (!(add_mask & (1UL << (i - 1))))
975 sprintf(fbuf, "0.0%%");
976 else {
977 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
978 j++;
979 }
980
981 gtk_list_store_set(model, &iter, i, fbuf, -1);
982 }
983
984}
985
986static void gfio_add_total_depths_tree(GtkListStore *model,
987 struct thread_stat *ts, unsigned int len)
988{
989 double io_u_dist[FIO_IO_U_MAP_NR];
990 GtkTreeIter iter;
991 /* Bits 1-6, and 8 */
992 const int add_mask = 0x17e;
993 int i, j;
994
995 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
996
997 gtk_list_store_append(model, &iter);
998
999 gtk_list_store_set(model, &iter, 0, "Total", -1);
1000
1001 for (i = 1, j = 0; i < len; i++) {
1002 char fbuf[32];
1003
1004 if (!(add_mask & (1UL << (i - 1))))
1005 sprintf(fbuf, "0.0%%");
1006 else {
1007 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1008 j++;
1009 }
1010
1011 gtk_list_store_set(model, &iter, i, fbuf, -1);
1012 }
1013
1014}
Jens Axboe2e331012012-03-05 22:07:54 +01001015
1016static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1017{
Jens Axboe2e331012012-03-05 22:07:54 +01001018 GtkWidget *frame, *box, *tree_view;
1019 GtkTreeSelection *selection;
1020 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +01001021 GType types[FIO_IO_U_MAP_NR + 1];
1022 int i;
Jens Axboe19998db2012-03-06 09:17:59 +01001023#define NR_LABELS 10
1024 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +01001025
1026 frame = gtk_frame_new("IO depths");
1027 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1028
1029 box = gtk_hbox_new(FALSE, 3);
1030 gtk_container_add(GTK_CONTAINER(frame), box);
1031
Jens Axboe19998db2012-03-06 09:17:59 +01001032 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001033 types[i] = G_TYPE_STRING;
1034
Jens Axboe19998db2012-03-06 09:17:59 +01001035 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +01001036
1037 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1038 gtk_widget_set_can_focus(tree_view, FALSE);
1039
Jens Axboe661f7412012-03-06 13:55:45 +01001040 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1041 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1042
Jens Axboe2e331012012-03-05 22:07:54 +01001043 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1044 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1045
Jens Axboe19998db2012-03-06 09:17:59 +01001046 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001047 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1048
Jens Axboe19998db2012-03-06 09:17:59 +01001049 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1050 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1051 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001052
1053 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1054}
1055
Jens Axboef9d40b42012-03-06 09:52:49 +01001056static gboolean results_window_delete(GtkWidget *w, gpointer data)
1057{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001058 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001059
1060 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001061 ge->results_window = NULL;
1062 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001063 return TRUE;
1064}
1065
Jens Axboe2f99deb2012-03-09 14:37:29 +01001066static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001067{
1068 GtkWidget *win, *notebook;
1069
Jens Axboe2f99deb2012-03-09 14:37:29 +01001070 if (ge->results_window)
1071 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001072
1073 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1074 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001075 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001076 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1077 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001078
1079 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001080 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1081 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboef9d40b42012-03-06 09:52:49 +01001082 gtk_container_add(GTK_CONTAINER(win), notebook);
1083
Jens Axboe2f99deb2012-03-09 14:37:29 +01001084 ge->results_window = win;
1085 ge->results_notebook = notebook;
1086 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001087}
1088
Jens Axboe3650a3c2012-03-05 14:09:03 +01001089static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1090 struct group_run_stats *rs)
1091{
Jens Axboeb01329d2012-03-07 20:31:28 +01001092 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
Jens Axboee0681f32012-03-06 12:14:42 +01001093 struct gfio_client *gc = client->client_data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001094
1095 gdk_threads_enter();
1096
Jens Axboe2f99deb2012-03-09 14:37:29 +01001097 res_win = get_results_window(gc->ge);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001098
Jens Axboeb01329d2012-03-07 20:31:28 +01001099 scroll = gtk_scrolled_window_new(NULL, NULL);
1100 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1101 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1102
Jens Axboe3650a3c2012-03-05 14:09:03 +01001103 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001104
Jens Axboeb01329d2012-03-07 20:31:28 +01001105 box = gtk_hbox_new(FALSE, 0);
1106 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001107
Jens Axboeb01329d2012-03-07 20:31:28 +01001108 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1109
1110 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +01001111
Jens Axboee0681f32012-03-06 12:14:42 +01001112 gc->results_widget = vbox;
1113
Jens Axboe3650a3c2012-03-05 14:09:03 +01001114 entry = new_info_entry_in_frame(box, "Name");
1115 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1116 if (strlen(ts->description)) {
1117 entry = new_info_entry_in_frame(box, "Description");
1118 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1119 }
1120 entry = new_info_entry_in_frame(box, "Group ID");
1121 entry_set_int_value(entry, ts->groupid);
1122 entry = new_info_entry_in_frame(box, "Jobs");
1123 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +01001124 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +01001125 entry_set_int_value(entry, ts->error);
1126 entry = new_info_entry_in_frame(box, "PID");
1127 entry_set_int_value(entry, ts->pid);
1128
1129 if (ts->io_bytes[DDIR_READ])
1130 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1131 if (ts->io_bytes[DDIR_WRITE])
1132 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1133
Jens Axboee5bd1342012-03-05 21:38:12 +01001134 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +01001135 gfio_show_cpu_usage(vbox, ts);
1136 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +01001137
Jens Axboe2f99deb2012-03-09 14:37:29 +01001138 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001139 gdk_threads_leave();
1140}
1141
Jens Axboe084d1c62012-03-03 20:28:07 +01001142static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001143{
Jens Axboe9b260bd2012-03-06 11:02:52 +01001144 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001145 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001146 GtkTreeIter iter;
1147 struct tm *tm;
1148 time_t sec;
1149 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001150
Jens Axboe9b260bd2012-03-06 11:02:52 +01001151 sec = p->log_sec;
1152 tm = localtime(&sec);
1153 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1154 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1155
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001156 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +01001157
Jens Axboe2f99deb2012-03-09 14:37:29 +01001158 gtk_list_store_append(ui->log_model, &iter);
1159 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1160 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1161 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1162 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001163
Jens Axboe6b79c802012-03-08 10:51:36 +01001164 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001165 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +01001166
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001167 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001168}
1169
1170static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1171{
Jens Axboee0681f32012-03-06 12:14:42 +01001172 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1173 struct gfio_client *gc = client->client_data;
1174 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +01001175 double util;
1176 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001177
Jens Axboe0050e5f2012-03-06 09:23:27 +01001178 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +01001179
Jens Axboe45dcb2e2012-03-07 16:16:50 +01001180 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +01001181 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +01001182
1183 if (!gc->disk_util_frame) {
1184 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1185 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1186 }
1187
1188 vbox = gtk_vbox_new(FALSE, 3);
1189 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1190
1191 frame = gtk_frame_new((char *) p->dus.name);
1192 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1193
1194 box = gtk_vbox_new(FALSE, 3);
1195 gtk_container_add(GTK_CONTAINER(frame), box);
1196
1197 frame = gtk_frame_new("Read");
1198 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1199 vbox = gtk_hbox_new(TRUE, 3);
1200 gtk_container_add(GTK_CONTAINER(frame), vbox);
1201 entry = new_info_entry_in_frame(vbox, "IOs");
1202 entry_set_int_value(entry, p->dus.ios[0]);
1203 entry = new_info_entry_in_frame(vbox, "Merges");
1204 entry_set_int_value(entry, p->dus.merges[0]);
1205 entry = new_info_entry_in_frame(vbox, "Sectors");
1206 entry_set_int_value(entry, p->dus.sectors[0]);
1207 entry = new_info_entry_in_frame(vbox, "Ticks");
1208 entry_set_int_value(entry, p->dus.ticks[0]);
1209
1210 frame = gtk_frame_new("Write");
1211 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1212 vbox = gtk_hbox_new(TRUE, 3);
1213 gtk_container_add(GTK_CONTAINER(frame), vbox);
1214 entry = new_info_entry_in_frame(vbox, "IOs");
1215 entry_set_int_value(entry, p->dus.ios[1]);
1216 entry = new_info_entry_in_frame(vbox, "Merges");
1217 entry_set_int_value(entry, p->dus.merges[1]);
1218 entry = new_info_entry_in_frame(vbox, "Sectors");
1219 entry_set_int_value(entry, p->dus.sectors[1]);
1220 entry = new_info_entry_in_frame(vbox, "Ticks");
1221 entry_set_int_value(entry, p->dus.ticks[1]);
1222
1223 frame = gtk_frame_new("Shared");
1224 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1225 vbox = gtk_hbox_new(TRUE, 3);
1226 gtk_container_add(GTK_CONTAINER(frame), vbox);
1227 entry = new_info_entry_in_frame(vbox, "IO ticks");
1228 entry_set_int_value(entry, p->dus.io_ticks);
1229 entry = new_info_entry_in_frame(vbox, "Time in queue");
1230 entry_set_int_value(entry, p->dus.time_in_queue);
1231
Jens Axboe604cfe32012-03-07 19:51:36 +01001232 util = 0.0;
1233 if (p->dus.msec)
1234 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1235 if (util > 100.0)
1236 util = 100.0;
1237
1238 sprintf(tmp, "%3.2f%%", util);
1239 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1240 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1241
Jens Axboee0681f32012-03-06 12:14:42 +01001242 gtk_widget_show_all(gc->results_widget);
1243out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001244 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001245}
1246
Jens Axboe3650a3c2012-03-05 14:09:03 +01001247extern int sum_stat_clients;
1248extern struct thread_stat client_ts;
1249extern struct group_run_stats client_gs;
1250
1251static int sum_stat_nr;
1252
Jens Axboe89e5fad2012-03-05 09:21:12 +01001253static void gfio_thread_status_op(struct fio_client *client,
1254 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001255{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001256 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1257
1258 gfio_display_ts(client, &p->ts, &p->rs);
1259
1260 if (sum_stat_clients == 1)
1261 return;
1262
1263 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1264 sum_group_stats(&client_gs, &p->rs);
1265
1266 client_ts.members++;
1267 client_ts.groupid = p->ts.groupid;
1268
1269 if (++sum_stat_nr == sum_stat_clients) {
1270 strcpy(client_ts.name, "All clients");
1271 gfio_display_ts(client, &client_ts, &client_gs);
1272 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001273}
1274
Jens Axboe89e5fad2012-03-05 09:21:12 +01001275static void gfio_group_stats_op(struct fio_client *client,
1276 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001277{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001278 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001279}
1280
Jens Axboe2f99deb2012-03-09 14:37:29 +01001281static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1282 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001283{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001284 struct gfio_graphs *g = data;
1285
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001286 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1287 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1288 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1289 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001290 return TRUE;
1291}
1292
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001293static void draw_graph(struct graph *g, cairo_t *cr)
1294{
1295 line_graph_draw(g, cr);
1296 cairo_stroke(cr);
1297}
1298
Jens Axboe93e2db22012-03-13 09:45:22 +01001299static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1300 gboolean keyboard_mode, GtkTooltip *tooltip,
1301 gpointer data)
1302{
1303 struct gfio_graphs *g = data;
1304 const char *text = NULL;
1305
1306 if (graph_contains_xy(g->iops_graph, x, y))
1307 text = graph_find_tooltip(g->iops_graph, x, y);
1308 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1309 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1310
1311 if (text) {
1312 gtk_tooltip_set_text(tooltip, text);
1313 return TRUE;
1314 }
1315
1316 return FALSE;
1317}
1318
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001319static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1320{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001321 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001322 cairo_t *cr;
1323
1324 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001325
1326 if (graph_has_tooltips(g->iops_graph) ||
1327 graph_has_tooltips(g->bandwidth_graph)) {
1328 g_object_set(w, "has-tooltip", TRUE, NULL);
1329 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1330 }
1331
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001332 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001333 draw_graph(g->iops_graph, cr);
1334 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001335 cairo_destroy(cr);
1336
1337 return FALSE;
1338}
1339
Jens Axboe2f99deb2012-03-09 14:37:29 +01001340/*
1341 * Client specific ETA
1342 */
1343static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001344{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001345 struct gfio_client *gc = client->client_data;
1346 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001347 static int eta_good;
1348 char eta_str[128];
1349 char output[256];
1350 char tmp[32];
1351 double perc = 0.0;
1352 int i2p = 0;
1353
Jens Axboe0050e5f2012-03-06 09:23:27 +01001354 gdk_threads_enter();
1355
Jens Axboe3e47bd22012-02-29 13:45:02 +01001356 eta_str[0] = '\0';
1357 output[0] = '\0';
1358
1359 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1360 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1361 eta_to_str(eta_str, je->eta_sec);
1362 }
1363
1364 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001365 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001366 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001367 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001368
1369#if 0
1370 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1371 if (je->m_rate || je->t_rate) {
1372 char *tr, *mr;
1373
1374 mr = num2str(je->m_rate, 4, 0, i2p);
1375 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001376 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001377 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1378 free(tr);
1379 free(mr);
1380 } else if (je->m_iops || je->t_iops)
1381 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001382
Jens Axboe2f99deb2012-03-09 14:37:29 +01001383 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1384 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1385 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1386 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001387#endif
1388
1389 if (je->eta_sec != INT_MAX && je->nr_running) {
1390 char *iops_str[2];
1391 char *rate_str[2];
1392
1393 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1394 strcpy(output, "-.-% done");
1395 else {
1396 eta_good = 1;
1397 perc *= 100.0;
1398 sprintf(output, "%3.1f%% done", perc);
1399 }
1400
1401 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1402 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1403
1404 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1405 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1406
Jens Axboe2f99deb2012-03-09 14:37:29 +01001407 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1408 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1409 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1410 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001411
Jens Axboe93e2db22012-03-13 09:45:22 +01001412 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1413 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1414 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1415 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 +01001416
1417 free(rate_str[0]);
1418 free(rate_str[1]);
1419 free(iops_str[0]);
1420 free(iops_str[1]);
1421 }
1422
1423 if (eta_str[0]) {
1424 char *dst = output + strlen(output);
1425
1426 sprintf(dst, " - %s", eta_str);
1427 }
1428
Jens Axboe9988ca72012-03-09 15:14:06 +01001429 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001430 gdk_threads_leave();
1431}
1432
1433/*
1434 * Update ETA in main window for all clients
1435 */
1436static void gfio_update_all_eta(struct jobs_eta *je)
1437{
1438 struct gui *ui = &main_ui;
1439 static int eta_good;
1440 char eta_str[128];
1441 char output[256];
1442 double perc = 0.0;
1443 int i2p = 0;
1444
1445 gdk_threads_enter();
1446
1447 eta_str[0] = '\0';
1448 output[0] = '\0';
1449
1450 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1451 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1452 eta_to_str(eta_str, je->eta_sec);
1453 }
1454
1455#if 0
1456 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1457 if (je->m_rate || je->t_rate) {
1458 char *tr, *mr;
1459
1460 mr = num2str(je->m_rate, 4, 0, i2p);
1461 tr = num2str(je->t_rate, 4, 0, i2p);
1462 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1463 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1464 free(tr);
1465 free(mr);
1466 } else if (je->m_iops || je->t_iops)
1467 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1468
1469 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1470 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1471 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1472 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1473#endif
1474
Jens Axboe3863d1a2012-03-09 17:39:05 +01001475 entry_set_int_value(ui->eta.jobs, je->nr_running);
1476
Jens Axboe2f99deb2012-03-09 14:37:29 +01001477 if (je->eta_sec != INT_MAX && je->nr_running) {
1478 char *iops_str[2];
1479 char *rate_str[2];
1480
1481 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1482 strcpy(output, "-.-% done");
1483 else {
1484 eta_good = 1;
1485 perc *= 100.0;
1486 sprintf(output, "%3.1f%% done", perc);
1487 }
1488
1489 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1490 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1491
1492 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1493 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1494
1495 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1496 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1497 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1498 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1499
Jens Axboe93e2db22012-03-13 09:45:22 +01001500 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1501 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1502 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1503 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 +01001504
Jens Axboe3e47bd22012-02-29 13:45:02 +01001505 free(rate_str[0]);
1506 free(rate_str[1]);
1507 free(iops_str[0]);
1508 free(iops_str[1]);
1509 }
1510
1511 if (eta_str[0]) {
1512 char *dst = output + strlen(output);
1513
1514 sprintf(dst, " - %s", eta_str);
1515 }
1516
Jens Axboe9988ca72012-03-09 15:14:06 +01001517 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001518 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001519}
1520
Stephen M. Camerona1820202012-02-24 08:17:31 +01001521static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1522{
Jens Axboe843ad232012-02-29 11:44:53 +01001523 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001524 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001525 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001526 const char *os, *arch;
1527 char buf[64];
1528
1529 os = fio_get_os_string(probe->os);
1530 if (!os)
1531 os = "unknown";
1532
1533 arch = fio_get_arch_string(probe->arch);
1534 if (!arch)
1535 os = "unknown";
1536
1537 if (!client->name)
1538 client->name = strdup((char *) probe->hostname);
1539
Jens Axboe0050e5f2012-03-06 09:23:27 +01001540 gdk_threads_enter();
1541
Jens Axboe2f99deb2012-03-09 14:37:29 +01001542 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1543 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1544 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001545 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001546 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001547
Jens Axboe85dd01e2012-03-12 14:33:16 +01001548 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001549
1550 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001551}
1552
Jens Axboe9988ca72012-03-09 15:14:06 +01001553static void gfio_update_thread_status(struct gui_entry *ge,
1554 char *status_message, double perc)
1555{
1556 static char message[100];
1557 const char *m = message;
1558
1559 strncpy(message, status_message, sizeof(message) - 1);
1560 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1561 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1562 gtk_widget_queue_draw(main_ui.window);
1563}
1564
1565static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001566{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001567 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001568 static char message[100];
1569 const char *m = message;
1570
1571 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001572 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1573 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1574 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001575}
1576
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001577static void gfio_quit_op(struct fio_client *client)
1578{
Jens Axboee0681f32012-03-06 12:14:42 +01001579 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001580
Jens Axboe0050e5f2012-03-06 09:23:27 +01001581 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001582 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001583 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001584}
1585
Jens Axboe807f9972012-03-02 10:25:24 +01001586static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1587{
1588 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001589 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001590 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001591 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001592 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001593
Jens Axboedcaeb602012-03-08 19:45:37 +01001594 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001595
Jens Axboe0050e5f2012-03-06 09:23:27 +01001596 gdk_threads_enter();
1597
Jens Axboe2f99deb2012-03-09 14:37:29 +01001598 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1599
Jens Axboe3863d1a2012-03-09 17:39:05 +01001600 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1601 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1602
Jens Axboec80b74b2012-03-12 10:23:28 +01001603 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1604 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001605
Jens Axboedcaeb602012-03-08 19:45:37 +01001606 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001607 multitext_add_entry(&ge->eta.iodepth, tmp);
1608
1609 multitext_set_entry(&ge->eta.iotype, 0);
1610 multitext_set_entry(&ge->eta.ioengine, 0);
1611 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001612
Jens Axboedcaeb602012-03-08 19:45:37 +01001613 gc->job_added++;
1614
Jens Axboe85dd01e2012-03-12 14:33:16 +01001615 gfio_set_state(ge, GE_STATE_JOB_SENT);
1616
Jens Axboe0050e5f2012-03-06 09:23:27 +01001617 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001618}
1619
Jens Axboeed727a42012-03-02 12:14:40 +01001620static void gfio_client_timed_out(struct fio_client *client)
1621{
Jens Axboee0681f32012-03-06 12:14:42 +01001622 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001623 char buf[256];
1624
1625 gdk_threads_enter();
1626
Jens Axboe85dd01e2012-03-12 14:33:16 +01001627 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001628 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001629
1630 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001631 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001632
1633 gdk_threads_leave();
1634}
1635
Jens Axboe6b79c802012-03-08 10:51:36 +01001636static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1637{
1638 struct gfio_client *gc = client->client_data;
1639
1640 gdk_threads_enter();
1641
Jens Axboe85dd01e2012-03-12 14:33:16 +01001642 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001643
1644 if (gc->err_entry)
1645 entry_set_int_value(gc->err_entry, client->error);
1646
1647 gdk_threads_leave();
1648}
1649
Jens Axboe85dd01e2012-03-12 14:33:16 +01001650static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1651{
1652 struct gfio_client *gc = client->client_data;
1653
1654 gdk_threads_enter();
1655 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1656 gdk_threads_leave();
1657}
1658
1659static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1660{
1661 struct gfio_client *gc = client->client_data;
1662
1663 gdk_threads_enter();
1664 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1665 gdk_threads_leave();
1666}
1667
Stephen M. Camerona1820202012-02-24 08:17:31 +01001668struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001669 .text_op = gfio_text_op,
1670 .disk_util = gfio_disk_util_op,
1671 .thread_status = gfio_thread_status_op,
1672 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001673 .jobs_eta = gfio_update_client_eta,
1674 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001675 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001676 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001677 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001678 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001679 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001680 .start = gfio_client_start,
1681 .job_start = gfio_client_job_start,
Jens Axboe6433ee02012-03-09 20:10:51 +01001682 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001683 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001684};
1685
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001686static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1687 __attribute__((unused)) gpointer data)
1688{
1689 gtk_main_quit();
1690}
1691
Stephen M. Cameron25927252012-02-24 08:17:31 +01001692static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001693{
Jens Axboea9eccde2012-03-09 14:59:42 +01001694 struct gui *ui = arg;
1695
1696 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001697 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001698 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001699 return NULL;
1700}
1701
Jens Axboe2f99deb2012-03-09 14:37:29 +01001702static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001703{
Jens Axboe9988ca72012-03-09 15:14:06 +01001704 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001705 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001706
Jens Axboe2f99deb2012-03-09 14:37:29 +01001707 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001708 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001709 if (ret < 0) {
1710 GError *error;
1711
1712 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1713 report_error(error);
1714 g_error_free(error);
1715 break;
1716 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001717 break;
1718
Jens Axboe2f99deb2012-03-09 14:37:29 +01001719 free(ge->job_files[i]);
1720 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001721 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001722 while (i < ge->nr_job_files) {
1723 free(ge->job_files[i]);
1724 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001725 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001726 }
1727
Jens Axboe3af45202012-03-13 09:59:53 +01001728 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01001729 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001730}
1731
Jens Axboe63a130b2012-03-06 20:08:59 +01001732static void *server_thread(void *arg)
1733{
1734 is_backend = 1;
1735 gfio_server_running = 1;
1736 fio_start_server(NULL);
1737 gfio_server_running = 0;
1738 return NULL;
1739}
1740
Jens Axboe2f99deb2012-03-09 14:37:29 +01001741static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001742{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001743 struct gui *ui = &main_ui;
1744
Jens Axboe63a130b2012-03-06 20:08:59 +01001745 if (!gfio_server_running) {
1746 gfio_server_running = 1;
1747 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001748 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001749 }
1750}
1751
Stephen M. Cameron25927252012-02-24 08:17:31 +01001752static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1753 gpointer data)
1754{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001755 struct gui_entry *ge = data;
1756 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001757
Jens Axboe78cb2fe2012-03-12 23:05:29 +01001758 if (gc)
1759 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001760}
1761
Jens Axboedf06f222012-03-02 13:32:04 +01001762static void file_open(GtkWidget *w, gpointer data);
1763
1764static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001765{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001766 struct gui_entry *ge = data;
1767 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001768
Jens Axboe85dd01e2012-03-12 14:33:16 +01001769 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01001770 int ret;
1771
Jens Axboe2f99deb2012-03-09 14:37:29 +01001772 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01001773 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001774 if (!ge->nr_job_files)
1775 return;
1776
1777 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1778 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001779 ret = fio_client_connect(gc->client);
1780 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001781 if (!ge->ui->handler_running)
1782 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001783 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01001784 } else {
1785 GError *error;
1786
1787 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1788 report_error(error);
1789 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001790 }
Jens Axboedf06f222012-03-02 13:32:04 +01001791 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001792 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001793 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001794 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001795 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001796}
1797
Jens Axboeb9d2f302012-03-08 20:36:28 +01001798static void send_clicked(GtkWidget *widget, gpointer data)
1799{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001800 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001801
Jens Axboe2f99deb2012-03-09 14:37:29 +01001802 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001803 GError *error;
1804
1805 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);
1806 report_error(error);
1807 g_error_free(error);
1808
Jens Axboe2f99deb2012-03-09 14:37:29 +01001809 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001810 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01001811}
1812
Jens Axboe2f99deb2012-03-09 14:37:29 +01001813static GtkWidget *add_button(GtkWidget *buttonbox,
1814 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001815{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001816 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1817
1818 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1819 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1820 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1821 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1822
1823 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001824}
1825
Jens Axboe2f99deb2012-03-09 14:37:29 +01001826static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1827 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001828{
1829 int i;
1830
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001831 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001832 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001833}
1834
Jens Axboe0420ba62012-02-29 11:16:52 +01001835static void on_info_bar_response(GtkWidget *widget, gint response,
1836 gpointer data)
1837{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001838 struct gui *ui = &main_ui;
1839
Jens Axboe0420ba62012-02-29 11:16:52 +01001840 if (response == GTK_RESPONSE_OK) {
1841 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001842 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001843 }
1844}
1845
Jens Axboedf06f222012-03-02 13:32:04 +01001846void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001847{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001848 struct gui *ui = &main_ui;
1849
1850 if (ui->error_info_bar == NULL) {
1851 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001852 GTK_RESPONSE_OK,
1853 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001854 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1855 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001856 GTK_MESSAGE_ERROR);
1857
Jens Axboe2f99deb2012-03-09 14:37:29 +01001858 ui->error_label = gtk_label_new(error->message);
1859 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1860 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001861
Jens Axboe2f99deb2012-03-09 14:37:29 +01001862 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1863 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001864 } else {
1865 char buffer[256];
1866 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001867 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001868 }
1869}
1870
Jens Axboe62bc9372012-03-07 11:45:07 +01001871struct connection_widgets
1872{
1873 GtkWidget *hentry;
1874 GtkWidget *combo;
1875 GtkWidget *button;
1876};
1877
1878static void hostname_cb(GtkEntry *entry, gpointer data)
1879{
1880 struct connection_widgets *cw = data;
1881 int uses_net = 0, is_localhost = 0;
1882 const gchar *text;
1883 gchar *ctext;
1884
1885 /*
1886 * Check whether to display the 'auto start backend' box
1887 * or not. Show it if we are a localhost and using network,
1888 * or using a socket.
1889 */
1890 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1891 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1892 uses_net = 1;
1893 g_free(ctext);
1894
1895 if (uses_net) {
1896 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1897 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1898 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1899 !strcmp(text, "ip6-loopback"))
1900 is_localhost = 1;
1901 }
1902
1903 if (!uses_net || is_localhost) {
1904 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1905 gtk_widget_set_sensitive(cw->button, 1);
1906 } else {
1907 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1908 gtk_widget_set_sensitive(cw->button, 0);
1909 }
1910}
1911
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001912static int get_connection_details(char **host, int *port, int *type,
1913 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001914{
Jens Axboe62bc9372012-03-07 11:45:07 +01001915 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1916 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001917 char *typeentry;
1918
1919 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001920 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001921 GTK_DIALOG_DESTROY_WITH_PARENT,
1922 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1923 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1924
1925 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001926 /* gtk_dialog_get_content_area() is 2.14 and newer */
1927 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001928 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1929
1930 box = gtk_vbox_new(FALSE, 6);
1931 gtk_container_add(GTK_CONTAINER(frame), box);
1932
1933 hbox = gtk_hbox_new(TRUE, 10);
1934 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01001935 cw.hentry = gtk_entry_new();
1936 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1937 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001938
1939 frame = gtk_frame_new("Port");
1940 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1941 box = gtk_vbox_new(FALSE, 10);
1942 gtk_container_add(GTK_CONTAINER(frame), box);
1943
1944 hbox = gtk_hbox_new(TRUE, 4);
1945 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1946 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1947
1948 frame = gtk_frame_new("Type");
1949 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1950 box = gtk_vbox_new(FALSE, 10);
1951 gtk_container_add(GTK_CONTAINER(frame), box);
1952
1953 hbox = gtk_hbox_new(TRUE, 4);
1954 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1955
Jens Axboe62bc9372012-03-07 11:45:07 +01001956 cw.combo = gtk_combo_box_new_text();
1957 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1958 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1959 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1960 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001961
Jens Axboe62bc9372012-03-07 11:45:07 +01001962 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001963
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001964 frame = gtk_frame_new("Options");
1965 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1966 box = gtk_vbox_new(FALSE, 10);
1967 gtk_container_add(GTK_CONTAINER(frame), box);
1968
1969 hbox = gtk_hbox_new(TRUE, 4);
1970 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1971
Jens Axboe62bc9372012-03-07 11:45:07 +01001972 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1973 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1974 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.");
1975 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1976
1977 /*
1978 * Connect edit signal, so we can show/not-show the auto start button
1979 */
1980 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1981 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001982
Jens Axboea7a42ce2012-03-02 13:12:04 +01001983 gtk_widget_show_all(dialog);
1984
1985 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1986 gtk_widget_destroy(dialog);
1987 return 1;
1988 }
1989
Jens Axboe62bc9372012-03-07 11:45:07 +01001990 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001991 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1992
Jens Axboe62bc9372012-03-07 11:45:07 +01001993 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001994 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1995 *type = Fio_client_ipv4;
1996 else if (!strncmp(typeentry, "IPv6", 4))
1997 *type = Fio_client_ipv6;
1998 else
1999 *type = Fio_client_socket;
2000 g_free(typeentry);
2001
Jens Axboe62bc9372012-03-07 11:45:07 +01002002 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002003
Jens Axboea7a42ce2012-03-02 13:12:04 +01002004 gtk_widget_destroy(dialog);
2005 return 0;
2006}
2007
Jens Axboe2f99deb2012-03-09 14:37:29 +01002008static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002009{
2010 struct gfio_client *gc;
2011
2012 gc = malloc(sizeof(*gc));
2013 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002014 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002015 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002016
Jens Axboe2f99deb2012-03-09 14:37:29 +01002017 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002018
2019 client->client_data = gc;
2020}
2021
Jens Axboe2f99deb2012-03-09 14:37:29 +01002022static GtkWidget *new_client_page(struct gui_entry *ge);
2023
2024static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2025{
2026 struct gui_entry *ge;
2027
2028 ge = malloc(sizeof(*ge));
2029 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002030 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002031 INIT_FLIST_HEAD(&ge->list);
2032 flist_add_tail(&ge->list, &ui->list);
2033 ge->ui = ui;
2034 return ge;
2035}
2036
2037/*
2038 * FIXME: need more handling here
2039 */
2040static void ge_destroy(GtkWidget *w, gpointer data)
2041{
2042 struct gui_entry *ge = data;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002043 struct gfio_client *gc = ge->client;
2044
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002045 if (gc && gc->client) {
Jens Axboe85dd01e2012-03-12 14:33:16 +01002046 if (ge->state >= GE_STATE_CONNECTED)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002047 fio_client_terminate(gc->client);
2048
Jens Axboe343cb4a2012-03-09 17:16:51 +01002049 fio_put_client(gc->client);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002050 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002051
2052 flist_del(&ge->list);
2053 free(ge);
2054}
2055
2056static struct gui_entry *get_new_ge_with_tab(const char *name)
2057{
2058 struct gui_entry *ge;
2059
2060 ge = alloc_new_gui_entry(&main_ui);
2061
2062 ge->vbox = new_client_page(ge);
2063 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
2064
2065 ge->page_label = gtk_label_new(name);
2066 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2067
2068 gtk_widget_show_all(main_ui.window);
2069 return ge;
2070}
2071
2072static void file_new(GtkWidget *w, gpointer data)
2073{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002074 struct gui *ui = (struct gui *) data;
2075 struct gui_entry *ge;
2076
2077 ge = get_new_ge_with_tab("Untitled");
2078 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002079}
2080
2081/*
2082 * Return the 'ge' corresponding to the tab. If the active tab is the
2083 * main tab, open a new tab.
2084 */
Jens Axboecf4b0442012-03-12 15:09:42 +01002085static struct gui_entry *get_ge_from_page(gint cur_page)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002086{
2087 struct flist_head *entry;
2088 struct gui_entry *ge;
2089
2090 if (!cur_page)
2091 return get_new_ge_with_tab("Untitled");
2092
2093 flist_for_each(entry, &main_ui.list) {
2094 ge = flist_entry(entry, struct gui_entry, list);
2095 if (ge->page_num == cur_page)
2096 return ge;
2097 }
2098
2099 return NULL;
2100}
2101
Jens Axboe85dd01e2012-03-12 14:33:16 +01002102static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2103{
2104 gint cur_page;
2105
2106 /*
2107 * Main tab is tab 0, so any current page other than 0 holds
2108 * a ge entry.
2109 */
2110 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2111 if (cur_page)
2112 return get_ge_from_page(cur_page);
2113
2114 return NULL;
2115}
2116
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002117static void file_close(GtkWidget *w, gpointer data)
2118{
2119 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002120 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002121
2122 /*
2123 * Can't close the main tab
2124 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002125 ge = get_ge_from_cur_tab(ui);
2126 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002127 gtk_widget_destroy(ge->vbox);
2128 return;
2129 }
2130
Jens Axboef5c67262012-03-13 08:20:41 +01002131 if (!flist_empty(&ui->list)) {
2132 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2133 return;
2134 }
2135
2136 gtk_main_quit();
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002137}
2138
Jens Axboe0420ba62012-02-29 11:16:52 +01002139static void file_open(GtkWidget *w, gpointer data)
2140{
Jens Axboe63a130b2012-03-06 20:08:59 +01002141 struct gui *ui = data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002142 GtkWidget *dialog;
Jens Axboe0420ba62012-02-29 11:16:52 +01002143 GSList *filenames, *fn_glist;
2144 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002145 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002146 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002147 struct gui_entry *ge;
2148 gint cur_page;
2149
2150 /*
2151 * Creates new tab if current tab is the main window, or the
2152 * current tab already has a client.
2153 */
2154 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2155 ge = get_ge_from_page(cur_page);
2156 if (ge->client)
2157 ge = get_new_ge_with_tab("Untitled");
2158
2159 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002160
2161 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002162 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002163 GTK_FILE_CHOOSER_ACTION_OPEN,
2164 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2165 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2166 NULL);
2167 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2168
2169 filter = gtk_file_filter_new();
2170 gtk_file_filter_add_pattern(filter, "*.fio");
2171 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002172 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe0420ba62012-02-29 11:16:52 +01002173 gtk_file_filter_add_mime_type(filter, "text/fio");
2174 gtk_file_filter_set_name(filter, "Fio job file");
2175 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2176
2177 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2178 gtk_widget_destroy(dialog);
2179 return;
2180 }
2181
2182 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002183
2184 gtk_widget_destroy(dialog);
2185
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002186 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01002187 goto err;
2188
Jens Axboe0420ba62012-02-29 11:16:52 +01002189 filenames = fn_glist;
2190 while (filenames != NULL) {
Jens Axboee0681f32012-03-06 12:14:42 +01002191 struct fio_client *client;
2192
Jens Axboe2f99deb2012-03-09 14:37:29 +01002193 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2194 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2195 ge->nr_job_files++;
Jens Axboe0420ba62012-02-29 11:16:52 +01002196
Jens Axboee0681f32012-03-06 12:14:42 +01002197 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2198 if (!client) {
Jens Axboedf06f222012-03-02 13:32:04 +01002199 GError *error;
2200
2201 error = g_error_new(g_quark_from_string("fio"), 1,
2202 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01002203 report_error(error);
2204 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01002205 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002206 gfio_client_added(ge, client);
Jens Axboe0420ba62012-02-29 11:16:52 +01002207
2208 g_free(filenames->data);
2209 filenames = g_slist_next(filenames);
2210 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01002211 free(host);
Jens Axboe63a130b2012-03-06 20:08:59 +01002212
2213 if (server_start)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002214 gfio_start_server();
Jens Axboea7a42ce2012-03-02 13:12:04 +01002215err:
Jens Axboe0420ba62012-02-29 11:16:52 +01002216 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002217}
2218
2219static void file_save(GtkWidget *w, gpointer data)
2220{
Jens Axboe63a130b2012-03-06 20:08:59 +01002221 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002222 GtkWidget *dialog;
2223
2224 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002225 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002226 GTK_FILE_CHOOSER_ACTION_SAVE,
2227 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2228 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2229 NULL);
2230
2231 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2232 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2233
2234 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2235 char *filename;
2236
2237 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2238 // save_job_file(filename);
2239 g_free(filename);
2240 }
2241 gtk_widget_destroy(dialog);
2242}
2243
Jens Axboe9b260bd2012-03-06 11:02:52 +01002244static void view_log_destroy(GtkWidget *w, gpointer data)
2245{
2246 struct gui *ui = (struct gui *) data;
2247
2248 gtk_widget_ref(ui->log_tree);
2249 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2250 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002251 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002252}
2253
2254static void view_log(GtkWidget *w, gpointer data)
2255{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002256 GtkWidget *win, *scroll, *vbox, *box;
2257 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002258
Jens Axboe4cbe7212012-03-06 13:36:17 +01002259 if (ui->log_view)
2260 return;
2261
2262 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002263 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002264 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002265
Jens Axboe4cbe7212012-03-06 13:36:17 +01002266 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002267
Jens Axboe4cbe7212012-03-06 13:36:17 +01002268 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2269
2270 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2271
2272 box = gtk_hbox_new(TRUE, 0);
2273 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2274 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2275 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2276
2277 vbox = gtk_vbox_new(TRUE, 5);
2278 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2279
2280 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002281 gtk_widget_show_all(win);
2282}
2283
Jens Axboe85dd01e2012-03-12 14:33:16 +01002284static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002285{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002286 struct gui *ui = (struct gui *) data;
2287 struct gui_entry *ge;
2288
2289 ge = get_ge_from_cur_tab(ui);
2290 if (ge)
2291 connect_clicked(w, ge);
2292}
2293
2294static void send_job_entry(GtkWidget *w, gpointer data)
2295{
2296 struct gui *ui = (struct gui *) data;
2297 struct gui_entry *ge;
2298
2299 ge = get_ge_from_cur_tab(ui);
2300 if (ge)
2301 send_clicked(w, ge);
2302
2303}
2304
2305static void edit_job_entry(GtkWidget *w, gpointer data)
2306{
2307}
2308
2309static void start_job_entry(GtkWidget *w, gpointer data)
2310{
2311 struct gui *ui = (struct gui *) data;
2312 struct gui_entry *ge;
2313
2314 ge = get_ge_from_cur_tab(ui);
2315 if (ge)
2316 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002317}
2318
Jens Axboe8577f4f2012-03-09 19:28:27 +01002319static void __update_graph_limits(struct gfio_graphs *g)
2320{
2321 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2322 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2323}
2324
2325static void update_graph_limits(void)
2326{
2327 struct flist_head *entry;
2328 struct gui_entry *ge;
2329
2330 __update_graph_limits(&main_ui.graphs);
2331
2332 flist_for_each(entry, &main_ui.list) {
2333 ge = flist_entry(entry, struct gui_entry, list);
2334 __update_graph_limits(&ge->graphs);
2335 }
2336}
2337
Jens Axboe46974a72012-03-02 19:34:13 +01002338static void preferences(GtkWidget *w, gpointer data)
2339{
Jens Axboef3e84402012-03-07 13:14:32 +01002340 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002341 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002342 int i;
2343
2344 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002345 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002346 GTK_DIALOG_DESTROY_WITH_PARENT,
2347 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2348 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2349 NULL);
2350
Jens Axboe8577f4f2012-03-09 19:28:27 +01002351 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002352 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2353 vbox = gtk_vbox_new(FALSE, 6);
2354 gtk_container_add(GTK_CONTAINER(frame), vbox);
2355
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002356 hbox = gtk_hbox_new(FALSE, 5);
2357 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2358 entry = gtk_label_new("Font face to use for graph labels");
2359 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2360
Jens Axboef3e84402012-03-07 13:14:32 +01002361 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002362 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002363
Jens Axboe8577f4f2012-03-09 19:28:27 +01002364 box = gtk_vbox_new(FALSE, 6);
2365 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2366
2367 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002368 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002369 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2370 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2371
Jens Axboec05d9052012-03-11 13:05:35 +01002372 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002373
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002374 box = gtk_vbox_new(FALSE, 6);
2375 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2376
2377 hbox = gtk_hbox_new(FALSE, 5);
2378 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2379 entry = gtk_label_new("Client ETA request interval (msec)");
2380 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2381
2382 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002383 frame = gtk_frame_new("Debug logging");
2384 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2385 vbox = gtk_vbox_new(FALSE, 6);
2386 gtk_container_add(GTK_CONTAINER(frame), vbox);
2387
2388 box = gtk_hbox_new(FALSE, 6);
2389 gtk_container_add(GTK_CONTAINER(vbox), box);
2390
2391 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2392
2393 for (i = 0; i < FD_DEBUG_MAX; i++) {
2394 if (i == 7) {
2395 box = gtk_hbox_new(FALSE, 6);
2396 gtk_container_add(GTK_CONTAINER(vbox), box);
2397 }
2398
2399
2400 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2401 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2402 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2403 }
2404
Jens Axboe46974a72012-03-02 19:34:13 +01002405 gtk_widget_show_all(dialog);
2406
2407 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2408 gtk_widget_destroy(dialog);
2409 return;
2410 }
2411
2412 for (i = 0; i < FD_DEBUG_MAX; i++) {
2413 int set;
2414
2415 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2416 if (set)
2417 fio_debug |= (1UL << i);
2418 }
2419
Jens Axboef3e84402012-03-07 13:14:32 +01002420 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002421 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2422 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002423 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002424
Jens Axboe46974a72012-03-02 19:34:13 +01002425 gtk_widget_destroy(dialog);
2426}
2427
Jens Axboe0420ba62012-02-29 11:16:52 +01002428static void about_dialog(GtkWidget *w, gpointer data)
2429{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002430 const char *authors[] = {
2431 "Jens Axboe <axboe@kernel.dk>",
2432 "Stephen Carmeron <stephenmcameron@gmail.com>",
2433 NULL
2434 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002435 const char *license[] = {
2436 "Fio is free software; you can redistribute it and/or modify "
2437 "it under the terms of the GNU General Public License as published by "
2438 "the Free Software Foundation; either version 2 of the License, or "
2439 "(at your option) any later version.\n",
2440 "Fio is distributed in the hope that it will be useful, "
2441 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2442 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2443 "GNU General Public License for more details.\n",
2444 "You should have received a copy of the GNU General Public License "
2445 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2446 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2447 };
2448 char *license_trans;
2449
2450 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2451 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002452
Jens Axboe0420ba62012-02-29 11:16:52 +01002453 gtk_show_about_dialog(NULL,
2454 "program-name", "gfio",
2455 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002456 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002457 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2458 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002459 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002460 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002461 "logo-icon-name", "fio",
2462 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002463 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002464 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002465
Jens Axboe2f99deb2012-03-09 14:37:29 +01002466 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002467}
2468
2469static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002470 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002471 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002472 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002473 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002474 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002475 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002476 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2477 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2478 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002479 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002480 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2481 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2482 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2483 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002484 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2485 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002486};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002487static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002488
2489static const gchar *ui_string = " \
2490 <ui> \
2491 <menubar name=\"MainMenu\"> \
2492 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002493 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002494 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002495 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002496 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2497 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002498 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002499 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2500 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002501 <placeholder name=\"FileRecentFiles\"/> \
2502 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002503 <menuitem name=\"Quit\" action=\"Quit\" /> \
2504 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002505 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002506 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002507 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002508 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2509 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002510 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002511 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002512 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002513 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2514 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2515 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002516 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2517 <menuitem name=\"About\" action=\"About\" /> \
2518 </menu> \
2519 </menubar> \
2520 </ui> \
2521";
2522
Jens Axboe02421e62012-03-12 12:05:50 +01002523static void set_job_menu_visible(struct gui *ui, int visible)
2524{
2525 GtkWidget *job;
2526
2527 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2528 gtk_widget_set_sensitive(job, visible);
2529}
2530
Jens Axboe4cbe7212012-03-06 13:36:17 +01002531static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2532 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002533{
2534 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2535 GError *error = 0;
2536
2537 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002538 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002539
2540 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2541 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2542
2543 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002544
Jens Axboe0420ba62012-02-29 11:16:52 +01002545 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2546}
2547
2548void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2549 GtkWidget *vbox, GtkUIManager *ui_manager)
2550{
2551 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2552}
2553
Jens Axboec80b74b2012-03-12 10:23:28 +01002554static void combo_entry_changed(GtkComboBox *box, gpointer data)
2555{
2556 struct gui_entry *ge = (struct gui_entry *) data;
2557 gint index;
2558
2559 index = gtk_combo_box_get_active(box);
2560
2561 multitext_set_entry(&ge->eta.iotype, index);
2562 multitext_set_entry(&ge->eta.ioengine, index);
2563 multitext_set_entry(&ge->eta.iodepth, index);
2564}
2565
2566static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2567{
2568 struct gui_entry *ge = (struct gui_entry *) data;
2569
2570 multitext_free(&ge->eta.iotype);
2571 multitext_free(&ge->eta.ioengine);
2572 multitext_free(&ge->eta.iodepth);
2573}
2574
Jens Axboe2f99deb2012-03-09 14:37:29 +01002575static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002576{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002577 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002578 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002579 GdkColor white;
Jens Axboe0420ba62012-02-29 11:16:52 +01002580
Jens Axboe2f99deb2012-03-09 14:37:29 +01002581 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002582
Jens Axboe65476332012-03-13 10:37:04 +01002583 top_align = gtk_alignment_new(0, 0, 1, 0);
2584 top_vbox = gtk_vbox_new(FALSE, 3);
2585 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2586 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002587
Jens Axboe3e47bd22012-02-29 13:45:02 +01002588 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002589 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002590 probe_frame = gtk_vbox_new(FALSE, 3);
2591 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2592
2593 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002594 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2595 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2596 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2597 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2598 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002599
Jens Axboe3e47bd22012-02-29 13:45:02 +01002600 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002601 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2602
Jens Axboe3863d1a2012-03-09 17:39:05 +01002603 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002604 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2605 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2606 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2607 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2608 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002609 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2610 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2611
2612 probe_box = gtk_hbox_new(FALSE, 3);
2613 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2614 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2615 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2616 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2617 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2618
2619 /*
2620 * Only add this if we have a commit rate
2621 */
2622#if 0
2623 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002624 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002625
Jens Axboe2f99deb2012-03-09 14:37:29 +01002626 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2627 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2628
2629 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2630 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2631#endif
2632
2633 /*
2634 * Set up a drawing area and IOPS and bandwidth graphs
2635 */
2636 gdk_color_parse("white", &white);
2637 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002638 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002639 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002640 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2641 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2642 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2643 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2644 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002645 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2646 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002647 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002648 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002649 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002650 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002651
2652 setup_graphs(&ge->graphs);
2653
2654 /*
2655 * Set up alignments for widgets at the bottom of ui,
2656 * align bottom left, expand horizontally but not vertically
2657 */
Jens Axboe65476332012-03-13 10:37:04 +01002658 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002659 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002660 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2661 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002662
2663 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2664
2665 /*
2666 * Set up thread status progress bar
2667 */
2668 ge->thread_status_pb = gtk_progress_bar_new();
2669 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2670 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2671 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2672
2673
2674 return main_vbox;
2675}
2676
2677static GtkWidget *new_main_page(struct gui *ui)
2678{
2679 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002680 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002681 GdkColor white;
2682
2683 main_vbox = gtk_vbox_new(FALSE, 3);
2684
2685 /*
2686 * Set up alignments for widgets at the top of ui,
2687 * align top left, expand horizontally but not vertically
2688 */
Jens Axboe65476332012-03-13 10:37:04 +01002689 top_align = gtk_alignment_new(0, 0, 1, 0);
2690 top_vbox = gtk_vbox_new(FALSE, 0);
2691 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2692 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002693
2694 probe = gtk_frame_new("Run statistics");
2695 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2696 probe_frame = gtk_vbox_new(FALSE, 3);
2697 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002698
2699 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002700 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01002701 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01002702 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2703 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2704 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2705 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002706
2707 /*
2708 * Only add this if we have a commit rate
2709 */
2710#if 0
2711 probe_box = gtk_hbox_new(FALSE, 3);
2712 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2713
Jens Axboe3e47bd22012-02-29 13:45:02 +01002714 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2715 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2716
Jens Axboe3e47bd22012-02-29 13:45:02 +01002717 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2718 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002719#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002720
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002721 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002722 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002723 */
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002724 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002725 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002726 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002727 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002728 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2729 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2730 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2731 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2732 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002733 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2734 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002735 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002736 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002737 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002738 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002739 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002740
Jens Axboe2f99deb2012-03-09 14:37:29 +01002741 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002742
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002743 /*
2744 * Set up alignments for widgets at the bottom of ui,
2745 * align bottom left, expand horizontally but not vertically
2746 */
Jens Axboe65476332012-03-13 10:37:04 +01002747 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002748 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002749 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2750 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002751
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002752 /*
2753 * Set up thread status progress bar
2754 */
2755 ui->thread_status_pb = gtk_progress_bar_new();
2756 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002757 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002758 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2759
Jens Axboe2f99deb2012-03-09 14:37:29 +01002760 return main_vbox;
2761}
2762
2763static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2764 guint page, gpointer data)
2765
2766{
Jens Axboe02421e62012-03-12 12:05:50 +01002767 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002768 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01002769
Jens Axboe85dd01e2012-03-12 14:33:16 +01002770 if (!page) {
2771 set_job_menu_visible(ui, 0);
2772 return TRUE;
2773 }
2774
2775 set_job_menu_visible(ui, 1);
2776 ge = get_ge_from_page(page);
2777 if (ge)
2778 update_button_states(ui, ge);
2779
Jens Axboe2f99deb2012-03-09 14:37:29 +01002780 return TRUE;
2781}
2782
2783static void init_ui(int *argc, char **argv[], struct gui *ui)
2784{
2785 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01002786 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002787
2788 /* Magical g*thread incantation, you just need this thread stuff.
2789 * Without it, the update that happens in gfio_update_thread_status
2790 * doesn't really happen in a timely fashion, you need expose events
2791 */
2792 if (!g_thread_supported())
2793 g_thread_init(NULL);
2794 gdk_threads_init();
2795
2796 gtk_init(argc, argv);
2797 settings = gtk_settings_get_default();
2798 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2799 g_type_init();
2800
2801 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2802 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2803 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2804
2805 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2806 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2807
2808 ui->vbox = gtk_vbox_new(FALSE, 0);
2809 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2810
Jens Axboe02421e62012-03-12 12:05:50 +01002811 ui->uimanager = gtk_ui_manager_new();
2812 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
2813 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002814
2815 ui->notebook = gtk_notebook_new();
2816 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01002817 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01002818 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002819 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2820
2821 vbox = new_main_page(ui);
2822
2823 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2824
Jens Axboe9b260bd2012-03-06 11:02:52 +01002825 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002826
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002827 gtk_widget_show_all(ui->window);
2828}
2829
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002830int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002831{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002832 if (initialize_fio(envp))
2833 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01002834 if (fio_init_options())
2835 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01002836
Jens Axboe2f99deb2012-03-09 14:37:29 +01002837 memset(&main_ui, 0, sizeof(main_ui));
2838 INIT_FLIST_HEAD(&main_ui.list);
2839
2840 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01002841
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002842 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002843 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002844 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002845 return 0;
2846}