blob: 57a333b3c260ac943dd1b8203b51d5fcaee2b05a [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. Cameronc36f98d2012-02-24 08:17:32 +0100111 GtkWidget *topvbox;
112 GtkWidget *topalign;
113 GtkWidget *bottomalign;
Stephen M. Cameron04cc6b72012-02-24 08:17:31 +0100114 GtkWidget *thread_status_pb;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100115 GtkWidget *buttonbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100116 GtkWidget *scrolled_window;
117 GtkWidget *notebook;
118 GtkWidget *error_info_bar;
119 GtkWidget *error_label;
120 GtkListStore *log_model;
121 GtkWidget *log_tree;
122 GtkWidget *log_view;
123 struct gfio_graphs graphs;
124 struct probe_widget probe;
125 struct eta_widget eta;
126 pthread_t server_t;
127
Jens Axboea9eccde2012-03-09 14:59:42 +0100128 pthread_t t;
129 int handler_running;
130
Jens Axboe2f99deb2012-03-09 14:37:29 +0100131 struct flist_head list;
132} main_ui;
133
Jens Axboe85dd01e2012-03-12 14:33:16 +0100134enum {
135 GE_STATE_NEW = 1,
136 GE_STATE_CONNECTED,
137 GE_STATE_JOB_SENT,
138 GE_STATE_JOB_STARTED,
139 GE_STATE_JOB_RUNNING,
140 GE_STATE_JOB_DONE,
141};
142
Jens Axboe2f99deb2012-03-09 14:37:29 +0100143/*
144 * Notebook entry
145 */
146struct gui_entry {
147 struct flist_head list;
148 struct gui *ui;
149
150 GtkWidget *vbox;
151 GtkWidget *topvbox;
152 GtkWidget *topalign;
153 GtkWidget *bottomalign;
Jens Axboec80b74b2012-03-12 10:23:28 +0100154 GtkWidget *job_notebook;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100155 GtkWidget *thread_status_pb;
156 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100157 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100158 GtkWidget *scrolled_window;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100159 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100160 GtkWidget *error_info_bar;
161 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100162 GtkWidget *results_notebook;
163 GtkWidget *results_window;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100164 GtkListStore *log_model;
165 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100166 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100167 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100168 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100169 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100170 GtkWidget *page_label;
171 gint page_num;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100172 unsigned int state;
Jens Axboe0420ba62012-02-29 11:16:52 +0100173
Jens Axboeb9d2f302012-03-08 20:36:28 +0100174 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100175 int nr_job_files;
176 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100177};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100178
Jens Axboee0681f32012-03-06 12:14:42 +0100179struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100180 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100181 struct fio_client *client;
Jens Axboee0681f32012-03-06 12:14:42 +0100182 GtkWidget *results_widget;
183 GtkWidget *disk_util_frame;
Jens Axboe6b79c802012-03-08 10:51:36 +0100184 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100185 unsigned int job_added;
186 struct thread_options o;
Jens Axboee0681f32012-03-06 12:14:42 +0100187};
188
Jens Axboe9988ca72012-03-09 15:14:06 +0100189static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
190static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +0100191void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +0100192
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100193static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
194{
195 switch (power_of_ten) {
196 case 9: graph_y_title(g, "Billions of IOs / sec");
197 break;
198 case 6: graph_y_title(g, "Millions of IOs / sec");
199 break;
200 case 3: graph_y_title(g, "Thousands of IOs / sec");
201 break;
202 case 0:
203 default: graph_y_title(g, "IOs / sec");
204 break;
205 }
206}
207
Jens Axboe2f99deb2012-03-09 14:37:29 +0100208static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100209{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100210 struct graph *g;
211
212 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
213 graph_title(g, "IOPS");
214 graph_x_title(g, "Time (secs)");
215 graph_y_title(g, "IOs / sec");
216 graph_add_label(g, "Read IOPS");
217 graph_add_label(g, "Write IOPS");
218 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
219 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +0100220 line_graph_set_data_count_limit(g, gfio_graph_limit);
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100221 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
Stephen M. Camerondef0ac22012-03-12 07:32:57 +0100222 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100223 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100224}
225
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100226static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
227{
228 switch (power_of_ten) {
229 case 9: graph_y_title(g, "Petabytes / sec");
230 break;
231 case 6: graph_y_title(g, "Gigabytes / sec");
232 break;
233 case 3: graph_y_title(g, "Megabytes / sec");
234 break;
235 case 0:
236 default: graph_y_title(g, "Kilobytes / sec");
237 break;
238 }
239}
240
Jens Axboe2f99deb2012-03-09 14:37:29 +0100241static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100242{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100243 struct graph *g;
244
245 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
246 graph_title(g, "Bandwidth");
247 graph_x_title(g, "Time (secs)");
248 graph_y_title(g, "Kbytes / sec");
249 graph_add_label(g, "Read Bandwidth");
250 graph_add_label(g, "Write Bandwidth");
251 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
252 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
253 line_graph_set_data_count_limit(g, 100);
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100254 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
Stephen M. Camerondef0ac22012-03-12 07:32:57 +0100255 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
256
Jens Axboe2f99deb2012-03-09 14:37:29 +0100257 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100258}
259
Jens Axboe2f99deb2012-03-09 14:37:29 +0100260static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100261{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100262 g->iops_graph = setup_iops_graph();
263 g->bandwidth_graph = setup_bandwidth_graph();
264}
265
Jens Axboec80b74b2012-03-12 10:23:28 +0100266static void multitext_add_entry(struct multitext_widget *mt, const char *text)
267{
268 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
269 mt->text[mt->max_text] = strdup(text);
270 mt->max_text++;
271}
272
273static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
274{
275 if (index >= mt->max_text)
276 return;
Jens Axboeda185432012-03-12 11:05:46 +0100277 if (!mt->text || !mt->text[index])
Jens Axboec80b74b2012-03-12 10:23:28 +0100278 return;
279
280 mt->cur_text = index;
281 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
282}
283
284static void multitext_update_entry(struct multitext_widget *mt,
285 unsigned int index, const char *text)
286{
Jens Axboeda185432012-03-12 11:05:46 +0100287 if (!mt->text)
288 return;
289
Jens Axboec80b74b2012-03-12 10:23:28 +0100290 if (mt->text[index])
291 free(mt->text[index]);
292
293 mt->text[index] = strdup(text);
294 if (mt->cur_text == index)
295 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
296}
297
298static void multitext_free(struct multitext_widget *mt)
299{
300 int i;
301
302 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
303
304 for (i = 0; i < mt->max_text; i++) {
305 if (mt->text[i])
306 free(mt->text[i]);
307 }
308
309 free(mt->text);
310 mt->cur_text = -1;
311 mt->max_text = 0;
312}
313
Jens Axboe2f99deb2012-03-09 14:37:29 +0100314static void clear_ge_ui_info(struct gui_entry *ge)
315{
316 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
317 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
318 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
319 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100320#if 0
321 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100322 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100323#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100324 multitext_update_entry(&ge->eta.iotype, 0, "");
325 multitext_update_entry(&ge->eta.ioengine, 0, "");
326 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100327 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
328 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
329 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
330 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
331 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
332 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100333}
334
Jens Axboe3863d1a2012-03-09 17:39:05 +0100335static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
336{
337 GtkWidget *entry, *frame;
338
339 frame = gtk_frame_new(label);
340 entry = gtk_combo_box_new_text();
341 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
342 gtk_container_add(GTK_CONTAINER(frame), entry);
343
344 return entry;
345}
346
Jens Axboe3650a3c2012-03-05 14:09:03 +0100347static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
348{
349 GtkWidget *entry, *frame;
350
351 frame = gtk_frame_new(label);
352 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100353 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100354 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
355 gtk_container_add(GTK_CONTAINER(frame), entry);
356
357 return entry;
358}
359
360static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
361{
362 GtkWidget *label_widget;
363 GtkWidget *frame;
364
365 frame = gtk_frame_new(label);
366 label_widget = gtk_label_new(NULL);
367 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
368 gtk_container_add(GTK_CONTAINER(frame), label_widget);
369
370 return label_widget;
371}
372
373static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
374{
375 GtkWidget *button, *box;
376
377 box = gtk_hbox_new(FALSE, 3);
378 gtk_container_add(GTK_CONTAINER(hbox), box);
379
380 button = gtk_spin_button_new_with_range(min, max, 1.0);
381 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
382
383 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
384 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
385
386 return button;
387}
388
Jens Axboe3650a3c2012-03-05 14:09:03 +0100389static void label_set_int_value(GtkWidget *entry, unsigned int val)
390{
391 char tmp[80];
392
393 sprintf(tmp, "%u", val);
394 gtk_label_set_text(GTK_LABEL(entry), tmp);
395}
396
397static void entry_set_int_value(GtkWidget *entry, unsigned int val)
398{
399 char tmp[80];
400
401 sprintf(tmp, "%u", val);
402 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
403}
404
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100405static void show_info_dialog(struct gui *ui, const char *title,
406 const char *message)
407{
408 GtkWidget *dialog, *content, *label;
409
410 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
411 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
412 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
413
414 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
415 label = gtk_label_new(message);
416 gtk_container_add(GTK_CONTAINER(content), label);
417 gtk_widget_show_all(dialog);
418 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
419 gtk_dialog_run(GTK_DIALOG(dialog));
420 gtk_widget_destroy(dialog);
421}
422
Jens Axboe85dd01e2012-03-12 14:33:16 +0100423/*
424 * Update sensitivity of job buttons and job menu items, based on the
425 * state of the client.
426 */
427static void update_button_states(struct gui *ui, struct gui_entry *ge)
428{
429 unsigned int connect_state, send_state, start_state, edit_state;
430 const char *connect_str = NULL;
431 GtkWidget *w;
432
433 switch (ge->state) {
434 default: {
435 char tmp[80];
436
437 sprintf(tmp, "Bad client state: %u\n", ge->state);
438 show_info_dialog(ui, "Error", tmp);
439 /* fall through to new state */
440 }
441
442 case GE_STATE_NEW:
443 connect_state = 1;
444 edit_state = 0;
445 connect_str = "Connect";
446 send_state = 0;
447 start_state = 0;
448 break;
449 case GE_STATE_CONNECTED:
450 connect_state = 1;
451 edit_state = 0;
452 connect_str = "Disconnect";
453 send_state = 1;
454 start_state = 0;
455 break;
456 case GE_STATE_JOB_SENT:
457 connect_state = 1;
458 edit_state = 0;
459 connect_str = "Disconnect";
460 send_state = 0;
461 start_state = 1;
462 break;
463 case GE_STATE_JOB_STARTED:
464 connect_state = 1;
465 edit_state = 1;
466 connect_str = "Disconnect";
467 send_state = 0;
468 start_state = 1;
469 break;
470 case GE_STATE_JOB_RUNNING:
471 connect_state = 1;
472 edit_state = 0;
473 connect_str = "Disconnect";
474 send_state = 0;
475 start_state = 0;
476 break;
477 case GE_STATE_JOB_DONE:
478 connect_state = 1;
479 edit_state = 0;
480 connect_str = "Connect";
481 send_state = 0;
482 start_state = 0;
483 break;
484 }
485
486 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], connect_state);
487 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], send_state);
488 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], start_state);
489 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), connect_str);
490
491 /*
492 * So the below doesn't work at all, how to set those menu items
493 * invisibible...
494 */
495 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Connect");
496 if (w)
497 gtk_widget_set_sensitive(w, connect_state);
498
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100499 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Edit job");
Jens Axboe85dd01e2012-03-12 14:33:16 +0100500 if (w)
501 gtk_widget_set_sensitive(w, edit_state);
502
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100503 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Send job");
Jens Axboe85dd01e2012-03-12 14:33:16 +0100504 if (w)
505 gtk_widget_set_sensitive(w, send_state);
506
Stephen M. Cameron63f81ed2012-03-13 08:08:32 +0100507 w = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu/Start job");
Jens Axboe85dd01e2012-03-12 14:33:16 +0100508 if (w)
509 gtk_widget_set_sensitive(w, start_state);
510}
511
512static void gfio_set_state(struct gui_entry *ge, unsigned int state)
513{
514 ge->state = state;
515 update_button_states(ge->ui, ge);
516}
517
Jens Axboea2697902012-03-05 16:43:49 +0100518#define ALIGN_LEFT 1
519#define ALIGN_RIGHT 2
520#define INVISIBLE 4
521#define UNSORTABLE 8
522
523GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
524{
525 GtkCellRenderer *renderer;
526 GtkTreeViewColumn *col;
527 double xalign = 0.0; /* left as default */
528 PangoAlignment align;
529 gboolean visible;
530
531 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
532 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
533 PANGO_ALIGN_CENTER;
534 visible = !(flags & INVISIBLE);
535
536 renderer = gtk_cell_renderer_text_new();
537 col = gtk_tree_view_column_new();
538
539 gtk_tree_view_column_set_title(col, title);
540 if (!(flags & UNSORTABLE))
541 gtk_tree_view_column_set_sort_column_id(col, index);
542 gtk_tree_view_column_set_resizable(col, TRUE);
543 gtk_tree_view_column_pack_start(col, renderer, TRUE);
544 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
545 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
546 switch (align) {
547 case PANGO_ALIGN_LEFT:
548 xalign = 0.0;
549 break;
550 case PANGO_ALIGN_CENTER:
551 xalign = 0.5;
552 break;
553 case PANGO_ALIGN_RIGHT:
554 xalign = 1.0;
555 break;
556 }
557 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
558 gtk_tree_view_column_set_visible(col, visible);
559 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
560 return col;
561}
562
Jens Axboe9b260bd2012-03-06 11:02:52 +0100563static void gfio_ui_setup_log(struct gui *ui)
564{
565 GtkTreeSelection *selection;
566 GtkListStore *model;
567 GtkWidget *tree_view;
568
569 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
570
571 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
572 gtk_widget_set_can_focus(tree_view, FALSE);
573
574 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
575 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100576 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
577 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100578
579 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
580 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
581 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100582 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100583
584 ui->log_model = model;
585 ui->log_tree = tree_view;
586}
587
Jens Axboea2697902012-03-05 16:43:49 +0100588static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
589 fio_fp64_t *plist,
590 unsigned int len,
591 const char *base,
592 unsigned int scale)
593{
594 GType types[FIO_IO_U_LIST_MAX_LEN];
595 GtkWidget *tree_view;
596 GtkTreeSelection *selection;
597 GtkListStore *model;
598 GtkTreeIter iter;
599 int i;
600
601 for (i = 0; i < len; i++)
602 types[i] = G_TYPE_INT;
603
604 model = gtk_list_store_newv(len, types);
605
606 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
607 gtk_widget_set_can_focus(tree_view, FALSE);
608
Jens Axboe661f7412012-03-06 13:55:45 +0100609 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
610 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
611
Jens Axboea2697902012-03-05 16:43:49 +0100612 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
613 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
614
615 for (i = 0; i < len; i++) {
616 char fbuf[8];
617
618 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
619 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
620 }
621
622 gtk_list_store_append(model, &iter);
623
Jens Axboee0681f32012-03-06 12:14:42 +0100624 for (i = 0; i < len; i++) {
625 if (scale)
626 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100627 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100628 }
Jens Axboea2697902012-03-05 16:43:49 +0100629
630 return tree_view;
631}
632
633static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
634 int ddir)
635{
636 unsigned int *io_u_plat = ts->io_u_plat[ddir];
637 unsigned long nr = ts->clat_stat[ddir].samples;
638 fio_fp64_t *plist = ts->percentile_list;
639 unsigned int *ovals, len, minv, maxv, scale_down;
640 const char *base;
641 GtkWidget *tree_view, *frame, *hbox;
642 char tmp[64];
643
644 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
645 if (!len)
646 goto out;
647
648 /*
649 * We default to usecs, but if the value range is such that we
650 * should scale down to msecs, do that.
651 */
652 if (minv > 2000 && maxv > 99999) {
653 scale_down = 1;
654 base = "msec";
655 } else {
656 scale_down = 0;
657 base = "usec";
658 }
659
660 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
661
662 sprintf(tmp, "Completion percentiles (%s)", base);
663 frame = gtk_frame_new(tmp);
664 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
665
666 hbox = gtk_hbox_new(FALSE, 3);
667 gtk_container_add(GTK_CONTAINER(frame), hbox);
668
669 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
670out:
671 if (ovals)
672 free(ovals);
673}
674
Jens Axboe3650a3c2012-03-05 14:09:03 +0100675static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
676 unsigned long max, double mean, double dev)
677{
678 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100679 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100680 char *minp, *maxp;
681 char tmp[64];
682
683 if (!usec_to_msec(&min, &max, &mean, &dev))
684 base = "(msec)";
685
686 minp = num2str(min, 6, 1, 0);
687 maxp = num2str(max, 6, 1, 0);
688
Jens Axboe3650a3c2012-03-05 14:09:03 +0100689 sprintf(tmp, "%s %s", name, base);
690 frame = gtk_frame_new(tmp);
691 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
692
Jens Axboe3650a3c2012-03-05 14:09:03 +0100693 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100694 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100695
696 label = new_info_label_in_frame(hbox, "Minimum");
697 gtk_label_set_text(GTK_LABEL(label), minp);
698 label = new_info_label_in_frame(hbox, "Maximum");
699 gtk_label_set_text(GTK_LABEL(label), maxp);
700 label = new_info_label_in_frame(hbox, "Average");
701 sprintf(tmp, "%5.02f", mean);
702 gtk_label_set_text(GTK_LABEL(label), tmp);
703 label = new_info_label_in_frame(hbox, "Standard deviation");
704 sprintf(tmp, "%5.02f", dev);
705 gtk_label_set_text(GTK_LABEL(label), tmp);
706
707 free(minp);
708 free(maxp);
709
710}
711
Jens Axboeca850992012-03-05 20:04:43 +0100712#define GFIO_CLAT 1
713#define GFIO_SLAT 2
714#define GFIO_LAT 4
715
Jens Axboe3650a3c2012-03-05 14:09:03 +0100716static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
717 struct thread_stat *ts, int ddir)
718{
719 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100720 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100721 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100722 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100723 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100724 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100725 char *io_p, *bw_p, *iops_p;
726 int i2p;
727
728 if (!ts->runtime[ddir])
729 return;
730
731 i2p = is_power_of_2(rs->kb_base);
732 runt = ts->runtime[ddir];
733
734 bw = (1000 * ts->io_bytes[ddir]) / runt;
735 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
736 bw_p = num2str(bw, 6, 1, i2p);
737
738 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
739 iops_p = num2str(iops, 6, 1, 0);
740
741 box = gtk_hbox_new(FALSE, 3);
742 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
743
744 frame = gtk_frame_new(ddir_label[ddir]);
745 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
746
Jens Axboe0b761302012-03-05 20:44:11 +0100747 main_vbox = gtk_vbox_new(FALSE, 3);
748 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100749
750 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100751 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100752
753 label = new_info_label_in_frame(box, "IO");
754 gtk_label_set_text(GTK_LABEL(label), io_p);
755 label = new_info_label_in_frame(box, "Bandwidth");
756 gtk_label_set_text(GTK_LABEL(label), bw_p);
757 label = new_info_label_in_frame(box, "IOPS");
758 gtk_label_set_text(GTK_LABEL(label), iops_p);
759 label = new_info_label_in_frame(box, "Runtime (msec)");
760 label_set_int_value(label, ts->runtime[ddir]);
761
Jens Axboee0681f32012-03-06 12:14:42 +0100762 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100763 double p_of_agg = 100.0;
764 const char *bw_str = "KB";
765 char tmp[32];
766
767 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100768 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100769 if (p_of_agg > 100.0)
770 p_of_agg = 100.0;
771 }
772
Jens Axboee0681f32012-03-06 12:14:42 +0100773 if (mean[0] > 999999.9) {
774 min[0] /= 1000.0;
775 max[0] /= 1000.0;
776 mean[0] /= 1000.0;
777 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100778 bw_str = "MB";
779 }
780
Jens Axboe0b761302012-03-05 20:44:11 +0100781 sprintf(tmp, "Bandwidth (%s)", bw_str);
782 frame = gtk_frame_new(tmp);
783 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100784
Jens Axboe0b761302012-03-05 20:44:11 +0100785 box = gtk_hbox_new(FALSE, 3);
786 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100787
Jens Axboe0b761302012-03-05 20:44:11 +0100788 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100789 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100790 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100791 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100792 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100793 sprintf(tmp, "%3.2f%%", p_of_agg);
794 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100795 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100796 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100797 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100798 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100799 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100800 gtk_label_set_text(GTK_LABEL(label), tmp);
801 }
802
Jens Axboee0681f32012-03-06 12:14:42 +0100803 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100804 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100805 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100806 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100807 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100808 flags |= GFIO_LAT;
809
810 if (flags) {
811 frame = gtk_frame_new("Latency");
812 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
813
814 vbox = gtk_vbox_new(FALSE, 3);
815 gtk_container_add(GTK_CONTAINER(frame), vbox);
816
817 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100818 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100819 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100820 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100821 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100822 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100823 }
824
825 if (ts->clat_percentiles)
826 gfio_show_clat_percentiles(main_vbox, ts, ddir);
827
828
Jens Axboe3650a3c2012-03-05 14:09:03 +0100829 free(io_p);
830 free(bw_p);
831 free(iops_p);
832}
833
Jens Axboee5bd1342012-03-05 21:38:12 +0100834static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
835 const char **labels)
836{
837 GtkWidget *tree_view;
838 GtkTreeSelection *selection;
839 GtkListStore *model;
840 GtkTreeIter iter;
841 GType *types;
842 int i, skipped;
843
844 /*
845 * Check if all are empty, in which case don't bother
846 */
847 for (i = 0, skipped = 0; i < num; i++)
848 if (lat[i] <= 0.0)
849 skipped++;
850
851 if (skipped == num)
852 return NULL;
853
854 types = malloc(num * sizeof(GType));
855
856 for (i = 0; i < num; i++)
857 types[i] = G_TYPE_STRING;
858
859 model = gtk_list_store_newv(num, types);
860 free(types);
861 types = NULL;
862
863 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
864 gtk_widget_set_can_focus(tree_view, FALSE);
865
Jens Axboe661f7412012-03-06 13:55:45 +0100866 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
867 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
868
Jens Axboee5bd1342012-03-05 21:38:12 +0100869 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
870 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
871
872 for (i = 0; i < num; i++)
873 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
874
875 gtk_list_store_append(model, &iter);
876
877 for (i = 0; i < num; i++) {
878 char fbuf[32];
879
880 if (lat[i] <= 0.0)
881 sprintf(fbuf, "0.00");
882 else
883 sprintf(fbuf, "%3.2f%%", lat[i]);
884
885 gtk_list_store_set(model, &iter, i, fbuf, -1);
886 }
887
888 return tree_view;
889}
890
891static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
892{
893 GtkWidget *box, *frame, *tree_view;
894 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
895 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
896 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
897 "250", "500", "750", "1000", };
898 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
899 "250", "500", "750", "1000", "2000",
900 ">= 2000", };
901
902 stat_calc_lat_u(ts, io_u_lat_u);
903 stat_calc_lat_m(ts, io_u_lat_m);
904
905 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
906 if (tree_view) {
907 frame = gtk_frame_new("Latency buckets (usec)");
908 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
909
910 box = gtk_hbox_new(FALSE, 3);
911 gtk_container_add(GTK_CONTAINER(frame), box);
912 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
913 }
914
915 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
916 if (tree_view) {
917 frame = gtk_frame_new("Latency buckets (msec)");
918 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
919
920 box = gtk_hbox_new(FALSE, 3);
921 gtk_container_add(GTK_CONTAINER(frame), box);
922 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
923 }
924}
925
Jens Axboe2e331012012-03-05 22:07:54 +0100926static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
927{
928 GtkWidget *box, *frame, *entry;
929 double usr_cpu, sys_cpu;
930 unsigned long runtime;
931 char tmp[32];
932
933 runtime = ts->total_run_time;
934 if (runtime) {
935 double runt = (double) runtime;
936
937 usr_cpu = (double) ts->usr_time * 100 / runt;
938 sys_cpu = (double) ts->sys_time * 100 / runt;
939 } else {
940 usr_cpu = 0;
941 sys_cpu = 0;
942 }
943
944 frame = gtk_frame_new("OS resources");
945 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
946
947 box = gtk_hbox_new(FALSE, 3);
948 gtk_container_add(GTK_CONTAINER(frame), box);
949
950 entry = new_info_entry_in_frame(box, "User CPU");
951 sprintf(tmp, "%3.2f%%", usr_cpu);
952 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
953 entry = new_info_entry_in_frame(box, "System CPU");
954 sprintf(tmp, "%3.2f%%", sys_cpu);
955 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
956 entry = new_info_entry_in_frame(box, "Context switches");
957 entry_set_int_value(entry, ts->ctx);
958 entry = new_info_entry_in_frame(box, "Major faults");
959 entry_set_int_value(entry, ts->majf);
960 entry = new_info_entry_in_frame(box, "Minor faults");
961 entry_set_int_value(entry, ts->minf);
962}
Jens Axboe19998db2012-03-06 09:17:59 +0100963static void gfio_add_sc_depths_tree(GtkListStore *model,
964 struct thread_stat *ts, unsigned int len,
965 int submit)
966{
967 double io_u_dist[FIO_IO_U_MAP_NR];
968 GtkTreeIter iter;
969 /* Bits 0, and 3-8 */
970 const int add_mask = 0x1f9;
971 int i, j;
972
973 if (submit)
974 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
975 else
976 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
977
978 gtk_list_store_append(model, &iter);
979
980 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
981
982 for (i = 1, j = 0; i < len; i++) {
983 char fbuf[32];
984
985 if (!(add_mask & (1UL << (i - 1))))
986 sprintf(fbuf, "0.0%%");
987 else {
988 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
989 j++;
990 }
991
992 gtk_list_store_set(model, &iter, i, fbuf, -1);
993 }
994
995}
996
997static void gfio_add_total_depths_tree(GtkListStore *model,
998 struct thread_stat *ts, unsigned int len)
999{
1000 double io_u_dist[FIO_IO_U_MAP_NR];
1001 GtkTreeIter iter;
1002 /* Bits 1-6, and 8 */
1003 const int add_mask = 0x17e;
1004 int i, j;
1005
1006 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
1007
1008 gtk_list_store_append(model, &iter);
1009
1010 gtk_list_store_set(model, &iter, 0, "Total", -1);
1011
1012 for (i = 1, j = 0; i < len; i++) {
1013 char fbuf[32];
1014
1015 if (!(add_mask & (1UL << (i - 1))))
1016 sprintf(fbuf, "0.0%%");
1017 else {
1018 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
1019 j++;
1020 }
1021
1022 gtk_list_store_set(model, &iter, i, fbuf, -1);
1023 }
1024
1025}
Jens Axboe2e331012012-03-05 22:07:54 +01001026
1027static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
1028{
Jens Axboe2e331012012-03-05 22:07:54 +01001029 GtkWidget *frame, *box, *tree_view;
1030 GtkTreeSelection *selection;
1031 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +01001032 GType types[FIO_IO_U_MAP_NR + 1];
1033 int i;
Jens Axboe19998db2012-03-06 09:17:59 +01001034#define NR_LABELS 10
1035 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +01001036
1037 frame = gtk_frame_new("IO depths");
1038 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1039
1040 box = gtk_hbox_new(FALSE, 3);
1041 gtk_container_add(GTK_CONTAINER(frame), box);
1042
Jens Axboe19998db2012-03-06 09:17:59 +01001043 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001044 types[i] = G_TYPE_STRING;
1045
Jens Axboe19998db2012-03-06 09:17:59 +01001046 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +01001047
1048 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
1049 gtk_widget_set_can_focus(tree_view, FALSE);
1050
Jens Axboe661f7412012-03-06 13:55:45 +01001051 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
1052 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
1053
Jens Axboe2e331012012-03-05 22:07:54 +01001054 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
1055 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
1056
Jens Axboe19998db2012-03-06 09:17:59 +01001057 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +01001058 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
1059
Jens Axboe19998db2012-03-06 09:17:59 +01001060 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1061 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1062 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001063
1064 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1065}
1066
Jens Axboef9d40b42012-03-06 09:52:49 +01001067static gboolean results_window_delete(GtkWidget *w, gpointer data)
1068{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001069 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001070
1071 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001072 ge->results_window = NULL;
1073 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001074 return TRUE;
1075}
1076
Jens Axboe2f99deb2012-03-09 14:37:29 +01001077static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001078{
1079 GtkWidget *win, *notebook;
1080
Jens Axboe2f99deb2012-03-09 14:37:29 +01001081 if (ge->results_window)
1082 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001083
1084 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1085 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001086 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001087 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1088 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001089
1090 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001091 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1092 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboef9d40b42012-03-06 09:52:49 +01001093 gtk_container_add(GTK_CONTAINER(win), notebook);
1094
Jens Axboe2f99deb2012-03-09 14:37:29 +01001095 ge->results_window = win;
1096 ge->results_notebook = notebook;
1097 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001098}
1099
Jens Axboe3650a3c2012-03-05 14:09:03 +01001100static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1101 struct group_run_stats *rs)
1102{
Jens Axboeb01329d2012-03-07 20:31:28 +01001103 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
Jens Axboee0681f32012-03-06 12:14:42 +01001104 struct gfio_client *gc = client->client_data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001105
1106 gdk_threads_enter();
1107
Jens Axboe2f99deb2012-03-09 14:37:29 +01001108 res_win = get_results_window(gc->ge);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001109
Jens Axboeb01329d2012-03-07 20:31:28 +01001110 scroll = gtk_scrolled_window_new(NULL, NULL);
1111 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1112 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1113
Jens Axboe3650a3c2012-03-05 14:09:03 +01001114 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001115
Jens Axboeb01329d2012-03-07 20:31:28 +01001116 box = gtk_hbox_new(FALSE, 0);
1117 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001118
Jens Axboeb01329d2012-03-07 20:31:28 +01001119 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1120
1121 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +01001122
Jens Axboee0681f32012-03-06 12:14:42 +01001123 gc->results_widget = vbox;
1124
Jens Axboe3650a3c2012-03-05 14:09:03 +01001125 entry = new_info_entry_in_frame(box, "Name");
1126 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1127 if (strlen(ts->description)) {
1128 entry = new_info_entry_in_frame(box, "Description");
1129 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1130 }
1131 entry = new_info_entry_in_frame(box, "Group ID");
1132 entry_set_int_value(entry, ts->groupid);
1133 entry = new_info_entry_in_frame(box, "Jobs");
1134 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +01001135 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +01001136 entry_set_int_value(entry, ts->error);
1137 entry = new_info_entry_in_frame(box, "PID");
1138 entry_set_int_value(entry, ts->pid);
1139
1140 if (ts->io_bytes[DDIR_READ])
1141 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1142 if (ts->io_bytes[DDIR_WRITE])
1143 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1144
Jens Axboee5bd1342012-03-05 21:38:12 +01001145 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +01001146 gfio_show_cpu_usage(vbox, ts);
1147 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +01001148
Jens Axboe2f99deb2012-03-09 14:37:29 +01001149 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001150 gdk_threads_leave();
1151}
1152
Jens Axboe084d1c62012-03-03 20:28:07 +01001153static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001154{
Jens Axboe9b260bd2012-03-06 11:02:52 +01001155 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001156 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001157 GtkTreeIter iter;
1158 struct tm *tm;
1159 time_t sec;
1160 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001161
Jens Axboe9b260bd2012-03-06 11:02:52 +01001162 sec = p->log_sec;
1163 tm = localtime(&sec);
1164 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1165 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1166
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001167 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +01001168
Jens Axboe2f99deb2012-03-09 14:37:29 +01001169 gtk_list_store_append(ui->log_model, &iter);
1170 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1171 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1172 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1173 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001174
Jens Axboe6b79c802012-03-08 10:51:36 +01001175 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001176 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +01001177
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001178 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001179}
1180
1181static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1182{
Jens Axboee0681f32012-03-06 12:14:42 +01001183 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1184 struct gfio_client *gc = client->client_data;
1185 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +01001186 double util;
1187 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001188
Jens Axboe0050e5f2012-03-06 09:23:27 +01001189 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +01001190
Jens Axboe45dcb2e2012-03-07 16:16:50 +01001191 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +01001192 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +01001193
1194 if (!gc->disk_util_frame) {
1195 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1196 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1197 }
1198
1199 vbox = gtk_vbox_new(FALSE, 3);
1200 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1201
1202 frame = gtk_frame_new((char *) p->dus.name);
1203 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1204
1205 box = gtk_vbox_new(FALSE, 3);
1206 gtk_container_add(GTK_CONTAINER(frame), box);
1207
1208 frame = gtk_frame_new("Read");
1209 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1210 vbox = gtk_hbox_new(TRUE, 3);
1211 gtk_container_add(GTK_CONTAINER(frame), vbox);
1212 entry = new_info_entry_in_frame(vbox, "IOs");
1213 entry_set_int_value(entry, p->dus.ios[0]);
1214 entry = new_info_entry_in_frame(vbox, "Merges");
1215 entry_set_int_value(entry, p->dus.merges[0]);
1216 entry = new_info_entry_in_frame(vbox, "Sectors");
1217 entry_set_int_value(entry, p->dus.sectors[0]);
1218 entry = new_info_entry_in_frame(vbox, "Ticks");
1219 entry_set_int_value(entry, p->dus.ticks[0]);
1220
1221 frame = gtk_frame_new("Write");
1222 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1223 vbox = gtk_hbox_new(TRUE, 3);
1224 gtk_container_add(GTK_CONTAINER(frame), vbox);
1225 entry = new_info_entry_in_frame(vbox, "IOs");
1226 entry_set_int_value(entry, p->dus.ios[1]);
1227 entry = new_info_entry_in_frame(vbox, "Merges");
1228 entry_set_int_value(entry, p->dus.merges[1]);
1229 entry = new_info_entry_in_frame(vbox, "Sectors");
1230 entry_set_int_value(entry, p->dus.sectors[1]);
1231 entry = new_info_entry_in_frame(vbox, "Ticks");
1232 entry_set_int_value(entry, p->dus.ticks[1]);
1233
1234 frame = gtk_frame_new("Shared");
1235 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1236 vbox = gtk_hbox_new(TRUE, 3);
1237 gtk_container_add(GTK_CONTAINER(frame), vbox);
1238 entry = new_info_entry_in_frame(vbox, "IO ticks");
1239 entry_set_int_value(entry, p->dus.io_ticks);
1240 entry = new_info_entry_in_frame(vbox, "Time in queue");
1241 entry_set_int_value(entry, p->dus.time_in_queue);
1242
Jens Axboe604cfe32012-03-07 19:51:36 +01001243 util = 0.0;
1244 if (p->dus.msec)
1245 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1246 if (util > 100.0)
1247 util = 100.0;
1248
1249 sprintf(tmp, "%3.2f%%", util);
1250 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1251 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1252
Jens Axboee0681f32012-03-06 12:14:42 +01001253 gtk_widget_show_all(gc->results_widget);
1254out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001255 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001256}
1257
Jens Axboe3650a3c2012-03-05 14:09:03 +01001258extern int sum_stat_clients;
1259extern struct thread_stat client_ts;
1260extern struct group_run_stats client_gs;
1261
1262static int sum_stat_nr;
1263
Jens Axboe89e5fad2012-03-05 09:21:12 +01001264static void gfio_thread_status_op(struct fio_client *client,
1265 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001266{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001267 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1268
1269 gfio_display_ts(client, &p->ts, &p->rs);
1270
1271 if (sum_stat_clients == 1)
1272 return;
1273
1274 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1275 sum_group_stats(&client_gs, &p->rs);
1276
1277 client_ts.members++;
1278 client_ts.groupid = p->ts.groupid;
1279
1280 if (++sum_stat_nr == sum_stat_clients) {
1281 strcpy(client_ts.name, "All clients");
1282 gfio_display_ts(client, &client_ts, &client_gs);
1283 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001284}
1285
Jens Axboe89e5fad2012-03-05 09:21:12 +01001286static void gfio_group_stats_op(struct fio_client *client,
1287 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001288{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001289 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001290}
1291
Jens Axboe2f99deb2012-03-09 14:37:29 +01001292static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1293 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001294{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001295 struct gfio_graphs *g = data;
1296
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001297 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1298 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1299 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1300 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001301 return TRUE;
1302}
1303
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001304static void draw_graph(struct graph *g, cairo_t *cr)
1305{
1306 line_graph_draw(g, cr);
1307 cairo_stroke(cr);
1308}
1309
Jens Axboe93e2db22012-03-13 09:45:22 +01001310static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1311 gboolean keyboard_mode, GtkTooltip *tooltip,
1312 gpointer data)
1313{
1314 struct gfio_graphs *g = data;
1315 const char *text = NULL;
1316
1317 if (graph_contains_xy(g->iops_graph, x, y))
1318 text = graph_find_tooltip(g->iops_graph, x, y);
1319 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1320 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1321
1322 if (text) {
1323 gtk_tooltip_set_text(tooltip, text);
1324 return TRUE;
1325 }
1326
1327 return FALSE;
1328}
1329
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001330static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1331{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001332 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001333 cairo_t *cr;
1334
1335 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001336
1337 if (graph_has_tooltips(g->iops_graph) ||
1338 graph_has_tooltips(g->bandwidth_graph)) {
1339 g_object_set(w, "has-tooltip", TRUE, NULL);
1340 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1341 }
1342
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001343 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001344 draw_graph(g->iops_graph, cr);
1345 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001346 cairo_destroy(cr);
1347
1348 return FALSE;
1349}
1350
Jens Axboe2f99deb2012-03-09 14:37:29 +01001351/*
1352 * Client specific ETA
1353 */
1354static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001355{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001356 struct gfio_client *gc = client->client_data;
1357 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001358 static int eta_good;
1359 char eta_str[128];
1360 char output[256];
1361 char tmp[32];
1362 double perc = 0.0;
1363 int i2p = 0;
1364
Jens Axboe0050e5f2012-03-06 09:23:27 +01001365 gdk_threads_enter();
1366
Jens Axboe3e47bd22012-02-29 13:45:02 +01001367 eta_str[0] = '\0';
1368 output[0] = '\0';
1369
1370 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1371 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1372 eta_to_str(eta_str, je->eta_sec);
1373 }
1374
1375 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001376 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001377 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001378 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001379
1380#if 0
1381 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1382 if (je->m_rate || je->t_rate) {
1383 char *tr, *mr;
1384
1385 mr = num2str(je->m_rate, 4, 0, i2p);
1386 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001387 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001388 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1389 free(tr);
1390 free(mr);
1391 } else if (je->m_iops || je->t_iops)
1392 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001393
Jens Axboe2f99deb2012-03-09 14:37:29 +01001394 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1395 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1396 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1397 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001398#endif
1399
1400 if (je->eta_sec != INT_MAX && je->nr_running) {
1401 char *iops_str[2];
1402 char *rate_str[2];
1403
1404 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1405 strcpy(output, "-.-% done");
1406 else {
1407 eta_good = 1;
1408 perc *= 100.0;
1409 sprintf(output, "%3.1f%% done", perc);
1410 }
1411
1412 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1413 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1414
1415 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1416 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1417
Jens Axboe2f99deb2012-03-09 14:37:29 +01001418 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1419 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1420 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1421 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001422
Jens Axboe93e2db22012-03-13 09:45:22 +01001423 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1424 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1425 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1426 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 +01001427
1428 free(rate_str[0]);
1429 free(rate_str[1]);
1430 free(iops_str[0]);
1431 free(iops_str[1]);
1432 }
1433
1434 if (eta_str[0]) {
1435 char *dst = output + strlen(output);
1436
1437 sprintf(dst, " - %s", eta_str);
1438 }
1439
Jens Axboe9988ca72012-03-09 15:14:06 +01001440 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001441 gdk_threads_leave();
1442}
1443
1444/*
1445 * Update ETA in main window for all clients
1446 */
1447static void gfio_update_all_eta(struct jobs_eta *je)
1448{
1449 struct gui *ui = &main_ui;
1450 static int eta_good;
1451 char eta_str[128];
1452 char output[256];
1453 double perc = 0.0;
1454 int i2p = 0;
1455
1456 gdk_threads_enter();
1457
1458 eta_str[0] = '\0';
1459 output[0] = '\0';
1460
1461 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1462 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1463 eta_to_str(eta_str, je->eta_sec);
1464 }
1465
1466#if 0
1467 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1468 if (je->m_rate || je->t_rate) {
1469 char *tr, *mr;
1470
1471 mr = num2str(je->m_rate, 4, 0, i2p);
1472 tr = num2str(je->t_rate, 4, 0, i2p);
1473 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1474 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1475 free(tr);
1476 free(mr);
1477 } else if (je->m_iops || je->t_iops)
1478 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1479
1480 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1481 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1482 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1483 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1484#endif
1485
Jens Axboe3863d1a2012-03-09 17:39:05 +01001486 entry_set_int_value(ui->eta.jobs, je->nr_running);
1487
Jens Axboe2f99deb2012-03-09 14:37:29 +01001488 if (je->eta_sec != INT_MAX && je->nr_running) {
1489 char *iops_str[2];
1490 char *rate_str[2];
1491
1492 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1493 strcpy(output, "-.-% done");
1494 else {
1495 eta_good = 1;
1496 perc *= 100.0;
1497 sprintf(output, "%3.1f%% done", perc);
1498 }
1499
1500 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1501 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1502
1503 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1504 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1505
1506 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1507 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1508 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1509 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1510
Jens Axboe93e2db22012-03-13 09:45:22 +01001511 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1512 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1513 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1514 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 +01001515
Jens Axboe3e47bd22012-02-29 13:45:02 +01001516 free(rate_str[0]);
1517 free(rate_str[1]);
1518 free(iops_str[0]);
1519 free(iops_str[1]);
1520 }
1521
1522 if (eta_str[0]) {
1523 char *dst = output + strlen(output);
1524
1525 sprintf(dst, " - %s", eta_str);
1526 }
1527
Jens Axboe9988ca72012-03-09 15:14:06 +01001528 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001529 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001530}
1531
Stephen M. Camerona1820202012-02-24 08:17:31 +01001532static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1533{
Jens Axboe843ad232012-02-29 11:44:53 +01001534 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001535 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001536 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001537 const char *os, *arch;
1538 char buf[64];
1539
1540 os = fio_get_os_string(probe->os);
1541 if (!os)
1542 os = "unknown";
1543
1544 arch = fio_get_arch_string(probe->arch);
1545 if (!arch)
1546 os = "unknown";
1547
1548 if (!client->name)
1549 client->name = strdup((char *) probe->hostname);
1550
Jens Axboe0050e5f2012-03-06 09:23:27 +01001551 gdk_threads_enter();
1552
Jens Axboe2f99deb2012-03-09 14:37:29 +01001553 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1554 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1555 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001556 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001557 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001558
Jens Axboe85dd01e2012-03-12 14:33:16 +01001559 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001560
1561 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001562}
1563
Jens Axboe9988ca72012-03-09 15:14:06 +01001564static void gfio_update_thread_status(struct gui_entry *ge,
1565 char *status_message, double perc)
1566{
1567 static char message[100];
1568 const char *m = message;
1569
1570 strncpy(message, status_message, sizeof(message) - 1);
1571 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1572 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1573 gtk_widget_queue_draw(main_ui.window);
1574}
1575
1576static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001577{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001578 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001579 static char message[100];
1580 const char *m = message;
1581
1582 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001583 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1584 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1585 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001586}
1587
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001588static void gfio_quit_op(struct fio_client *client)
1589{
Jens Axboee0681f32012-03-06 12:14:42 +01001590 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001591
Jens Axboe0050e5f2012-03-06 09:23:27 +01001592 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001593 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001594 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001595}
1596
Jens Axboe807f9972012-03-02 10:25:24 +01001597static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1598{
1599 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001600 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001601 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001602 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001603 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001604
Jens Axboedcaeb602012-03-08 19:45:37 +01001605 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001606
Jens Axboe0050e5f2012-03-06 09:23:27 +01001607 gdk_threads_enter();
1608
Jens Axboe2f99deb2012-03-09 14:37:29 +01001609 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1610
Jens Axboe3863d1a2012-03-09 17:39:05 +01001611 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1612 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1613
Jens Axboec80b74b2012-03-12 10:23:28 +01001614 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1615 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001616
Jens Axboedcaeb602012-03-08 19:45:37 +01001617 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001618 multitext_add_entry(&ge->eta.iodepth, tmp);
1619
1620 multitext_set_entry(&ge->eta.iotype, 0);
1621 multitext_set_entry(&ge->eta.ioengine, 0);
1622 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001623
Jens Axboedcaeb602012-03-08 19:45:37 +01001624 gc->job_added++;
1625
Jens Axboe85dd01e2012-03-12 14:33:16 +01001626 gfio_set_state(ge, GE_STATE_JOB_SENT);
1627
Jens Axboe0050e5f2012-03-06 09:23:27 +01001628 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001629}
1630
Jens Axboeed727a42012-03-02 12:14:40 +01001631static void gfio_client_timed_out(struct fio_client *client)
1632{
Jens Axboee0681f32012-03-06 12:14:42 +01001633 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001634 char buf[256];
1635
1636 gdk_threads_enter();
1637
Jens Axboe85dd01e2012-03-12 14:33:16 +01001638 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001639 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001640
1641 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001642 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001643
1644 gdk_threads_leave();
1645}
1646
Jens Axboe6b79c802012-03-08 10:51:36 +01001647static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1648{
1649 struct gfio_client *gc = client->client_data;
1650
1651 gdk_threads_enter();
1652
Jens Axboe85dd01e2012-03-12 14:33:16 +01001653 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001654
1655 if (gc->err_entry)
1656 entry_set_int_value(gc->err_entry, client->error);
1657
1658 gdk_threads_leave();
1659}
1660
Jens Axboe85dd01e2012-03-12 14:33:16 +01001661static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1662{
1663 struct gfio_client *gc = client->client_data;
1664
1665 gdk_threads_enter();
1666 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1667 gdk_threads_leave();
1668}
1669
1670static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1671{
1672 struct gfio_client *gc = client->client_data;
1673
1674 gdk_threads_enter();
1675 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1676 gdk_threads_leave();
1677}
1678
Stephen M. Camerona1820202012-02-24 08:17:31 +01001679struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001680 .text_op = gfio_text_op,
1681 .disk_util = gfio_disk_util_op,
1682 .thread_status = gfio_thread_status_op,
1683 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001684 .jobs_eta = gfio_update_client_eta,
1685 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001686 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001687 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001688 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001689 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001690 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001691 .start = gfio_client_start,
1692 .job_start = gfio_client_job_start,
Jens Axboe6433ee02012-03-09 20:10:51 +01001693 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001694 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001695};
1696
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001697static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1698 __attribute__((unused)) gpointer data)
1699{
1700 gtk_main_quit();
1701}
1702
Stephen M. Cameron25927252012-02-24 08:17:31 +01001703static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001704{
Jens Axboea9eccde2012-03-09 14:59:42 +01001705 struct gui *ui = arg;
1706
1707 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001708 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001709 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001710 return NULL;
1711}
1712
Jens Axboe2f99deb2012-03-09 14:37:29 +01001713static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001714{
Jens Axboe9988ca72012-03-09 15:14:06 +01001715 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001716 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001717
Jens Axboe2f99deb2012-03-09 14:37:29 +01001718 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001719 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001720 if (ret < 0) {
1721 GError *error;
1722
1723 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1724 report_error(error);
1725 g_error_free(error);
1726 break;
1727 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001728 break;
1729
Jens Axboe2f99deb2012-03-09 14:37:29 +01001730 free(ge->job_files[i]);
1731 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001732 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001733 while (i < ge->nr_job_files) {
1734 free(ge->job_files[i]);
1735 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001736 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001737 }
1738
Jens Axboe3af45202012-03-13 09:59:53 +01001739 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01001740 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001741}
1742
Jens Axboe63a130b2012-03-06 20:08:59 +01001743static void *server_thread(void *arg)
1744{
1745 is_backend = 1;
1746 gfio_server_running = 1;
1747 fio_start_server(NULL);
1748 gfio_server_running = 0;
1749 return NULL;
1750}
1751
Jens Axboe2f99deb2012-03-09 14:37:29 +01001752static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001753{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001754 struct gui *ui = &main_ui;
1755
Jens Axboe63a130b2012-03-06 20:08:59 +01001756 if (!gfio_server_running) {
1757 gfio_server_running = 1;
1758 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001759 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001760 }
1761}
1762
Stephen M. Cameron25927252012-02-24 08:17:31 +01001763static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1764 gpointer data)
1765{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001766 struct gui_entry *ge = data;
1767 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001768
Jens Axboe78cb2fe2012-03-12 23:05:29 +01001769 if (gc)
1770 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001771}
1772
Jens Axboedf06f222012-03-02 13:32:04 +01001773static void file_open(GtkWidget *w, gpointer data);
1774
1775static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001776{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001777 struct gui_entry *ge = data;
1778 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001779
Jens Axboe85dd01e2012-03-12 14:33:16 +01001780 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01001781 int ret;
1782
Jens Axboe2f99deb2012-03-09 14:37:29 +01001783 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01001784 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001785 if (!ge->nr_job_files)
1786 return;
1787
1788 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1789 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001790 ret = fio_client_connect(gc->client);
1791 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001792 if (!ge->ui->handler_running)
1793 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001794 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01001795 } else {
1796 GError *error;
1797
1798 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1799 report_error(error);
1800 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001801 }
Jens Axboedf06f222012-03-02 13:32:04 +01001802 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001803 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001804 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001805 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001806 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001807}
1808
Jens Axboeb9d2f302012-03-08 20:36:28 +01001809static void send_clicked(GtkWidget *widget, gpointer data)
1810{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001811 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001812
Jens Axboe2f99deb2012-03-09 14:37:29 +01001813 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001814 GError *error;
1815
1816 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);
1817 report_error(error);
1818 g_error_free(error);
1819
Jens Axboe2f99deb2012-03-09 14:37:29 +01001820 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001821 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01001822}
1823
Jens Axboe2f99deb2012-03-09 14:37:29 +01001824static GtkWidget *add_button(GtkWidget *buttonbox,
1825 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001826{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001827 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1828
1829 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1830 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1831 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1832 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1833
1834 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001835}
1836
Jens Axboe2f99deb2012-03-09 14:37:29 +01001837static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1838 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001839{
1840 int i;
1841
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001842 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001843 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001844}
1845
Jens Axboe0420ba62012-02-29 11:16:52 +01001846static void on_info_bar_response(GtkWidget *widget, gint response,
1847 gpointer data)
1848{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001849 struct gui *ui = &main_ui;
1850
Jens Axboe0420ba62012-02-29 11:16:52 +01001851 if (response == GTK_RESPONSE_OK) {
1852 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001853 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001854 }
1855}
1856
Jens Axboedf06f222012-03-02 13:32:04 +01001857void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001858{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001859 struct gui *ui = &main_ui;
1860
1861 if (ui->error_info_bar == NULL) {
1862 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001863 GTK_RESPONSE_OK,
1864 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001865 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1866 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001867 GTK_MESSAGE_ERROR);
1868
Jens Axboe2f99deb2012-03-09 14:37:29 +01001869 ui->error_label = gtk_label_new(error->message);
1870 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1871 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001872
Jens Axboe2f99deb2012-03-09 14:37:29 +01001873 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1874 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001875 } else {
1876 char buffer[256];
1877 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001878 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001879 }
1880}
1881
Jens Axboe62bc9372012-03-07 11:45:07 +01001882struct connection_widgets
1883{
1884 GtkWidget *hentry;
1885 GtkWidget *combo;
1886 GtkWidget *button;
1887};
1888
1889static void hostname_cb(GtkEntry *entry, gpointer data)
1890{
1891 struct connection_widgets *cw = data;
1892 int uses_net = 0, is_localhost = 0;
1893 const gchar *text;
1894 gchar *ctext;
1895
1896 /*
1897 * Check whether to display the 'auto start backend' box
1898 * or not. Show it if we are a localhost and using network,
1899 * or using a socket.
1900 */
1901 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1902 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1903 uses_net = 1;
1904 g_free(ctext);
1905
1906 if (uses_net) {
1907 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1908 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1909 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1910 !strcmp(text, "ip6-loopback"))
1911 is_localhost = 1;
1912 }
1913
1914 if (!uses_net || is_localhost) {
1915 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1916 gtk_widget_set_sensitive(cw->button, 1);
1917 } else {
1918 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1919 gtk_widget_set_sensitive(cw->button, 0);
1920 }
1921}
1922
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001923static int get_connection_details(char **host, int *port, int *type,
1924 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001925{
Jens Axboe62bc9372012-03-07 11:45:07 +01001926 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1927 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001928 char *typeentry;
1929
1930 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001931 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001932 GTK_DIALOG_DESTROY_WITH_PARENT,
1933 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1934 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1935
1936 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001937 /* gtk_dialog_get_content_area() is 2.14 and newer */
1938 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001939 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1940
1941 box = gtk_vbox_new(FALSE, 6);
1942 gtk_container_add(GTK_CONTAINER(frame), box);
1943
1944 hbox = gtk_hbox_new(TRUE, 10);
1945 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01001946 cw.hentry = gtk_entry_new();
1947 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1948 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001949
1950 frame = gtk_frame_new("Port");
1951 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1952 box = gtk_vbox_new(FALSE, 10);
1953 gtk_container_add(GTK_CONTAINER(frame), box);
1954
1955 hbox = gtk_hbox_new(TRUE, 4);
1956 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1957 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1958
1959 frame = gtk_frame_new("Type");
1960 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1961 box = gtk_vbox_new(FALSE, 10);
1962 gtk_container_add(GTK_CONTAINER(frame), box);
1963
1964 hbox = gtk_hbox_new(TRUE, 4);
1965 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1966
Jens Axboe62bc9372012-03-07 11:45:07 +01001967 cw.combo = gtk_combo_box_new_text();
1968 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1969 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1970 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1971 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001972
Jens Axboe62bc9372012-03-07 11:45:07 +01001973 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001974
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001975 frame = gtk_frame_new("Options");
1976 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1977 box = gtk_vbox_new(FALSE, 10);
1978 gtk_container_add(GTK_CONTAINER(frame), box);
1979
1980 hbox = gtk_hbox_new(TRUE, 4);
1981 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1982
Jens Axboe62bc9372012-03-07 11:45:07 +01001983 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1984 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1985 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.");
1986 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1987
1988 /*
1989 * Connect edit signal, so we can show/not-show the auto start button
1990 */
1991 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1992 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001993
Jens Axboea7a42ce2012-03-02 13:12:04 +01001994 gtk_widget_show_all(dialog);
1995
1996 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1997 gtk_widget_destroy(dialog);
1998 return 1;
1999 }
2000
Jens Axboe62bc9372012-03-07 11:45:07 +01002001 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002002 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2003
Jens Axboe62bc9372012-03-07 11:45:07 +01002004 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002005 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2006 *type = Fio_client_ipv4;
2007 else if (!strncmp(typeentry, "IPv6", 4))
2008 *type = Fio_client_ipv6;
2009 else
2010 *type = Fio_client_socket;
2011 g_free(typeentry);
2012
Jens Axboe62bc9372012-03-07 11:45:07 +01002013 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002014
Jens Axboea7a42ce2012-03-02 13:12:04 +01002015 gtk_widget_destroy(dialog);
2016 return 0;
2017}
2018
Jens Axboe2f99deb2012-03-09 14:37:29 +01002019static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002020{
2021 struct gfio_client *gc;
2022
2023 gc = malloc(sizeof(*gc));
2024 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002025 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002026 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002027
Jens Axboe2f99deb2012-03-09 14:37:29 +01002028 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002029
2030 client->client_data = gc;
2031}
2032
Jens Axboe2f99deb2012-03-09 14:37:29 +01002033static GtkWidget *new_client_page(struct gui_entry *ge);
2034
2035static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2036{
2037 struct gui_entry *ge;
2038
2039 ge = malloc(sizeof(*ge));
2040 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002041 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002042 INIT_FLIST_HEAD(&ge->list);
2043 flist_add_tail(&ge->list, &ui->list);
2044 ge->ui = ui;
2045 return ge;
2046}
2047
2048/*
2049 * FIXME: need more handling here
2050 */
2051static void ge_destroy(GtkWidget *w, gpointer data)
2052{
2053 struct gui_entry *ge = data;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002054 struct gfio_client *gc = ge->client;
2055
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002056 if (gc && gc->client) {
Jens Axboe85dd01e2012-03-12 14:33:16 +01002057 if (ge->state >= GE_STATE_CONNECTED)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002058 fio_client_terminate(gc->client);
2059
Jens Axboe343cb4a2012-03-09 17:16:51 +01002060 fio_put_client(gc->client);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002061 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002062
2063 flist_del(&ge->list);
2064 free(ge);
2065}
2066
2067static struct gui_entry *get_new_ge_with_tab(const char *name)
2068{
2069 struct gui_entry *ge;
2070
2071 ge = alloc_new_gui_entry(&main_ui);
2072
2073 ge->vbox = new_client_page(ge);
2074 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
2075
2076 ge->page_label = gtk_label_new(name);
2077 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2078
2079 gtk_widget_show_all(main_ui.window);
2080 return ge;
2081}
2082
2083static void file_new(GtkWidget *w, gpointer data)
2084{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002085 struct gui *ui = (struct gui *) data;
2086 struct gui_entry *ge;
2087
2088 ge = get_new_ge_with_tab("Untitled");
2089 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002090}
2091
2092/*
2093 * Return the 'ge' corresponding to the tab. If the active tab is the
2094 * main tab, open a new tab.
2095 */
Jens Axboecf4b0442012-03-12 15:09:42 +01002096static struct gui_entry *get_ge_from_page(gint cur_page)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002097{
2098 struct flist_head *entry;
2099 struct gui_entry *ge;
2100
2101 if (!cur_page)
2102 return get_new_ge_with_tab("Untitled");
2103
2104 flist_for_each(entry, &main_ui.list) {
2105 ge = flist_entry(entry, struct gui_entry, list);
2106 if (ge->page_num == cur_page)
2107 return ge;
2108 }
2109
2110 return NULL;
2111}
2112
Jens Axboe85dd01e2012-03-12 14:33:16 +01002113static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2114{
2115 gint cur_page;
2116
2117 /*
2118 * Main tab is tab 0, so any current page other than 0 holds
2119 * a ge entry.
2120 */
2121 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2122 if (cur_page)
2123 return get_ge_from_page(cur_page);
2124
2125 return NULL;
2126}
2127
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002128static void file_close(GtkWidget *w, gpointer data)
2129{
2130 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002131 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002132
2133 /*
2134 * Can't close the main tab
2135 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002136 ge = get_ge_from_cur_tab(ui);
2137 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002138 gtk_widget_destroy(ge->vbox);
2139 return;
2140 }
2141
Jens Axboef5c67262012-03-13 08:20:41 +01002142 if (!flist_empty(&ui->list)) {
2143 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2144 return;
2145 }
2146
2147 gtk_main_quit();
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002148}
2149
Jens Axboe0420ba62012-02-29 11:16:52 +01002150static void file_open(GtkWidget *w, gpointer data)
2151{
Jens Axboe63a130b2012-03-06 20:08:59 +01002152 struct gui *ui = data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002153 GtkWidget *dialog;
Jens Axboe0420ba62012-02-29 11:16:52 +01002154 GSList *filenames, *fn_glist;
2155 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002156 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002157 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002158 struct gui_entry *ge;
2159 gint cur_page;
2160
2161 /*
2162 * Creates new tab if current tab is the main window, or the
2163 * current tab already has a client.
2164 */
2165 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2166 ge = get_ge_from_page(cur_page);
2167 if (ge->client)
2168 ge = get_new_ge_with_tab("Untitled");
2169
2170 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002171
2172 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002173 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002174 GTK_FILE_CHOOSER_ACTION_OPEN,
2175 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2176 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2177 NULL);
2178 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2179
2180 filter = gtk_file_filter_new();
2181 gtk_file_filter_add_pattern(filter, "*.fio");
2182 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002183 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe0420ba62012-02-29 11:16:52 +01002184 gtk_file_filter_add_mime_type(filter, "text/fio");
2185 gtk_file_filter_set_name(filter, "Fio job file");
2186 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2187
2188 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2189 gtk_widget_destroy(dialog);
2190 return;
2191 }
2192
2193 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002194
2195 gtk_widget_destroy(dialog);
2196
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002197 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01002198 goto err;
2199
Jens Axboe0420ba62012-02-29 11:16:52 +01002200 filenames = fn_glist;
2201 while (filenames != NULL) {
Jens Axboee0681f32012-03-06 12:14:42 +01002202 struct fio_client *client;
2203
Jens Axboe2f99deb2012-03-09 14:37:29 +01002204 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2205 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2206 ge->nr_job_files++;
Jens Axboe0420ba62012-02-29 11:16:52 +01002207
Jens Axboee0681f32012-03-06 12:14:42 +01002208 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2209 if (!client) {
Jens Axboedf06f222012-03-02 13:32:04 +01002210 GError *error;
2211
2212 error = g_error_new(g_quark_from_string("fio"), 1,
2213 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01002214 report_error(error);
2215 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01002216 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002217 gfio_client_added(ge, client);
Jens Axboe0420ba62012-02-29 11:16:52 +01002218
2219 g_free(filenames->data);
2220 filenames = g_slist_next(filenames);
2221 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01002222 free(host);
Jens Axboe63a130b2012-03-06 20:08:59 +01002223
2224 if (server_start)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002225 gfio_start_server();
Jens Axboea7a42ce2012-03-02 13:12:04 +01002226err:
Jens Axboe0420ba62012-02-29 11:16:52 +01002227 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002228}
2229
2230static void file_save(GtkWidget *w, gpointer data)
2231{
Jens Axboe63a130b2012-03-06 20:08:59 +01002232 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002233 GtkWidget *dialog;
2234
2235 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002236 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002237 GTK_FILE_CHOOSER_ACTION_SAVE,
2238 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2239 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2240 NULL);
2241
2242 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2243 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2244
2245 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2246 char *filename;
2247
2248 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2249 // save_job_file(filename);
2250 g_free(filename);
2251 }
2252 gtk_widget_destroy(dialog);
2253}
2254
Jens Axboe9b260bd2012-03-06 11:02:52 +01002255static void view_log_destroy(GtkWidget *w, gpointer data)
2256{
2257 struct gui *ui = (struct gui *) data;
2258
2259 gtk_widget_ref(ui->log_tree);
2260 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2261 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002262 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002263}
2264
2265static void view_log(GtkWidget *w, gpointer data)
2266{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002267 GtkWidget *win, *scroll, *vbox, *box;
2268 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002269
Jens Axboe4cbe7212012-03-06 13:36:17 +01002270 if (ui->log_view)
2271 return;
2272
2273 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002274 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002275 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002276
Jens Axboe4cbe7212012-03-06 13:36:17 +01002277 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002278
Jens Axboe4cbe7212012-03-06 13:36:17 +01002279 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2280
2281 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2282
2283 box = gtk_hbox_new(TRUE, 0);
2284 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2285 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2286 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2287
2288 vbox = gtk_vbox_new(TRUE, 5);
2289 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2290
2291 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002292 gtk_widget_show_all(win);
2293}
2294
Jens Axboe85dd01e2012-03-12 14:33:16 +01002295static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002296{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002297 struct gui *ui = (struct gui *) data;
2298 struct gui_entry *ge;
2299
2300 ge = get_ge_from_cur_tab(ui);
2301 if (ge)
2302 connect_clicked(w, ge);
2303}
2304
2305static void send_job_entry(GtkWidget *w, gpointer data)
2306{
2307 struct gui *ui = (struct gui *) data;
2308 struct gui_entry *ge;
2309
2310 ge = get_ge_from_cur_tab(ui);
2311 if (ge)
2312 send_clicked(w, ge);
2313
2314}
2315
2316static void edit_job_entry(GtkWidget *w, gpointer data)
2317{
2318}
2319
2320static void start_job_entry(GtkWidget *w, gpointer data)
2321{
2322 struct gui *ui = (struct gui *) data;
2323 struct gui_entry *ge;
2324
2325 ge = get_ge_from_cur_tab(ui);
2326 if (ge)
2327 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002328}
2329
Jens Axboe8577f4f2012-03-09 19:28:27 +01002330static void __update_graph_limits(struct gfio_graphs *g)
2331{
2332 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2333 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2334}
2335
2336static void update_graph_limits(void)
2337{
2338 struct flist_head *entry;
2339 struct gui_entry *ge;
2340
2341 __update_graph_limits(&main_ui.graphs);
2342
2343 flist_for_each(entry, &main_ui.list) {
2344 ge = flist_entry(entry, struct gui_entry, list);
2345 __update_graph_limits(&ge->graphs);
2346 }
2347}
2348
Jens Axboe46974a72012-03-02 19:34:13 +01002349static void preferences(GtkWidget *w, gpointer data)
2350{
Jens Axboef3e84402012-03-07 13:14:32 +01002351 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002352 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002353 int i;
2354
2355 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002356 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002357 GTK_DIALOG_DESTROY_WITH_PARENT,
2358 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2359 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2360 NULL);
2361
Jens Axboe8577f4f2012-03-09 19:28:27 +01002362 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002363 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2364 vbox = gtk_vbox_new(FALSE, 6);
2365 gtk_container_add(GTK_CONTAINER(frame), vbox);
2366
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002367 hbox = gtk_hbox_new(FALSE, 5);
2368 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2369 entry = gtk_label_new("Font face to use for graph labels");
2370 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2371
Jens Axboef3e84402012-03-07 13:14:32 +01002372 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002373 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002374
Jens Axboe8577f4f2012-03-09 19:28:27 +01002375 box = gtk_vbox_new(FALSE, 6);
2376 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2377
2378 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002379 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002380 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2381 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2382
Jens Axboec05d9052012-03-11 13:05:35 +01002383 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002384
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002385 box = gtk_vbox_new(FALSE, 6);
2386 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2387
2388 hbox = gtk_hbox_new(FALSE, 5);
2389 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2390 entry = gtk_label_new("Client ETA request interval (msec)");
2391 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2392
2393 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002394 frame = gtk_frame_new("Debug logging");
2395 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2396 vbox = gtk_vbox_new(FALSE, 6);
2397 gtk_container_add(GTK_CONTAINER(frame), vbox);
2398
2399 box = gtk_hbox_new(FALSE, 6);
2400 gtk_container_add(GTK_CONTAINER(vbox), box);
2401
2402 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2403
2404 for (i = 0; i < FD_DEBUG_MAX; i++) {
2405 if (i == 7) {
2406 box = gtk_hbox_new(FALSE, 6);
2407 gtk_container_add(GTK_CONTAINER(vbox), box);
2408 }
2409
2410
2411 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2412 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2413 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2414 }
2415
Jens Axboe46974a72012-03-02 19:34:13 +01002416 gtk_widget_show_all(dialog);
2417
2418 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2419 gtk_widget_destroy(dialog);
2420 return;
2421 }
2422
2423 for (i = 0; i < FD_DEBUG_MAX; i++) {
2424 int set;
2425
2426 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2427 if (set)
2428 fio_debug |= (1UL << i);
2429 }
2430
Jens Axboef3e84402012-03-07 13:14:32 +01002431 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002432 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2433 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002434 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002435
Jens Axboe46974a72012-03-02 19:34:13 +01002436 gtk_widget_destroy(dialog);
2437}
2438
Jens Axboe0420ba62012-02-29 11:16:52 +01002439static void about_dialog(GtkWidget *w, gpointer data)
2440{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002441 const char *authors[] = {
2442 "Jens Axboe <axboe@kernel.dk>",
2443 "Stephen Carmeron <stephenmcameron@gmail.com>",
2444 NULL
2445 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002446 const char *license[] = {
2447 "Fio is free software; you can redistribute it and/or modify "
2448 "it under the terms of the GNU General Public License as published by "
2449 "the Free Software Foundation; either version 2 of the License, or "
2450 "(at your option) any later version.\n",
2451 "Fio is distributed in the hope that it will be useful, "
2452 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2453 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2454 "GNU General Public License for more details.\n",
2455 "You should have received a copy of the GNU General Public License "
2456 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2457 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2458 };
2459 char *license_trans;
2460
2461 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2462 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002463
Jens Axboe0420ba62012-02-29 11:16:52 +01002464 gtk_show_about_dialog(NULL,
2465 "program-name", "gfio",
2466 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002467 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002468 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2469 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002470 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002471 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002472 "logo-icon-name", "fio",
2473 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002474 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002475 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002476
Jens Axboe2f99deb2012-03-09 14:37:29 +01002477 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002478}
2479
2480static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002481 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002482 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002483 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002484 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002485 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002486 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002487 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2488 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2489 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002490 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002491 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2492 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2493 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2494 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002495 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2496 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002497};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002498static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002499
2500static const gchar *ui_string = " \
2501 <ui> \
2502 <menubar name=\"MainMenu\"> \
2503 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002504 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002505 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002506 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002507 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2508 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002509 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002510 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2511 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002512 <placeholder name=\"FileRecentFiles\"/> \
2513 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002514 <menuitem name=\"Quit\" action=\"Quit\" /> \
2515 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002516 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002517 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002518 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002519 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2520 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002521 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002522 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002523 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002524 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2525 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2526 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002527 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2528 <menuitem name=\"About\" action=\"About\" /> \
2529 </menu> \
2530 </menubar> \
2531 </ui> \
2532";
2533
Jens Axboe02421e62012-03-12 12:05:50 +01002534static void set_job_menu_visible(struct gui *ui, int visible)
2535{
2536 GtkWidget *job;
2537
2538 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2539 gtk_widget_set_sensitive(job, visible);
2540}
2541
Jens Axboe4cbe7212012-03-06 13:36:17 +01002542static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2543 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002544{
2545 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2546 GError *error = 0;
2547
2548 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002549 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002550
2551 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2552 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2553
2554 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002555
Jens Axboe0420ba62012-02-29 11:16:52 +01002556 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2557}
2558
2559void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2560 GtkWidget *vbox, GtkUIManager *ui_manager)
2561{
2562 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2563}
2564
Jens Axboec80b74b2012-03-12 10:23:28 +01002565static void combo_entry_changed(GtkComboBox *box, gpointer data)
2566{
2567 struct gui_entry *ge = (struct gui_entry *) data;
2568 gint index;
2569
2570 index = gtk_combo_box_get_active(box);
2571
2572 multitext_set_entry(&ge->eta.iotype, index);
2573 multitext_set_entry(&ge->eta.ioengine, index);
2574 multitext_set_entry(&ge->eta.iodepth, index);
2575}
2576
2577static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2578{
2579 struct gui_entry *ge = (struct gui_entry *) data;
2580
2581 multitext_free(&ge->eta.iotype);
2582 multitext_free(&ge->eta.ioengine);
2583 multitext_free(&ge->eta.iodepth);
2584}
2585
Jens Axboe2f99deb2012-03-09 14:37:29 +01002586static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002587{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002588 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002589 GdkColor white;
Jens Axboe0420ba62012-02-29 11:16:52 +01002590
Jens Axboe2f99deb2012-03-09 14:37:29 +01002591 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002592
Jens Axboe2f99deb2012-03-09 14:37:29 +01002593 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2594 ge->topvbox = gtk_vbox_new(FALSE, 3);
2595 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2596 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002597
Jens Axboe3e47bd22012-02-29 13:45:02 +01002598 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002599 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002600 probe_frame = gtk_vbox_new(FALSE, 3);
2601 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2602
2603 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002604 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2605 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2606 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2607 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2608 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002609
Jens Axboe3e47bd22012-02-29 13:45:02 +01002610 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002611 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2612
Jens Axboe3863d1a2012-03-09 17:39:05 +01002613 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002614 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2615 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2616 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2617 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2618 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002619 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2620 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2621
2622 probe_box = gtk_hbox_new(FALSE, 3);
2623 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2624 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2625 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2626 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2627 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2628
2629 /*
2630 * Only add this if we have a commit rate
2631 */
2632#if 0
2633 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002634 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002635
Jens Axboe2f99deb2012-03-09 14:37:29 +01002636 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2637 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2638
2639 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2640 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2641#endif
2642
2643 /*
2644 * Set up a drawing area and IOPS and bandwidth graphs
2645 */
2646 gdk_color_parse("white", &white);
2647 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002648 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002649 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002650 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2651 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2652 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2653 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2654 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2655 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2656 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2657 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2658 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2659 ge->graphs.drawing_area);
2660 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2661 TRUE, TRUE, 0);
2662
2663 setup_graphs(&ge->graphs);
2664
2665 /*
2666 * Set up alignments for widgets at the bottom of ui,
2667 * align bottom left, expand horizontally but not vertically
2668 */
2669 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2670 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2671 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2672 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2673 FALSE, FALSE, 0);
2674
2675 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2676
2677 /*
2678 * Set up thread status progress bar
2679 */
2680 ge->thread_status_pb = gtk_progress_bar_new();
2681 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2682 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2683 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2684
2685
2686 return main_vbox;
2687}
2688
2689static GtkWidget *new_main_page(struct gui *ui)
2690{
2691 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2692 GdkColor white;
2693
2694 main_vbox = gtk_vbox_new(FALSE, 3);
2695
2696 /*
2697 * Set up alignments for widgets at the top of ui,
2698 * align top left, expand horizontally but not vertically
2699 */
2700 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2701 ui->topvbox = gtk_vbox_new(FALSE, 0);
2702 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2703 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2704
2705 probe = gtk_frame_new("Run statistics");
2706 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2707 probe_frame = gtk_vbox_new(FALSE, 3);
2708 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002709
2710 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002711 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01002712 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01002713 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2714 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2715 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2716 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002717
2718 /*
2719 * Only add this if we have a commit rate
2720 */
2721#if 0
2722 probe_box = gtk_hbox_new(FALSE, 3);
2723 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2724
Jens Axboe3e47bd22012-02-29 13:45:02 +01002725 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2726 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2727
Jens Axboe3e47bd22012-02-29 13:45:02 +01002728 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2729 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002730#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002731
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002732 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002733 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002734 */
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002735 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002736 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002737 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002738 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002739 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2740 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2741 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2742 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2743 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002744 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2745 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2746 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002747 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002748 ui->graphs.drawing_area);
2749 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002750 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002751
Jens Axboe2f99deb2012-03-09 14:37:29 +01002752 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002753
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002754 /*
2755 * Set up alignments for widgets at the bottom of ui,
2756 * align bottom left, expand horizontally but not vertically
2757 */
2758 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2759 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2760 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002761 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002762 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002763
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002764 /*
2765 * Set up thread status progress bar
2766 */
2767 ui->thread_status_pb = gtk_progress_bar_new();
2768 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002769 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002770 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2771
Jens Axboe2f99deb2012-03-09 14:37:29 +01002772 return main_vbox;
2773}
2774
2775static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2776 guint page, gpointer data)
2777
2778{
Jens Axboe02421e62012-03-12 12:05:50 +01002779 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002780 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01002781
Jens Axboe85dd01e2012-03-12 14:33:16 +01002782 if (!page) {
2783 set_job_menu_visible(ui, 0);
2784 return TRUE;
2785 }
2786
2787 set_job_menu_visible(ui, 1);
2788 ge = get_ge_from_page(page);
2789 if (ge)
2790 update_button_states(ui, ge);
2791
Jens Axboe2f99deb2012-03-09 14:37:29 +01002792 return TRUE;
2793}
2794
2795static void init_ui(int *argc, char **argv[], struct gui *ui)
2796{
2797 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01002798 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002799
2800 /* Magical g*thread incantation, you just need this thread stuff.
2801 * Without it, the update that happens in gfio_update_thread_status
2802 * doesn't really happen in a timely fashion, you need expose events
2803 */
2804 if (!g_thread_supported())
2805 g_thread_init(NULL);
2806 gdk_threads_init();
2807
2808 gtk_init(argc, argv);
2809 settings = gtk_settings_get_default();
2810 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2811 g_type_init();
2812
2813 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2814 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2815 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2816
2817 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2818 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2819
2820 ui->vbox = gtk_vbox_new(FALSE, 0);
2821 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2822
Jens Axboe02421e62012-03-12 12:05:50 +01002823 ui->uimanager = gtk_ui_manager_new();
2824 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
2825 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002826
2827 ui->notebook = gtk_notebook_new();
2828 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01002829 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01002830 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002831 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2832
2833 vbox = new_main_page(ui);
2834
2835 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2836
Jens Axboe9b260bd2012-03-06 11:02:52 +01002837 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002838
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002839 gtk_widget_show_all(ui->window);
2840}
2841
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002842int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002843{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002844 if (initialize_fio(envp))
2845 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01002846 if (fio_init_options())
2847 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01002848
Jens Axboe2f99deb2012-03-09 14:37:29 +01002849 memset(&main_ui, 0, sizeof(main_ui));
2850 INIT_FLIST_HEAD(&main_ui.list);
2851
2852 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01002853
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002854 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002855 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002856 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002857 return 0;
2858}