blob: 40ef34c92a63663e1d9d52fc157e292534a1f5f1 [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
134/*
135 * Notebook entry
136 */
137struct gui_entry {
138 struct flist_head list;
139 struct gui *ui;
140
141 GtkWidget *vbox;
142 GtkWidget *topvbox;
143 GtkWidget *topalign;
144 GtkWidget *bottomalign;
Jens Axboec80b74b2012-03-12 10:23:28 +0100145 GtkWidget *job_notebook;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100146 GtkWidget *thread_status_pb;
147 GtkWidget *buttonbox;
Stephen M. Cameronf3074002012-02-24 08:17:30 +0100148 GtkWidget *button[ARRAYSIZE(buttonspeclist)];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +0100149 GtkWidget *scrolled_window;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100150 GtkWidget *notebook;
Jens Axboe0420ba62012-02-29 11:16:52 +0100151 GtkWidget *error_info_bar;
152 GtkWidget *error_label;
Jens Axboef9d40b42012-03-06 09:52:49 +0100153 GtkWidget *results_notebook;
154 GtkWidget *results_window;
Jens Axboe9b260bd2012-03-06 11:02:52 +0100155 GtkListStore *log_model;
156 GtkWidget *log_tree;
Jens Axboe4cbe7212012-03-06 13:36:17 +0100157 GtkWidget *log_view;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100158 struct gfio_graphs graphs;
Jens Axboe843ad232012-02-29 11:44:53 +0100159 struct probe_widget probe;
Jens Axboe3e47bd22012-02-29 13:45:02 +0100160 struct eta_widget eta;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100161 GtkWidget *page_label;
162 gint page_num;
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100163 int connected;
Jens Axboe0420ba62012-02-29 11:16:52 +0100164
Jens Axboeb9d2f302012-03-08 20:36:28 +0100165 struct gfio_client *client;
Jens Axboe0420ba62012-02-29 11:16:52 +0100166 int nr_job_files;
167 char **job_files;
Jens Axboe2f99deb2012-03-09 14:37:29 +0100168};
Stephen M. Cameronff1f3282012-02-24 08:17:30 +0100169
Jens Axboee0681f32012-03-06 12:14:42 +0100170struct gfio_client {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100171 struct gui_entry *ge;
Jens Axboeb9d2f302012-03-08 20:36:28 +0100172 struct fio_client *client;
Jens Axboee0681f32012-03-06 12:14:42 +0100173 GtkWidget *results_widget;
174 GtkWidget *disk_util_frame;
Jens Axboe6b79c802012-03-08 10:51:36 +0100175 GtkWidget *err_entry;
Jens Axboedcaeb602012-03-08 19:45:37 +0100176 unsigned int job_added;
177 struct thread_options o;
Jens Axboee0681f32012-03-06 12:14:42 +0100178};
179
Jens Axboe9988ca72012-03-09 15:14:06 +0100180static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
181static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +0100182void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +0100183
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100184static void iops_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
185{
186 switch (power_of_ten) {
187 case 9: graph_y_title(g, "Billions of IOs / sec");
188 break;
189 case 6: graph_y_title(g, "Millions of IOs / sec");
190 break;
191 case 3: graph_y_title(g, "Thousands of IOs / sec");
192 break;
193 case 0:
194 default: graph_y_title(g, "IOs / sec");
195 break;
196 }
197}
198
Jens Axboe2f99deb2012-03-09 14:37:29 +0100199static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100200{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100201 struct graph *g;
202
203 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
204 graph_title(g, "IOPS");
205 graph_x_title(g, "Time (secs)");
206 graph_y_title(g, "IOs / sec");
207 graph_add_label(g, "Read IOPS");
208 graph_add_label(g, "Write IOPS");
209 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
210 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +0100211 line_graph_set_data_count_limit(g, gfio_graph_limit);
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100212 graph_y_axis_unit_change_notify(g, iops_graph_y_axis_unit_change);
Stephen M. Camerondef0ac22012-03-12 07:32:57 +0100213 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100214 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100215}
216
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100217static void bandwidth_graph_y_axis_unit_change(struct graph *g, int power_of_ten)
218{
219 switch (power_of_ten) {
220 case 9: graph_y_title(g, "Petabytes / sec");
221 break;
222 case 6: graph_y_title(g, "Gigabytes / sec");
223 break;
224 case 3: graph_y_title(g, "Megabytes / sec");
225 break;
226 case 0:
227 default: graph_y_title(g, "Kilobytes / sec");
228 break;
229 }
230}
231
Jens Axboe2f99deb2012-03-09 14:37:29 +0100232static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100233{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100234 struct graph *g;
235
236 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
237 graph_title(g, "Bandwidth");
238 graph_x_title(g, "Time (secs)");
239 graph_y_title(g, "Kbytes / sec");
240 graph_add_label(g, "Read Bandwidth");
241 graph_add_label(g, "Write Bandwidth");
242 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
243 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
244 line_graph_set_data_count_limit(g, 100);
Stephen M. Cameronf0af9f82012-03-11 11:35:50 +0100245 graph_y_axis_unit_change_notify(g, bandwidth_graph_y_axis_unit_change);
Stephen M. Camerondef0ac22012-03-12 07:32:57 +0100246 graph_add_extra_space(g, 0.005, 0.005, 0.03, 0.03);
247
Jens Axboe2f99deb2012-03-09 14:37:29 +0100248 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100249}
250
Jens Axboe2f99deb2012-03-09 14:37:29 +0100251static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100252{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100253 g->iops_graph = setup_iops_graph();
254 g->bandwidth_graph = setup_bandwidth_graph();
255}
256
Jens Axboec80b74b2012-03-12 10:23:28 +0100257static void multitext_add_entry(struct multitext_widget *mt, const char *text)
258{
259 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
260 mt->text[mt->max_text] = strdup(text);
261 mt->max_text++;
262}
263
264static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
265{
266 if (index >= mt->max_text)
267 return;
Jens Axboeda185432012-03-12 11:05:46 +0100268 if (!mt->text || !mt->text[index])
Jens Axboec80b74b2012-03-12 10:23:28 +0100269 return;
270
271 mt->cur_text = index;
272 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
273}
274
275static void multitext_update_entry(struct multitext_widget *mt,
276 unsigned int index, const char *text)
277{
Jens Axboeda185432012-03-12 11:05:46 +0100278 if (!mt->text)
279 return;
280
Jens Axboec80b74b2012-03-12 10:23:28 +0100281 if (mt->text[index])
282 free(mt->text[index]);
283
284 mt->text[index] = strdup(text);
285 if (mt->cur_text == index)
286 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
287}
288
289static void multitext_free(struct multitext_widget *mt)
290{
291 int i;
292
293 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
294
295 for (i = 0; i < mt->max_text; i++) {
296 if (mt->text[i])
297 free(mt->text[i]);
298 }
299
300 free(mt->text);
301 mt->cur_text = -1;
302 mt->max_text = 0;
303}
304
Jens Axboe2f99deb2012-03-09 14:37:29 +0100305static void clear_ge_ui_info(struct gui_entry *ge)
306{
307 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
308 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
309 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
310 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100311#if 0
312 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100313 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100314#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100315 multitext_update_entry(&ge->eta.iotype, 0, "");
316 multitext_update_entry(&ge->eta.ioengine, 0, "");
317 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100318 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
319 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
320 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
321 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
322 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
323 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100324}
325
Jens Axboe3863d1a2012-03-09 17:39:05 +0100326static GtkWidget *new_combo_entry_in_frame(GtkWidget *box, const char *label)
327{
328 GtkWidget *entry, *frame;
329
330 frame = gtk_frame_new(label);
331 entry = gtk_combo_box_new_text();
332 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
333 gtk_container_add(GTK_CONTAINER(frame), entry);
334
335 return entry;
336}
337
Jens Axboe3650a3c2012-03-05 14:09:03 +0100338static GtkWidget *new_info_entry_in_frame(GtkWidget *box, const char *label)
339{
340 GtkWidget *entry, *frame;
341
342 frame = gtk_frame_new(label);
343 entry = gtk_entry_new();
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100344 gtk_entry_set_editable(GTK_ENTRY(entry), 0);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100345 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
346 gtk_container_add(GTK_CONTAINER(frame), entry);
347
348 return entry;
349}
350
351static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
352{
353 GtkWidget *label_widget;
354 GtkWidget *frame;
355
356 frame = gtk_frame_new(label);
357 label_widget = gtk_label_new(NULL);
358 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 3);
359 gtk_container_add(GTK_CONTAINER(frame), label_widget);
360
361 return label_widget;
362}
363
364static GtkWidget *create_spinbutton(GtkWidget *hbox, double min, double max, double defval)
365{
366 GtkWidget *button, *box;
367
368 box = gtk_hbox_new(FALSE, 3);
369 gtk_container_add(GTK_CONTAINER(hbox), box);
370
371 button = gtk_spin_button_new_with_range(min, max, 1.0);
372 gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
373
374 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(button), GTK_UPDATE_IF_VALID);
375 gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), defval);
376
377 return button;
378}
379
Jens Axboe2f99deb2012-03-09 14:37:29 +0100380static void gfio_set_connected(struct gui_entry *ge, int connected)
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100381{
382 if (connected) {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100383 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
384 ge->connected = 1;
385 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Disconnect");
386 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100387 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +0100388 ge->connected = 0;
389 gtk_button_set_label(GTK_BUTTON(ge->button[CONNECT_BUTTON]), "Connect");
390 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
391 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
392 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 1);
Jens Axboe3ec62ec2012-03-01 12:01:29 +0100393 }
394}
395
Jens Axboe3650a3c2012-03-05 14:09:03 +0100396static void label_set_int_value(GtkWidget *entry, unsigned int val)
397{
398 char tmp[80];
399
400 sprintf(tmp, "%u", val);
401 gtk_label_set_text(GTK_LABEL(entry), tmp);
402}
403
404static void entry_set_int_value(GtkWidget *entry, unsigned int val)
405{
406 char tmp[80];
407
408 sprintf(tmp, "%u", val);
409 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
410}
411
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100412static void show_info_dialog(struct gui *ui, const char *title,
413 const char *message)
414{
415 GtkWidget *dialog, *content, *label;
416
417 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
418 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
419 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
420
421 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
422 label = gtk_label_new(message);
423 gtk_container_add(GTK_CONTAINER(content), label);
424 gtk_widget_show_all(dialog);
425 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
426 gtk_dialog_run(GTK_DIALOG(dialog));
427 gtk_widget_destroy(dialog);
428}
429
Jens Axboea2697902012-03-05 16:43:49 +0100430#define ALIGN_LEFT 1
431#define ALIGN_RIGHT 2
432#define INVISIBLE 4
433#define UNSORTABLE 8
434
435GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
436{
437 GtkCellRenderer *renderer;
438 GtkTreeViewColumn *col;
439 double xalign = 0.0; /* left as default */
440 PangoAlignment align;
441 gboolean visible;
442
443 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
444 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
445 PANGO_ALIGN_CENTER;
446 visible = !(flags & INVISIBLE);
447
448 renderer = gtk_cell_renderer_text_new();
449 col = gtk_tree_view_column_new();
450
451 gtk_tree_view_column_set_title(col, title);
452 if (!(flags & UNSORTABLE))
453 gtk_tree_view_column_set_sort_column_id(col, index);
454 gtk_tree_view_column_set_resizable(col, TRUE);
455 gtk_tree_view_column_pack_start(col, renderer, TRUE);
456 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
457 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
458 switch (align) {
459 case PANGO_ALIGN_LEFT:
460 xalign = 0.0;
461 break;
462 case PANGO_ALIGN_CENTER:
463 xalign = 0.5;
464 break;
465 case PANGO_ALIGN_RIGHT:
466 xalign = 1.0;
467 break;
468 }
469 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
470 gtk_tree_view_column_set_visible(col, visible);
471 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
472 return col;
473}
474
Jens Axboe9b260bd2012-03-06 11:02:52 +0100475static void gfio_ui_setup_log(struct gui *ui)
476{
477 GtkTreeSelection *selection;
478 GtkListStore *model;
479 GtkWidget *tree_view;
480
481 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
482
483 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
484 gtk_widget_set_can_focus(tree_view, FALSE);
485
486 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
487 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100488 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
489 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100490
491 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
492 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
493 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100494 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100495
496 ui->log_model = model;
497 ui->log_tree = tree_view;
498}
499
Jens Axboea2697902012-03-05 16:43:49 +0100500static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
501 fio_fp64_t *plist,
502 unsigned int len,
503 const char *base,
504 unsigned int scale)
505{
506 GType types[FIO_IO_U_LIST_MAX_LEN];
507 GtkWidget *tree_view;
508 GtkTreeSelection *selection;
509 GtkListStore *model;
510 GtkTreeIter iter;
511 int i;
512
513 for (i = 0; i < len; i++)
514 types[i] = G_TYPE_INT;
515
516 model = gtk_list_store_newv(len, types);
517
518 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
519 gtk_widget_set_can_focus(tree_view, FALSE);
520
Jens Axboe661f7412012-03-06 13:55:45 +0100521 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
522 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
523
Jens Axboea2697902012-03-05 16:43:49 +0100524 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
525 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
526
527 for (i = 0; i < len; i++) {
528 char fbuf[8];
529
530 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
531 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
532 }
533
534 gtk_list_store_append(model, &iter);
535
Jens Axboee0681f32012-03-06 12:14:42 +0100536 for (i = 0; i < len; i++) {
537 if (scale)
538 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100539 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100540 }
Jens Axboea2697902012-03-05 16:43:49 +0100541
542 return tree_view;
543}
544
545static void gfio_show_clat_percentiles(GtkWidget *vbox, struct thread_stat *ts,
546 int ddir)
547{
548 unsigned int *io_u_plat = ts->io_u_plat[ddir];
549 unsigned long nr = ts->clat_stat[ddir].samples;
550 fio_fp64_t *plist = ts->percentile_list;
551 unsigned int *ovals, len, minv, maxv, scale_down;
552 const char *base;
553 GtkWidget *tree_view, *frame, *hbox;
554 char tmp[64];
555
556 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
557 if (!len)
558 goto out;
559
560 /*
561 * We default to usecs, but if the value range is such that we
562 * should scale down to msecs, do that.
563 */
564 if (minv > 2000 && maxv > 99999) {
565 scale_down = 1;
566 base = "msec";
567 } else {
568 scale_down = 0;
569 base = "usec";
570 }
571
572 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
573
574 sprintf(tmp, "Completion percentiles (%s)", base);
575 frame = gtk_frame_new(tmp);
576 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
577
578 hbox = gtk_hbox_new(FALSE, 3);
579 gtk_container_add(GTK_CONTAINER(frame), hbox);
580
581 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
582out:
583 if (ovals)
584 free(ovals);
585}
586
Jens Axboe3650a3c2012-03-05 14:09:03 +0100587static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
588 unsigned long max, double mean, double dev)
589{
590 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100591 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100592 char *minp, *maxp;
593 char tmp[64];
594
595 if (!usec_to_msec(&min, &max, &mean, &dev))
596 base = "(msec)";
597
598 minp = num2str(min, 6, 1, 0);
599 maxp = num2str(max, 6, 1, 0);
600
Jens Axboe3650a3c2012-03-05 14:09:03 +0100601 sprintf(tmp, "%s %s", name, base);
602 frame = gtk_frame_new(tmp);
603 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
604
Jens Axboe3650a3c2012-03-05 14:09:03 +0100605 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100606 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100607
608 label = new_info_label_in_frame(hbox, "Minimum");
609 gtk_label_set_text(GTK_LABEL(label), minp);
610 label = new_info_label_in_frame(hbox, "Maximum");
611 gtk_label_set_text(GTK_LABEL(label), maxp);
612 label = new_info_label_in_frame(hbox, "Average");
613 sprintf(tmp, "%5.02f", mean);
614 gtk_label_set_text(GTK_LABEL(label), tmp);
615 label = new_info_label_in_frame(hbox, "Standard deviation");
616 sprintf(tmp, "%5.02f", dev);
617 gtk_label_set_text(GTK_LABEL(label), tmp);
618
619 free(minp);
620 free(maxp);
621
622}
623
Jens Axboeca850992012-03-05 20:04:43 +0100624#define GFIO_CLAT 1
625#define GFIO_SLAT 2
626#define GFIO_LAT 4
627
Jens Axboe3650a3c2012-03-05 14:09:03 +0100628static void gfio_show_ddir_status(GtkWidget *mbox, struct group_run_stats *rs,
629 struct thread_stat *ts, int ddir)
630{
631 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100632 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100633 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100634 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100635 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100636 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100637 char *io_p, *bw_p, *iops_p;
638 int i2p;
639
640 if (!ts->runtime[ddir])
641 return;
642
643 i2p = is_power_of_2(rs->kb_base);
644 runt = ts->runtime[ddir];
645
646 bw = (1000 * ts->io_bytes[ddir]) / runt;
647 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
648 bw_p = num2str(bw, 6, 1, i2p);
649
650 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
651 iops_p = num2str(iops, 6, 1, 0);
652
653 box = gtk_hbox_new(FALSE, 3);
654 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
655
656 frame = gtk_frame_new(ddir_label[ddir]);
657 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
658
Jens Axboe0b761302012-03-05 20:44:11 +0100659 main_vbox = gtk_vbox_new(FALSE, 3);
660 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100661
662 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100663 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100664
665 label = new_info_label_in_frame(box, "IO");
666 gtk_label_set_text(GTK_LABEL(label), io_p);
667 label = new_info_label_in_frame(box, "Bandwidth");
668 gtk_label_set_text(GTK_LABEL(label), bw_p);
669 label = new_info_label_in_frame(box, "IOPS");
670 gtk_label_set_text(GTK_LABEL(label), iops_p);
671 label = new_info_label_in_frame(box, "Runtime (msec)");
672 label_set_int_value(label, ts->runtime[ddir]);
673
Jens Axboee0681f32012-03-06 12:14:42 +0100674 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100675 double p_of_agg = 100.0;
676 const char *bw_str = "KB";
677 char tmp[32];
678
679 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100680 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100681 if (p_of_agg > 100.0)
682 p_of_agg = 100.0;
683 }
684
Jens Axboee0681f32012-03-06 12:14:42 +0100685 if (mean[0] > 999999.9) {
686 min[0] /= 1000.0;
687 max[0] /= 1000.0;
688 mean[0] /= 1000.0;
689 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100690 bw_str = "MB";
691 }
692
Jens Axboe0b761302012-03-05 20:44:11 +0100693 sprintf(tmp, "Bandwidth (%s)", bw_str);
694 frame = gtk_frame_new(tmp);
695 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100696
Jens Axboe0b761302012-03-05 20:44:11 +0100697 box = gtk_hbox_new(FALSE, 3);
698 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100699
Jens Axboe0b761302012-03-05 20:44:11 +0100700 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100701 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100702 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100703 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100704 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100705 sprintf(tmp, "%3.2f%%", p_of_agg);
706 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100707 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100708 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100709 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100710 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100711 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100712 gtk_label_set_text(GTK_LABEL(label), tmp);
713 }
714
Jens Axboee0681f32012-03-06 12:14:42 +0100715 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100716 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100717 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100718 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100719 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100720 flags |= GFIO_LAT;
721
722 if (flags) {
723 frame = gtk_frame_new("Latency");
724 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
725
726 vbox = gtk_vbox_new(FALSE, 3);
727 gtk_container_add(GTK_CONTAINER(frame), vbox);
728
729 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100730 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100731 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100732 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100733 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100734 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100735 }
736
737 if (ts->clat_percentiles)
738 gfio_show_clat_percentiles(main_vbox, ts, ddir);
739
740
Jens Axboe3650a3c2012-03-05 14:09:03 +0100741 free(io_p);
742 free(bw_p);
743 free(iops_p);
744}
745
Jens Axboee5bd1342012-03-05 21:38:12 +0100746static GtkWidget *gfio_output_lat_buckets(double *lat, unsigned int num,
747 const char **labels)
748{
749 GtkWidget *tree_view;
750 GtkTreeSelection *selection;
751 GtkListStore *model;
752 GtkTreeIter iter;
753 GType *types;
754 int i, skipped;
755
756 /*
757 * Check if all are empty, in which case don't bother
758 */
759 for (i = 0, skipped = 0; i < num; i++)
760 if (lat[i] <= 0.0)
761 skipped++;
762
763 if (skipped == num)
764 return NULL;
765
766 types = malloc(num * sizeof(GType));
767
768 for (i = 0; i < num; i++)
769 types[i] = G_TYPE_STRING;
770
771 model = gtk_list_store_newv(num, types);
772 free(types);
773 types = NULL;
774
775 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
776 gtk_widget_set_can_focus(tree_view, FALSE);
777
Jens Axboe661f7412012-03-06 13:55:45 +0100778 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
779 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
780
Jens Axboee5bd1342012-03-05 21:38:12 +0100781 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
782 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
783
784 for (i = 0; i < num; i++)
785 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
786
787 gtk_list_store_append(model, &iter);
788
789 for (i = 0; i < num; i++) {
790 char fbuf[32];
791
792 if (lat[i] <= 0.0)
793 sprintf(fbuf, "0.00");
794 else
795 sprintf(fbuf, "%3.2f%%", lat[i]);
796
797 gtk_list_store_set(model, &iter, i, fbuf, -1);
798 }
799
800 return tree_view;
801}
802
803static void gfio_show_latency_buckets(GtkWidget *vbox, struct thread_stat *ts)
804{
805 GtkWidget *box, *frame, *tree_view;
806 double io_u_lat_u[FIO_IO_U_LAT_U_NR];
807 double io_u_lat_m[FIO_IO_U_LAT_M_NR];
808 const char *uranges[] = { "2", "4", "10", "20", "50", "100",
809 "250", "500", "750", "1000", };
810 const char *mranges[] = { "2", "4", "10", "20", "50", "100",
811 "250", "500", "750", "1000", "2000",
812 ">= 2000", };
813
814 stat_calc_lat_u(ts, io_u_lat_u);
815 stat_calc_lat_m(ts, io_u_lat_m);
816
817 tree_view = gfio_output_lat_buckets(io_u_lat_u, FIO_IO_U_LAT_U_NR, uranges);
818 if (tree_view) {
819 frame = gtk_frame_new("Latency buckets (usec)");
820 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
821
822 box = gtk_hbox_new(FALSE, 3);
823 gtk_container_add(GTK_CONTAINER(frame), box);
824 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
825 }
826
827 tree_view = gfio_output_lat_buckets(io_u_lat_m, FIO_IO_U_LAT_M_NR, mranges);
828 if (tree_view) {
829 frame = gtk_frame_new("Latency buckets (msec)");
830 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
831
832 box = gtk_hbox_new(FALSE, 3);
833 gtk_container_add(GTK_CONTAINER(frame), box);
834 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
835 }
836}
837
Jens Axboe2e331012012-03-05 22:07:54 +0100838static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
839{
840 GtkWidget *box, *frame, *entry;
841 double usr_cpu, sys_cpu;
842 unsigned long runtime;
843 char tmp[32];
844
845 runtime = ts->total_run_time;
846 if (runtime) {
847 double runt = (double) runtime;
848
849 usr_cpu = (double) ts->usr_time * 100 / runt;
850 sys_cpu = (double) ts->sys_time * 100 / runt;
851 } else {
852 usr_cpu = 0;
853 sys_cpu = 0;
854 }
855
856 frame = gtk_frame_new("OS resources");
857 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
858
859 box = gtk_hbox_new(FALSE, 3);
860 gtk_container_add(GTK_CONTAINER(frame), box);
861
862 entry = new_info_entry_in_frame(box, "User CPU");
863 sprintf(tmp, "%3.2f%%", usr_cpu);
864 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
865 entry = new_info_entry_in_frame(box, "System CPU");
866 sprintf(tmp, "%3.2f%%", sys_cpu);
867 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
868 entry = new_info_entry_in_frame(box, "Context switches");
869 entry_set_int_value(entry, ts->ctx);
870 entry = new_info_entry_in_frame(box, "Major faults");
871 entry_set_int_value(entry, ts->majf);
872 entry = new_info_entry_in_frame(box, "Minor faults");
873 entry_set_int_value(entry, ts->minf);
874}
Jens Axboe19998db2012-03-06 09:17:59 +0100875static void gfio_add_sc_depths_tree(GtkListStore *model,
876 struct thread_stat *ts, unsigned int len,
877 int submit)
878{
879 double io_u_dist[FIO_IO_U_MAP_NR];
880 GtkTreeIter iter;
881 /* Bits 0, and 3-8 */
882 const int add_mask = 0x1f9;
883 int i, j;
884
885 if (submit)
886 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
887 else
888 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
889
890 gtk_list_store_append(model, &iter);
891
892 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
893
894 for (i = 1, j = 0; i < len; i++) {
895 char fbuf[32];
896
897 if (!(add_mask & (1UL << (i - 1))))
898 sprintf(fbuf, "0.0%%");
899 else {
900 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
901 j++;
902 }
903
904 gtk_list_store_set(model, &iter, i, fbuf, -1);
905 }
906
907}
908
909static void gfio_add_total_depths_tree(GtkListStore *model,
910 struct thread_stat *ts, unsigned int len)
911{
912 double io_u_dist[FIO_IO_U_MAP_NR];
913 GtkTreeIter iter;
914 /* Bits 1-6, and 8 */
915 const int add_mask = 0x17e;
916 int i, j;
917
918 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
919
920 gtk_list_store_append(model, &iter);
921
922 gtk_list_store_set(model, &iter, 0, "Total", -1);
923
924 for (i = 1, j = 0; i < len; i++) {
925 char fbuf[32];
926
927 if (!(add_mask & (1UL << (i - 1))))
928 sprintf(fbuf, "0.0%%");
929 else {
930 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
931 j++;
932 }
933
934 gtk_list_store_set(model, &iter, i, fbuf, -1);
935 }
936
937}
Jens Axboe2e331012012-03-05 22:07:54 +0100938
939static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
940{
Jens Axboe2e331012012-03-05 22:07:54 +0100941 GtkWidget *frame, *box, *tree_view;
942 GtkTreeSelection *selection;
943 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100944 GType types[FIO_IO_U_MAP_NR + 1];
945 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100946#define NR_LABELS 10
947 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100948
949 frame = gtk_frame_new("IO depths");
950 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
951
952 box = gtk_hbox_new(FALSE, 3);
953 gtk_container_add(GTK_CONTAINER(frame), box);
954
Jens Axboe19998db2012-03-06 09:17:59 +0100955 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100956 types[i] = G_TYPE_STRING;
957
Jens Axboe19998db2012-03-06 09:17:59 +0100958 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100959
960 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
961 gtk_widget_set_can_focus(tree_view, FALSE);
962
Jens Axboe661f7412012-03-06 13:55:45 +0100963 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
964 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
965
Jens Axboe2e331012012-03-05 22:07:54 +0100966 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
967 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
968
Jens Axboe19998db2012-03-06 09:17:59 +0100969 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100970 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
971
Jens Axboe19998db2012-03-06 09:17:59 +0100972 gfio_add_total_depths_tree(model, ts, NR_LABELS);
973 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
974 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +0100975
976 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
977}
978
Jens Axboef9d40b42012-03-06 09:52:49 +0100979static gboolean results_window_delete(GtkWidget *w, gpointer data)
980{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100981 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +0100982
983 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100984 ge->results_window = NULL;
985 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +0100986 return TRUE;
987}
988
Jens Axboe2f99deb2012-03-09 14:37:29 +0100989static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +0100990{
991 GtkWidget *win, *notebook;
992
Jens Axboe2f99deb2012-03-09 14:37:29 +0100993 if (ge->results_window)
994 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100995
996 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
997 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +0100998 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100999 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1000 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001001
1002 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001003 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1004 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboef9d40b42012-03-06 09:52:49 +01001005 gtk_container_add(GTK_CONTAINER(win), notebook);
1006
Jens Axboe2f99deb2012-03-09 14:37:29 +01001007 ge->results_window = win;
1008 ge->results_notebook = notebook;
1009 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001010}
1011
Jens Axboe3650a3c2012-03-05 14:09:03 +01001012static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1013 struct group_run_stats *rs)
1014{
Jens Axboeb01329d2012-03-07 20:31:28 +01001015 GtkWidget *res_win, *box, *vbox, *entry, *scroll;
Jens Axboee0681f32012-03-06 12:14:42 +01001016 struct gfio_client *gc = client->client_data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001017
1018 gdk_threads_enter();
1019
Jens Axboe2f99deb2012-03-09 14:37:29 +01001020 res_win = get_results_window(gc->ge);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001021
Jens Axboeb01329d2012-03-07 20:31:28 +01001022 scroll = gtk_scrolled_window_new(NULL, NULL);
1023 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1024 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1025
Jens Axboe3650a3c2012-03-05 14:09:03 +01001026 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001027
Jens Axboeb01329d2012-03-07 20:31:28 +01001028 box = gtk_hbox_new(FALSE, 0);
1029 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001030
Jens Axboeb01329d2012-03-07 20:31:28 +01001031 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1032
1033 gtk_notebook_append_page(GTK_NOTEBOOK(res_win), scroll, gtk_label_new(ts->name));
Jens Axboef9d40b42012-03-06 09:52:49 +01001034
Jens Axboee0681f32012-03-06 12:14:42 +01001035 gc->results_widget = vbox;
1036
Jens Axboe3650a3c2012-03-05 14:09:03 +01001037 entry = new_info_entry_in_frame(box, "Name");
1038 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1039 if (strlen(ts->description)) {
1040 entry = new_info_entry_in_frame(box, "Description");
1041 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1042 }
1043 entry = new_info_entry_in_frame(box, "Group ID");
1044 entry_set_int_value(entry, ts->groupid);
1045 entry = new_info_entry_in_frame(box, "Jobs");
1046 entry_set_int_value(entry, ts->members);
Jens Axboe6b79c802012-03-08 10:51:36 +01001047 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
Jens Axboe3650a3c2012-03-05 14:09:03 +01001048 entry_set_int_value(entry, ts->error);
1049 entry = new_info_entry_in_frame(box, "PID");
1050 entry_set_int_value(entry, ts->pid);
1051
1052 if (ts->io_bytes[DDIR_READ])
1053 gfio_show_ddir_status(vbox, rs, ts, DDIR_READ);
1054 if (ts->io_bytes[DDIR_WRITE])
1055 gfio_show_ddir_status(vbox, rs, ts, DDIR_WRITE);
1056
Jens Axboee5bd1342012-03-05 21:38:12 +01001057 gfio_show_latency_buckets(vbox, ts);
Jens Axboe2e331012012-03-05 22:07:54 +01001058 gfio_show_cpu_usage(vbox, ts);
1059 gfio_show_io_depths(vbox, ts);
Jens Axboee5bd1342012-03-05 21:38:12 +01001060
Jens Axboe2f99deb2012-03-09 14:37:29 +01001061 gtk_widget_show_all(gc->ge->results_window);
Jens Axboe3650a3c2012-03-05 14:09:03 +01001062 gdk_threads_leave();
1063}
1064
Jens Axboe084d1c62012-03-03 20:28:07 +01001065static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001066{
Jens Axboe9b260bd2012-03-06 11:02:52 +01001067 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001068 struct gui *ui = &main_ui;
Jens Axboe9b260bd2012-03-06 11:02:52 +01001069 GtkTreeIter iter;
1070 struct tm *tm;
1071 time_t sec;
1072 char tmp[64], timebuf[80];
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001073
Jens Axboe9b260bd2012-03-06 11:02:52 +01001074 sec = p->log_sec;
1075 tm = localtime(&sec);
1076 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1077 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1078
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001079 gdk_threads_enter();
Jens Axboe9b260bd2012-03-06 11:02:52 +01001080
Jens Axboe2f99deb2012-03-09 14:37:29 +01001081 gtk_list_store_append(ui->log_model, &iter);
1082 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1083 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1084 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1085 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
Jens Axboe9b260bd2012-03-06 11:02:52 +01001086
Jens Axboe6b79c802012-03-08 10:51:36 +01001087 if (p->level == FIO_LOG_ERR)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001088 view_log(NULL, (gpointer) ui);
Jens Axboe6b79c802012-03-08 10:51:36 +01001089
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01001090 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001091}
1092
1093static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1094{
Jens Axboee0681f32012-03-06 12:14:42 +01001095 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1096 struct gfio_client *gc = client->client_data;
1097 GtkWidget *box, *frame, *entry, *vbox;
Jens Axboe604cfe32012-03-07 19:51:36 +01001098 double util;
1099 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001100
Jens Axboe0050e5f2012-03-06 09:23:27 +01001101 gdk_threads_enter();
Jens Axboee0681f32012-03-06 12:14:42 +01001102
Jens Axboe45dcb2e2012-03-07 16:16:50 +01001103 if (!gc->results_widget)
Jens Axboee0681f32012-03-06 12:14:42 +01001104 goto out;
Jens Axboee0681f32012-03-06 12:14:42 +01001105
1106 if (!gc->disk_util_frame) {
1107 gc->disk_util_frame = gtk_frame_new("Disk utilization");
1108 gtk_box_pack_start(GTK_BOX(gc->results_widget), gc->disk_util_frame, FALSE, FALSE, 5);
1109 }
1110
1111 vbox = gtk_vbox_new(FALSE, 3);
1112 gtk_container_add(GTK_CONTAINER(gc->disk_util_frame), vbox);
1113
1114 frame = gtk_frame_new((char *) p->dus.name);
1115 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1116
1117 box = gtk_vbox_new(FALSE, 3);
1118 gtk_container_add(GTK_CONTAINER(frame), box);
1119
1120 frame = gtk_frame_new("Read");
1121 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1122 vbox = gtk_hbox_new(TRUE, 3);
1123 gtk_container_add(GTK_CONTAINER(frame), vbox);
1124 entry = new_info_entry_in_frame(vbox, "IOs");
1125 entry_set_int_value(entry, p->dus.ios[0]);
1126 entry = new_info_entry_in_frame(vbox, "Merges");
1127 entry_set_int_value(entry, p->dus.merges[0]);
1128 entry = new_info_entry_in_frame(vbox, "Sectors");
1129 entry_set_int_value(entry, p->dus.sectors[0]);
1130 entry = new_info_entry_in_frame(vbox, "Ticks");
1131 entry_set_int_value(entry, p->dus.ticks[0]);
1132
1133 frame = gtk_frame_new("Write");
1134 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1135 vbox = gtk_hbox_new(TRUE, 3);
1136 gtk_container_add(GTK_CONTAINER(frame), vbox);
1137 entry = new_info_entry_in_frame(vbox, "IOs");
1138 entry_set_int_value(entry, p->dus.ios[1]);
1139 entry = new_info_entry_in_frame(vbox, "Merges");
1140 entry_set_int_value(entry, p->dus.merges[1]);
1141 entry = new_info_entry_in_frame(vbox, "Sectors");
1142 entry_set_int_value(entry, p->dus.sectors[1]);
1143 entry = new_info_entry_in_frame(vbox, "Ticks");
1144 entry_set_int_value(entry, p->dus.ticks[1]);
1145
1146 frame = gtk_frame_new("Shared");
1147 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1148 vbox = gtk_hbox_new(TRUE, 3);
1149 gtk_container_add(GTK_CONTAINER(frame), vbox);
1150 entry = new_info_entry_in_frame(vbox, "IO ticks");
1151 entry_set_int_value(entry, p->dus.io_ticks);
1152 entry = new_info_entry_in_frame(vbox, "Time in queue");
1153 entry_set_int_value(entry, p->dus.time_in_queue);
1154
Jens Axboe604cfe32012-03-07 19:51:36 +01001155 util = 0.0;
1156 if (p->dus.msec)
1157 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1158 if (util > 100.0)
1159 util = 100.0;
1160
1161 sprintf(tmp, "%3.2f%%", util);
1162 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1163 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1164
Jens Axboee0681f32012-03-06 12:14:42 +01001165 gtk_widget_show_all(gc->results_widget);
1166out:
Jens Axboe0050e5f2012-03-06 09:23:27 +01001167 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001168}
1169
Jens Axboe3650a3c2012-03-05 14:09:03 +01001170extern int sum_stat_clients;
1171extern struct thread_stat client_ts;
1172extern struct group_run_stats client_gs;
1173
1174static int sum_stat_nr;
1175
Jens Axboe89e5fad2012-03-05 09:21:12 +01001176static void gfio_thread_status_op(struct fio_client *client,
1177 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001178{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001179 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1180
1181 gfio_display_ts(client, &p->ts, &p->rs);
1182
1183 if (sum_stat_clients == 1)
1184 return;
1185
1186 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1187 sum_group_stats(&client_gs, &p->rs);
1188
1189 client_ts.members++;
1190 client_ts.groupid = p->ts.groupid;
1191
1192 if (++sum_stat_nr == sum_stat_clients) {
1193 strcpy(client_ts.name, "All clients");
1194 gfio_display_ts(client, &client_ts, &client_gs);
1195 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001196}
1197
Jens Axboe89e5fad2012-03-05 09:21:12 +01001198static void gfio_group_stats_op(struct fio_client *client,
1199 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001200{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001201 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001202}
1203
Jens Axboe2f99deb2012-03-09 14:37:29 +01001204static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1205 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001206{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001207 struct gfio_graphs *g = data;
1208
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001209 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1210 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1211 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1212 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001213 return TRUE;
1214}
1215
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001216static void draw_graph(struct graph *g, cairo_t *cr)
1217{
1218 line_graph_draw(g, cr);
1219 cairo_stroke(cr);
1220}
1221
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001222static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1223{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001224 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001225 cairo_t *cr;
1226
1227 cr = gdk_cairo_create(w->window);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001228 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001229 draw_graph(g->iops_graph, cr);
1230 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001231 cairo_destroy(cr);
1232
1233 return FALSE;
1234}
1235
Jens Axboe2f99deb2012-03-09 14:37:29 +01001236/*
1237 * Client specific ETA
1238 */
1239static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001240{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001241 struct gfio_client *gc = client->client_data;
1242 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001243 static int eta_good;
1244 char eta_str[128];
1245 char output[256];
1246 char tmp[32];
1247 double perc = 0.0;
1248 int i2p = 0;
1249
Jens Axboe0050e5f2012-03-06 09:23:27 +01001250 gdk_threads_enter();
1251
Jens Axboe3e47bd22012-02-29 13:45:02 +01001252 eta_str[0] = '\0';
1253 output[0] = '\0';
1254
1255 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1256 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1257 eta_to_str(eta_str, je->eta_sec);
1258 }
1259
1260 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001261 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001262 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001263 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001264
1265#if 0
1266 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1267 if (je->m_rate || je->t_rate) {
1268 char *tr, *mr;
1269
1270 mr = num2str(je->m_rate, 4, 0, i2p);
1271 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001272 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001273 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1274 free(tr);
1275 free(mr);
1276 } else if (je->m_iops || je->t_iops)
1277 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001278
Jens Axboe2f99deb2012-03-09 14:37:29 +01001279 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1280 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1281 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1282 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001283#endif
1284
1285 if (je->eta_sec != INT_MAX && je->nr_running) {
1286 char *iops_str[2];
1287 char *rate_str[2];
1288
1289 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1290 strcpy(output, "-.-% done");
1291 else {
1292 eta_good = 1;
1293 perc *= 100.0;
1294 sprintf(output, "%3.1f%% done", perc);
1295 }
1296
1297 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1298 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1299
1300 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1301 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1302
Jens Axboe2f99deb2012-03-09 14:37:29 +01001303 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1304 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1305 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1306 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001307
Jens Axboe2f99deb2012-03-09 14:37:29 +01001308 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1309 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1310 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1311 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
1312
1313 free(rate_str[0]);
1314 free(rate_str[1]);
1315 free(iops_str[0]);
1316 free(iops_str[1]);
1317 }
1318
1319 if (eta_str[0]) {
1320 char *dst = output + strlen(output);
1321
1322 sprintf(dst, " - %s", eta_str);
1323 }
1324
Jens Axboe9988ca72012-03-09 15:14:06 +01001325 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001326 gdk_threads_leave();
1327}
1328
1329/*
1330 * Update ETA in main window for all clients
1331 */
1332static void gfio_update_all_eta(struct jobs_eta *je)
1333{
1334 struct gui *ui = &main_ui;
1335 static int eta_good;
1336 char eta_str[128];
1337 char output[256];
1338 double perc = 0.0;
1339 int i2p = 0;
1340
1341 gdk_threads_enter();
1342
1343 eta_str[0] = '\0';
1344 output[0] = '\0';
1345
1346 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1347 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1348 eta_to_str(eta_str, je->eta_sec);
1349 }
1350
1351#if 0
1352 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1353 if (je->m_rate || je->t_rate) {
1354 char *tr, *mr;
1355
1356 mr = num2str(je->m_rate, 4, 0, i2p);
1357 tr = num2str(je->t_rate, 4, 0, i2p);
1358 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1359 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1360 free(tr);
1361 free(mr);
1362 } else if (je->m_iops || je->t_iops)
1363 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1364
1365 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1366 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1367 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1368 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1369#endif
1370
Jens Axboe3863d1a2012-03-09 17:39:05 +01001371 entry_set_int_value(ui->eta.jobs, je->nr_running);
1372
Jens Axboe2f99deb2012-03-09 14:37:29 +01001373 if (je->eta_sec != INT_MAX && je->nr_running) {
1374 char *iops_str[2];
1375 char *rate_str[2];
1376
1377 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1378 strcpy(output, "-.-% done");
1379 else {
1380 eta_good = 1;
1381 perc *= 100.0;
1382 sprintf(output, "%3.1f%% done", perc);
1383 }
1384
1385 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1386 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1387
1388 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1389 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1390
1391 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1392 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1393 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1394 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1395
1396 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0]);
1397 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1]);
1398 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0]);
1399 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1]);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001400
Jens Axboe3e47bd22012-02-29 13:45:02 +01001401 free(rate_str[0]);
1402 free(rate_str[1]);
1403 free(iops_str[0]);
1404 free(iops_str[1]);
1405 }
1406
1407 if (eta_str[0]) {
1408 char *dst = output + strlen(output);
1409
1410 sprintf(dst, " - %s", eta_str);
1411 }
1412
Jens Axboe9988ca72012-03-09 15:14:06 +01001413 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001414 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001415}
1416
Stephen M. Camerona1820202012-02-24 08:17:31 +01001417static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1418{
Jens Axboe843ad232012-02-29 11:44:53 +01001419 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001420 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001421 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001422 const char *os, *arch;
1423 char buf[64];
1424
1425 os = fio_get_os_string(probe->os);
1426 if (!os)
1427 os = "unknown";
1428
1429 arch = fio_get_arch_string(probe->arch);
1430 if (!arch)
1431 os = "unknown";
1432
1433 if (!client->name)
1434 client->name = strdup((char *) probe->hostname);
1435
Jens Axboe0050e5f2012-03-06 09:23:27 +01001436 gdk_threads_enter();
1437
Jens Axboe2f99deb2012-03-09 14:37:29 +01001438 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1439 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1440 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001441 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001442 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001443
Jens Axboe2f99deb2012-03-09 14:37:29 +01001444 gfio_set_connected(ge, 1);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001445
1446 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001447}
1448
Jens Axboe9988ca72012-03-09 15:14:06 +01001449static void gfio_update_thread_status(struct gui_entry *ge,
1450 char *status_message, double perc)
1451{
1452 static char message[100];
1453 const char *m = message;
1454
1455 strncpy(message, status_message, sizeof(message) - 1);
1456 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1457 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1458 gtk_widget_queue_draw(main_ui.window);
1459}
1460
1461static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001462{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001463 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001464 static char message[100];
1465 const char *m = message;
1466
1467 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001468 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1469 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1470 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001471}
1472
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001473static void gfio_quit_op(struct fio_client *client)
1474{
Jens Axboee0681f32012-03-06 12:14:42 +01001475 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001476
Jens Axboe0050e5f2012-03-06 09:23:27 +01001477 gdk_threads_enter();
Jens Axboe2f99deb2012-03-09 14:37:29 +01001478 gfio_set_connected(gc->ge, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001479 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001480}
1481
Jens Axboe807f9972012-03-02 10:25:24 +01001482static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1483{
1484 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001485 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001486 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001487 struct gui_entry *ge = gc->ge;
Jens Axboe807f9972012-03-02 10:25:24 +01001488 char tmp[8];
Jens Axboe807f9972012-03-02 10:25:24 +01001489
Jens Axboedcaeb602012-03-08 19:45:37 +01001490 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001491
Jens Axboe0050e5f2012-03-06 09:23:27 +01001492 gdk_threads_enter();
1493
Jens Axboe2f99deb2012-03-09 14:37:29 +01001494 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1495
Jens Axboe3863d1a2012-03-09 17:39:05 +01001496 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1497 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1498
Jens Axboec80b74b2012-03-12 10:23:28 +01001499 multitext_add_entry(&ge->eta.iotype, ddir_str(o->td_ddir));
1500 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001501
Jens Axboedcaeb602012-03-08 19:45:37 +01001502 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001503 multitext_add_entry(&ge->eta.iodepth, tmp);
1504
1505 multitext_set_entry(&ge->eta.iotype, 0);
1506 multitext_set_entry(&ge->eta.ioengine, 0);
1507 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001508
Jens Axboedcaeb602012-03-08 19:45:37 +01001509 gc->job_added++;
1510
Jens Axboe0050e5f2012-03-06 09:23:27 +01001511 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001512}
1513
Jens Axboeed727a42012-03-02 12:14:40 +01001514static void gfio_client_timed_out(struct fio_client *client)
1515{
Jens Axboee0681f32012-03-06 12:14:42 +01001516 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001517 char buf[256];
1518
1519 gdk_threads_enter();
1520
Jens Axboe2f99deb2012-03-09 14:37:29 +01001521 gfio_set_connected(gc->ge, 0);
1522 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001523
1524 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001525 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001526
1527 gdk_threads_leave();
1528}
1529
Jens Axboe6b79c802012-03-08 10:51:36 +01001530static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1531{
1532 struct gfio_client *gc = client->client_data;
1533
1534 gdk_threads_enter();
1535
Jens Axboe2f99deb2012-03-09 14:37:29 +01001536 gfio_set_connected(gc->ge, 0);
Jens Axboe6b79c802012-03-08 10:51:36 +01001537
1538 if (gc->err_entry)
1539 entry_set_int_value(gc->err_entry, client->error);
1540
1541 gdk_threads_leave();
1542}
1543
Stephen M. Camerona1820202012-02-24 08:17:31 +01001544struct client_ops gfio_client_ops = {
Jens Axboe0420ba62012-02-29 11:16:52 +01001545 .text_op = gfio_text_op,
1546 .disk_util = gfio_disk_util_op,
1547 .thread_status = gfio_thread_status_op,
1548 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001549 .jobs_eta = gfio_update_client_eta,
1550 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001551 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001552 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001553 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001554 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001555 .stop = gfio_client_stop,
Jens Axboe6433ee02012-03-09 20:10:51 +01001556 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001557 .stay_connected = 1,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001558};
1559
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001560static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1561 __attribute__((unused)) gpointer data)
1562{
1563 gtk_main_quit();
1564}
1565
Stephen M. Cameron25927252012-02-24 08:17:31 +01001566static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001567{
Jens Axboea9eccde2012-03-09 14:59:42 +01001568 struct gui *ui = arg;
1569
1570 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001571 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001572 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001573 return NULL;
1574}
1575
Jens Axboe2f99deb2012-03-09 14:37:29 +01001576static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001577{
Jens Axboe9988ca72012-03-09 15:14:06 +01001578 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001579 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001580
Jens Axboe2f99deb2012-03-09 14:37:29 +01001581 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001582 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001583 if (ret < 0) {
1584 GError *error;
1585
1586 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1587 report_error(error);
1588 g_error_free(error);
1589 break;
1590 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001591 break;
1592
Jens Axboe2f99deb2012-03-09 14:37:29 +01001593 free(ge->job_files[i]);
1594 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001595 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001596 while (i < ge->nr_job_files) {
1597 free(ge->job_files[i]);
1598 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001599 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001600 }
1601
Jens Axboe441013b2012-03-01 08:01:52 +01001602 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001603}
1604
Jens Axboe63a130b2012-03-06 20:08:59 +01001605static void *server_thread(void *arg)
1606{
1607 is_backend = 1;
1608 gfio_server_running = 1;
1609 fio_start_server(NULL);
1610 gfio_server_running = 0;
1611 return NULL;
1612}
1613
Jens Axboe2f99deb2012-03-09 14:37:29 +01001614static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001615{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001616 struct gui *ui = &main_ui;
1617
Jens Axboe63a130b2012-03-06 20:08:59 +01001618 if (!gfio_server_running) {
1619 gfio_server_running = 1;
1620 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001621 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001622 }
1623}
1624
Stephen M. Cameron25927252012-02-24 08:17:31 +01001625static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1626 gpointer data)
1627{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001628 struct gui_entry *ge = data;
1629 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001630
Jens Axboe2f99deb2012-03-09 14:37:29 +01001631 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 0);
1632 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001633}
1634
Jens Axboedf06f222012-03-02 13:32:04 +01001635static void file_open(GtkWidget *w, gpointer data);
1636
1637static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001638{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001639 struct gui_entry *ge = data;
1640 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001641
Jens Axboe2f99deb2012-03-09 14:37:29 +01001642 if (!ge->connected) {
Jens Axboec7249262012-03-09 17:11:04 +01001643 int ret;
1644
Jens Axboe2f99deb2012-03-09 14:37:29 +01001645 if (!ge->nr_job_files)
Jens Axboedf06f222012-03-02 13:32:04 +01001646 file_open(widget, data);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001647 if (!ge->nr_job_files)
1648 return;
1649
1650 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1651 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001652 ret = fio_client_connect(gc->client);
1653 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001654 if (!ge->ui->handler_running)
1655 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001656 gtk_widget_set_sensitive(ge->button[CONNECT_BUTTON], 0);
1657 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 1);
Jens Axboec7249262012-03-09 17:11:04 +01001658 } else {
1659 GError *error;
1660
1661 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1662 report_error(error);
1663 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001664 }
Jens Axboedf06f222012-03-02 13:32:04 +01001665 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001666 fio_client_terminate(gc->client);
1667 gfio_set_connected(ge, 0);
1668 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001669 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001670}
1671
Jens Axboeb9d2f302012-03-08 20:36:28 +01001672static void send_clicked(GtkWidget *widget, gpointer data)
1673{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001674 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001675
Jens Axboe2f99deb2012-03-09 14:37:29 +01001676 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001677 GError *error;
1678
1679 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);
1680 report_error(error);
1681 g_error_free(error);
1682
Jens Axboe2f99deb2012-03-09 14:37:29 +01001683 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001684 }
1685
Jens Axboe2f99deb2012-03-09 14:37:29 +01001686 gtk_widget_set_sensitive(ge->button[SEND_BUTTON], 0);
1687 gtk_widget_set_sensitive(ge->button[START_JOB_BUTTON], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001688}
1689
Jens Axboe2f99deb2012-03-09 14:37:29 +01001690static GtkWidget *add_button(GtkWidget *buttonbox,
1691 struct button_spec *buttonspec, gpointer data)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001692{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001693 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
1694
1695 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
1696 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
1697 gtk_widget_set_tooltip_text(button, buttonspec->tooltiptext);
1698 gtk_widget_set_sensitive(button, !buttonspec->start_insensitive);
1699
1700 return button;
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001701}
1702
Jens Axboe2f99deb2012-03-09 14:37:29 +01001703static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
1704 int nbuttons)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001705{
1706 int i;
1707
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001708 for (i = 0; i < nbuttons; i++)
Jens Axboe2f99deb2012-03-09 14:37:29 +01001709 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001710}
1711
Jens Axboe0420ba62012-02-29 11:16:52 +01001712static void on_info_bar_response(GtkWidget *widget, gint response,
1713 gpointer data)
1714{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001715 struct gui *ui = &main_ui;
1716
Jens Axboe0420ba62012-02-29 11:16:52 +01001717 if (response == GTK_RESPONSE_OK) {
1718 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001719 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001720 }
1721}
1722
Jens Axboedf06f222012-03-02 13:32:04 +01001723void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001724{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001725 struct gui *ui = &main_ui;
1726
1727 if (ui->error_info_bar == NULL) {
1728 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001729 GTK_RESPONSE_OK,
1730 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001731 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1732 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001733 GTK_MESSAGE_ERROR);
1734
Jens Axboe2f99deb2012-03-09 14:37:29 +01001735 ui->error_label = gtk_label_new(error->message);
1736 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1737 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001738
Jens Axboe2f99deb2012-03-09 14:37:29 +01001739 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1740 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001741 } else {
1742 char buffer[256];
1743 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001744 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001745 }
1746}
1747
Jens Axboe62bc9372012-03-07 11:45:07 +01001748struct connection_widgets
1749{
1750 GtkWidget *hentry;
1751 GtkWidget *combo;
1752 GtkWidget *button;
1753};
1754
1755static void hostname_cb(GtkEntry *entry, gpointer data)
1756{
1757 struct connection_widgets *cw = data;
1758 int uses_net = 0, is_localhost = 0;
1759 const gchar *text;
1760 gchar *ctext;
1761
1762 /*
1763 * Check whether to display the 'auto start backend' box
1764 * or not. Show it if we are a localhost and using network,
1765 * or using a socket.
1766 */
1767 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1768 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1769 uses_net = 1;
1770 g_free(ctext);
1771
1772 if (uses_net) {
1773 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1774 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1775 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1776 !strcmp(text, "ip6-loopback"))
1777 is_localhost = 1;
1778 }
1779
1780 if (!uses_net || is_localhost) {
1781 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1782 gtk_widget_set_sensitive(cw->button, 1);
1783 } else {
1784 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1785 gtk_widget_set_sensitive(cw->button, 0);
1786 }
1787}
1788
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001789static int get_connection_details(char **host, int *port, int *type,
1790 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001791{
Jens Axboe62bc9372012-03-07 11:45:07 +01001792 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1793 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001794 char *typeentry;
1795
1796 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001797 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001798 GTK_DIALOG_DESTROY_WITH_PARENT,
1799 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1800 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1801
1802 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001803 /* gtk_dialog_get_content_area() is 2.14 and newer */
1804 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001805 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1806
1807 box = gtk_vbox_new(FALSE, 6);
1808 gtk_container_add(GTK_CONTAINER(frame), box);
1809
1810 hbox = gtk_hbox_new(TRUE, 10);
1811 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01001812 cw.hentry = gtk_entry_new();
1813 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1814 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001815
1816 frame = gtk_frame_new("Port");
1817 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1818 box = gtk_vbox_new(FALSE, 10);
1819 gtk_container_add(GTK_CONTAINER(frame), box);
1820
1821 hbox = gtk_hbox_new(TRUE, 4);
1822 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1823 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
1824
1825 frame = gtk_frame_new("Type");
1826 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1827 box = gtk_vbox_new(FALSE, 10);
1828 gtk_container_add(GTK_CONTAINER(frame), box);
1829
1830 hbox = gtk_hbox_new(TRUE, 4);
1831 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1832
Jens Axboe62bc9372012-03-07 11:45:07 +01001833 cw.combo = gtk_combo_box_new_text();
1834 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
1835 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
1836 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
1837 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001838
Jens Axboe62bc9372012-03-07 11:45:07 +01001839 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001840
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001841 frame = gtk_frame_new("Options");
1842 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1843 box = gtk_vbox_new(FALSE, 10);
1844 gtk_container_add(GTK_CONTAINER(frame), box);
1845
1846 hbox = gtk_hbox_new(TRUE, 4);
1847 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1848
Jens Axboe62bc9372012-03-07 11:45:07 +01001849 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
1850 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
1851 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.");
1852 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
1853
1854 /*
1855 * Connect edit signal, so we can show/not-show the auto start button
1856 */
1857 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
1858 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001859
Jens Axboea7a42ce2012-03-02 13:12:04 +01001860 gtk_widget_show_all(dialog);
1861
1862 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
1863 gtk_widget_destroy(dialog);
1864 return 1;
1865 }
1866
Jens Axboe62bc9372012-03-07 11:45:07 +01001867 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001868 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
1869
Jens Axboe62bc9372012-03-07 11:45:07 +01001870 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01001871 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
1872 *type = Fio_client_ipv4;
1873 else if (!strncmp(typeentry, "IPv6", 4))
1874 *type = Fio_client_ipv6;
1875 else
1876 *type = Fio_client_socket;
1877 g_free(typeentry);
1878
Jens Axboe62bc9372012-03-07 11:45:07 +01001879 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001880
Jens Axboea7a42ce2012-03-02 13:12:04 +01001881 gtk_widget_destroy(dialog);
1882 return 0;
1883}
1884
Jens Axboe2f99deb2012-03-09 14:37:29 +01001885static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01001886{
1887 struct gfio_client *gc;
1888
1889 gc = malloc(sizeof(*gc));
1890 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01001891 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01001892 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001893
Jens Axboe2f99deb2012-03-09 14:37:29 +01001894 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01001895
1896 client->client_data = gc;
1897}
1898
Jens Axboe2f99deb2012-03-09 14:37:29 +01001899static GtkWidget *new_client_page(struct gui_entry *ge);
1900
1901static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
1902{
1903 struct gui_entry *ge;
1904
1905 ge = malloc(sizeof(*ge));
1906 memset(ge, 0, sizeof(*ge));
1907 INIT_FLIST_HEAD(&ge->list);
1908 flist_add_tail(&ge->list, &ui->list);
1909 ge->ui = ui;
1910 return ge;
1911}
1912
1913/*
1914 * FIXME: need more handling here
1915 */
1916static void ge_destroy(GtkWidget *w, gpointer data)
1917{
1918 struct gui_entry *ge = data;
Jens Axboe343cb4a2012-03-09 17:16:51 +01001919 struct gfio_client *gc = ge->client;
1920
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001921 if (gc && gc->client) {
1922 if (ge->connected)
1923 fio_client_terminate(gc->client);
1924
Jens Axboe343cb4a2012-03-09 17:16:51 +01001925 fio_put_client(gc->client);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001926 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001927
1928 flist_del(&ge->list);
1929 free(ge);
1930}
1931
1932static struct gui_entry *get_new_ge_with_tab(const char *name)
1933{
1934 struct gui_entry *ge;
1935
1936 ge = alloc_new_gui_entry(&main_ui);
1937
1938 ge->vbox = new_client_page(ge);
1939 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_destroy), ge);
1940
1941 ge->page_label = gtk_label_new(name);
1942 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
1943
1944 gtk_widget_show_all(main_ui.window);
1945 return ge;
1946}
1947
1948static void file_new(GtkWidget *w, gpointer data)
1949{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001950 struct gui *ui = (struct gui *) data;
1951 struct gui_entry *ge;
1952
1953 ge = get_new_ge_with_tab("Untitled");
1954 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001955}
1956
1957/*
1958 * Return the 'ge' corresponding to the tab. If the active tab is the
1959 * main tab, open a new tab.
1960 */
1961static struct gui_entry *get_ge_from_page(unsigned int cur_page)
1962{
1963 struct flist_head *entry;
1964 struct gui_entry *ge;
1965
1966 if (!cur_page)
1967 return get_new_ge_with_tab("Untitled");
1968
1969 flist_for_each(entry, &main_ui.list) {
1970 ge = flist_entry(entry, struct gui_entry, list);
1971 if (ge->page_num == cur_page)
1972 return ge;
1973 }
1974
1975 return NULL;
1976}
1977
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001978static void file_close(GtkWidget *w, gpointer data)
1979{
1980 struct gui *ui = (struct gui *) data;
1981 gint cur_page;
1982
1983 /*
1984 * Can't close the main tab
1985 */
1986 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
1987 if (cur_page) {
1988 struct gui_entry *ge = get_ge_from_page(cur_page);
1989
1990 gtk_widget_destroy(ge->vbox);
1991 return;
1992 }
1993
1994 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
1995}
1996
Jens Axboe0420ba62012-02-29 11:16:52 +01001997static void file_open(GtkWidget *w, gpointer data)
1998{
Jens Axboe63a130b2012-03-06 20:08:59 +01001999 struct gui *ui = data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002000 GtkWidget *dialog;
Jens Axboe0420ba62012-02-29 11:16:52 +01002001 GSList *filenames, *fn_glist;
2002 GtkFileFilter *filter;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002003 char *host;
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002004 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002005 struct gui_entry *ge;
2006 gint cur_page;
2007
2008 /*
2009 * Creates new tab if current tab is the main window, or the
2010 * current tab already has a client.
2011 */
2012 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2013 ge = get_ge_from_page(cur_page);
2014 if (ge->client)
2015 ge = get_new_ge_with_tab("Untitled");
2016
2017 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002018
2019 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002020 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002021 GTK_FILE_CHOOSER_ACTION_OPEN,
2022 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2023 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2024 NULL);
2025 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2026
2027 filter = gtk_file_filter_new();
2028 gtk_file_filter_add_pattern(filter, "*.fio");
2029 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002030 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe0420ba62012-02-29 11:16:52 +01002031 gtk_file_filter_add_mime_type(filter, "text/fio");
2032 gtk_file_filter_set_name(filter, "Fio job file");
2033 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2034
2035 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2036 gtk_widget_destroy(dialog);
2037 return;
2038 }
2039
2040 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002041
2042 gtk_widget_destroy(dialog);
2043
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002044 if (get_connection_details(&host, &port, &type, &server_start))
Jens Axboea7a42ce2012-03-02 13:12:04 +01002045 goto err;
2046
Jens Axboe0420ba62012-02-29 11:16:52 +01002047 filenames = fn_glist;
2048 while (filenames != NULL) {
Jens Axboee0681f32012-03-06 12:14:42 +01002049 struct fio_client *client;
2050
Jens Axboe2f99deb2012-03-09 14:37:29 +01002051 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2052 ge->job_files[ge->nr_job_files] = strdup(filenames->data);
2053 ge->nr_job_files++;
Jens Axboe0420ba62012-02-29 11:16:52 +01002054
Jens Axboee0681f32012-03-06 12:14:42 +01002055 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2056 if (!client) {
Jens Axboedf06f222012-03-02 13:32:04 +01002057 GError *error;
2058
2059 error = g_error_new(g_quark_from_string("fio"), 1,
2060 "Failed to add client %s", host);
Jens Axboe0420ba62012-02-29 11:16:52 +01002061 report_error(error);
2062 g_error_free(error);
Jens Axboe0420ba62012-02-29 11:16:52 +01002063 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002064 gfio_client_added(ge, client);
Jens Axboe0420ba62012-02-29 11:16:52 +01002065
2066 g_free(filenames->data);
2067 filenames = g_slist_next(filenames);
2068 }
Jens Axboea7a42ce2012-03-02 13:12:04 +01002069 free(host);
Jens Axboe63a130b2012-03-06 20:08:59 +01002070
2071 if (server_start)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002072 gfio_start_server();
Jens Axboea7a42ce2012-03-02 13:12:04 +01002073err:
Jens Axboe0420ba62012-02-29 11:16:52 +01002074 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002075}
2076
2077static void file_save(GtkWidget *w, gpointer data)
2078{
Jens Axboe63a130b2012-03-06 20:08:59 +01002079 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002080 GtkWidget *dialog;
2081
2082 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002083 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002084 GTK_FILE_CHOOSER_ACTION_SAVE,
2085 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2086 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2087 NULL);
2088
2089 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2090 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2091
2092 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2093 char *filename;
2094
2095 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2096 // save_job_file(filename);
2097 g_free(filename);
2098 }
2099 gtk_widget_destroy(dialog);
2100}
2101
Jens Axboe9b260bd2012-03-06 11:02:52 +01002102static void view_log_destroy(GtkWidget *w, gpointer data)
2103{
2104 struct gui *ui = (struct gui *) data;
2105
2106 gtk_widget_ref(ui->log_tree);
2107 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2108 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002109 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002110}
2111
2112static void view_log(GtkWidget *w, gpointer data)
2113{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002114 GtkWidget *win, *scroll, *vbox, *box;
2115 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002116
Jens Axboe4cbe7212012-03-06 13:36:17 +01002117 if (ui->log_view)
2118 return;
2119
2120 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002121 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002122 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002123
Jens Axboe4cbe7212012-03-06 13:36:17 +01002124 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002125
Jens Axboe4cbe7212012-03-06 13:36:17 +01002126 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2127
2128 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2129
2130 box = gtk_hbox_new(TRUE, 0);
2131 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2132 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2133 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2134
2135 vbox = gtk_vbox_new(TRUE, 5);
2136 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2137
2138 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002139 gtk_widget_show_all(win);
2140}
2141
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002142static void edit_options(GtkWidget *w, gpointer data)
2143{
2144}
2145
Jens Axboe8577f4f2012-03-09 19:28:27 +01002146static void __update_graph_limits(struct gfio_graphs *g)
2147{
2148 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2149 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2150}
2151
2152static void update_graph_limits(void)
2153{
2154 struct flist_head *entry;
2155 struct gui_entry *ge;
2156
2157 __update_graph_limits(&main_ui.graphs);
2158
2159 flist_for_each(entry, &main_ui.list) {
2160 ge = flist_entry(entry, struct gui_entry, list);
2161 __update_graph_limits(&ge->graphs);
2162 }
2163}
2164
Jens Axboe46974a72012-03-02 19:34:13 +01002165static void preferences(GtkWidget *w, gpointer data)
2166{
Jens Axboef3e84402012-03-07 13:14:32 +01002167 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002168 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002169 int i;
2170
2171 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002172 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002173 GTK_DIALOG_DESTROY_WITH_PARENT,
2174 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2175 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2176 NULL);
2177
Jens Axboe8577f4f2012-03-09 19:28:27 +01002178 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002179 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2180 vbox = gtk_vbox_new(FALSE, 6);
2181 gtk_container_add(GTK_CONTAINER(frame), vbox);
2182
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002183 hbox = gtk_hbox_new(FALSE, 5);
2184 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2185 entry = gtk_label_new("Font face to use for graph labels");
2186 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2187
Jens Axboef3e84402012-03-07 13:14:32 +01002188 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002189 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002190
Jens Axboe8577f4f2012-03-09 19:28:27 +01002191 box = gtk_vbox_new(FALSE, 6);
2192 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2193
2194 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002195 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002196 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2197 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2198
Jens Axboec05d9052012-03-11 13:05:35 +01002199 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002200
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002201 box = gtk_vbox_new(FALSE, 6);
2202 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2203
2204 hbox = gtk_hbox_new(FALSE, 5);
2205 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2206 entry = gtk_label_new("Client ETA request interval (msec)");
2207 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2208
2209 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002210 frame = gtk_frame_new("Debug logging");
2211 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2212 vbox = gtk_vbox_new(FALSE, 6);
2213 gtk_container_add(GTK_CONTAINER(frame), vbox);
2214
2215 box = gtk_hbox_new(FALSE, 6);
2216 gtk_container_add(GTK_CONTAINER(vbox), box);
2217
2218 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2219
2220 for (i = 0; i < FD_DEBUG_MAX; i++) {
2221 if (i == 7) {
2222 box = gtk_hbox_new(FALSE, 6);
2223 gtk_container_add(GTK_CONTAINER(vbox), box);
2224 }
2225
2226
2227 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2228 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2229 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2230 }
2231
Jens Axboe46974a72012-03-02 19:34:13 +01002232 gtk_widget_show_all(dialog);
2233
2234 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2235 gtk_widget_destroy(dialog);
2236 return;
2237 }
2238
2239 for (i = 0; i < FD_DEBUG_MAX; i++) {
2240 int set;
2241
2242 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2243 if (set)
2244 fio_debug |= (1UL << i);
2245 }
2246
Jens Axboef3e84402012-03-07 13:14:32 +01002247 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002248 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2249 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002250 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002251
Jens Axboe46974a72012-03-02 19:34:13 +01002252 gtk_widget_destroy(dialog);
2253}
2254
Jens Axboe0420ba62012-02-29 11:16:52 +01002255static void about_dialog(GtkWidget *w, gpointer data)
2256{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002257 const char *authors[] = {
2258 "Jens Axboe <axboe@kernel.dk>",
2259 "Stephen Carmeron <stephenmcameron@gmail.com>",
2260 NULL
2261 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002262 const char *license[] = {
2263 "Fio is free software; you can redistribute it and/or modify "
2264 "it under the terms of the GNU General Public License as published by "
2265 "the Free Software Foundation; either version 2 of the License, or "
2266 "(at your option) any later version.\n",
2267 "Fio is distributed in the hope that it will be useful, "
2268 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2269 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2270 "GNU General Public License for more details.\n",
2271 "You should have received a copy of the GNU General Public License "
2272 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2273 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2274 };
2275 char *license_trans;
2276
2277 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2278 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002279
Jens Axboe0420ba62012-02-29 11:16:52 +01002280 gtk_show_about_dialog(NULL,
2281 "program-name", "gfio",
2282 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002283 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002284 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2285 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002286 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002287 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002288 "logo-icon-name", "fio",
2289 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002290 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002291 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002292
Jens Axboe2f99deb2012-03-09 14:37:29 +01002293 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002294}
2295
2296static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002297 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002298 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002299 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002300 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002301 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002302 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002303 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2304 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2305 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002306 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002307 { "EditOptions", NULL, "Edit Options", "<Control>E", NULL, G_CALLBACK(edit_options) },
Jens Axboe46974a72012-03-02 19:34:13 +01002308 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2309 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002310};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002311static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002312
2313static const gchar *ui_string = " \
2314 <ui> \
2315 <menubar name=\"MainMenu\"> \
2316 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002317 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002318 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002319 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002320 <menuitem name=\"Open\" action=\"OpenFile\" /> \
2321 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002322 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002323 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2324 <separator name=\"Separator3\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002325 <menuitem name=\"Quit\" action=\"Quit\" /> \
2326 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002327 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
2328 <menuitem name=\"Edit Options\" action=\"EditOptions\" /> \
2329 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002330 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
2331 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2332 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002333 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2334 <menuitem name=\"About\" action=\"About\" /> \
2335 </menu> \
2336 </menubar> \
2337 </ui> \
2338";
2339
Jens Axboe02421e62012-03-12 12:05:50 +01002340static void set_job_menu_visible(struct gui *ui, int visible)
2341{
2342 GtkWidget *job;
2343
2344 job = gtk_ui_manager_get_widget(ui->uimanager, "/MainMenu/JobMenu");
2345 gtk_widget_set_sensitive(job, visible);
2346}
2347
Jens Axboe4cbe7212012-03-06 13:36:17 +01002348static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2349 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002350{
2351 GtkActionGroup *action_group = gtk_action_group_new("Menu");
2352 GError *error = 0;
2353
2354 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002355 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002356
2357 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2358 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2359
2360 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002361
Jens Axboe0420ba62012-02-29 11:16:52 +01002362 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2363}
2364
2365void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2366 GtkWidget *vbox, GtkUIManager *ui_manager)
2367{
2368 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2369}
2370
Jens Axboec80b74b2012-03-12 10:23:28 +01002371static void combo_entry_changed(GtkComboBox *box, gpointer data)
2372{
2373 struct gui_entry *ge = (struct gui_entry *) data;
2374 gint index;
2375
2376 index = gtk_combo_box_get_active(box);
2377
2378 multitext_set_entry(&ge->eta.iotype, index);
2379 multitext_set_entry(&ge->eta.ioengine, index);
2380 multitext_set_entry(&ge->eta.iodepth, index);
2381}
2382
2383static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2384{
2385 struct gui_entry *ge = (struct gui_entry *) data;
2386
2387 multitext_free(&ge->eta.iotype);
2388 multitext_free(&ge->eta.ioengine);
2389 multitext_free(&ge->eta.iodepth);
2390}
2391
Jens Axboe2f99deb2012-03-09 14:37:29 +01002392static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002393{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002394 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002395 GdkColor white;
Jens Axboe0420ba62012-02-29 11:16:52 +01002396
Jens Axboe2f99deb2012-03-09 14:37:29 +01002397 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002398
Jens Axboe2f99deb2012-03-09 14:37:29 +01002399 ge->topalign = gtk_alignment_new(0, 0, 1, 0);
2400 ge->topvbox = gtk_vbox_new(FALSE, 3);
2401 gtk_container_add(GTK_CONTAINER(ge->topalign), ge->topvbox);
2402 gtk_box_pack_start(GTK_BOX(main_vbox), ge->topalign, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002403
Jens Axboe3e47bd22012-02-29 13:45:02 +01002404 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002405 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002406 probe_frame = gtk_vbox_new(FALSE, 3);
2407 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2408
2409 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002410 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2411 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2412 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2413 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2414 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002415
Jens Axboe3e47bd22012-02-29 13:45:02 +01002416 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002417 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2418
Jens Axboe3863d1a2012-03-09 17:39:05 +01002419 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002420 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2421 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2422 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
2423 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2424 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002425 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2426 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2427
2428 probe_box = gtk_hbox_new(FALSE, 3);
2429 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2430 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2431 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2432 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2433 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2434
2435 /*
2436 * Only add this if we have a commit rate
2437 */
2438#if 0
2439 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002440 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002441
Jens Axboe2f99deb2012-03-09 14:37:29 +01002442 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2443 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2444
2445 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2446 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2447#endif
2448
2449 /*
2450 * Set up a drawing area and IOPS and bandwidth graphs
2451 */
2452 gdk_color_parse("white", &white);
2453 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002454 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002455 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002456 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2457 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2458 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2459 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2460 G_CALLBACK(on_config_drawing_area), &ge->graphs);
2461 ge->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2462 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2463 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2464 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ge->scrolled_window),
2465 ge->graphs.drawing_area);
2466 gtk_box_pack_start(GTK_BOX(main_vbox), ge->scrolled_window,
2467 TRUE, TRUE, 0);
2468
2469 setup_graphs(&ge->graphs);
2470
2471 /*
2472 * Set up alignments for widgets at the bottom of ui,
2473 * align bottom left, expand horizontally but not vertically
2474 */
2475 ge->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2476 ge->buttonbox = gtk_hbox_new(FALSE, 0);
2477 gtk_container_add(GTK_CONTAINER(ge->bottomalign), ge->buttonbox);
2478 gtk_box_pack_start(GTK_BOX(main_vbox), ge->bottomalign,
2479 FALSE, FALSE, 0);
2480
2481 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2482
2483 /*
2484 * Set up thread status progress bar
2485 */
2486 ge->thread_status_pb = gtk_progress_bar_new();
2487 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2488 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2489 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2490
2491
2492 return main_vbox;
2493}
2494
2495static GtkWidget *new_main_page(struct gui *ui)
2496{
2497 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
2498 GdkColor white;
2499
2500 main_vbox = gtk_vbox_new(FALSE, 3);
2501
2502 /*
2503 * Set up alignments for widgets at the top of ui,
2504 * align top left, expand horizontally but not vertically
2505 */
2506 ui->topalign = gtk_alignment_new(0, 0, 1, 0);
2507 ui->topvbox = gtk_vbox_new(FALSE, 0);
2508 gtk_container_add(GTK_CONTAINER(ui->topalign), ui->topvbox);
2509 gtk_box_pack_start(GTK_BOX(main_vbox), ui->topalign, FALSE, FALSE, 0);
2510
2511 probe = gtk_frame_new("Run statistics");
2512 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2513 probe_frame = gtk_vbox_new(FALSE, 3);
2514 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002515
2516 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002517 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01002518 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01002519 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2520 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2521 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2522 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002523
2524 /*
2525 * Only add this if we have a commit rate
2526 */
2527#if 0
2528 probe_box = gtk_hbox_new(FALSE, 3);
2529 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2530
Jens Axboe3e47bd22012-02-29 13:45:02 +01002531 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2532 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2533
Jens Axboe3e47bd22012-02-29 13:45:02 +01002534 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2535 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002536#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002537
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002538 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002539 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002540 */
Stephen M. Cameronaaa71f62012-03-07 14:47:03 +01002541 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002542 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002543 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002544 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002545 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2546 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2547 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2548 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2549 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002550 ui->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2551 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ui->scrolled_window),
2552 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002553 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(ui->scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002554 ui->graphs.drawing_area);
2555 gtk_box_pack_start(GTK_BOX(main_vbox), ui->scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002556 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002557
Jens Axboe2f99deb2012-03-09 14:37:29 +01002558 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002559
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002560 /*
2561 * Set up alignments for widgets at the bottom of ui,
2562 * align bottom left, expand horizontally but not vertically
2563 */
2564 ui->bottomalign = gtk_alignment_new(0, 1, 1, 0);
2565 ui->buttonbox = gtk_hbox_new(FALSE, 0);
2566 gtk_container_add(GTK_CONTAINER(ui->bottomalign), ui->buttonbox);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002567 gtk_box_pack_start(GTK_BOX(main_vbox), ui->bottomalign,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002568 FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002569
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002570 /*
2571 * Set up thread status progress bar
2572 */
2573 ui->thread_status_pb = gtk_progress_bar_new();
2574 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002575 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002576 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2577
Jens Axboe2f99deb2012-03-09 14:37:29 +01002578 return main_vbox;
2579}
2580
2581static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2582 guint page, gpointer data)
2583
2584{
Jens Axboe02421e62012-03-12 12:05:50 +01002585 struct gui *ui = (struct gui *) data;
2586
2587 set_job_menu_visible(ui, page);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002588 return TRUE;
2589}
2590
2591static void init_ui(int *argc, char **argv[], struct gui *ui)
2592{
2593 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01002594 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002595
2596 /* Magical g*thread incantation, you just need this thread stuff.
2597 * Without it, the update that happens in gfio_update_thread_status
2598 * doesn't really happen in a timely fashion, you need expose events
2599 */
2600 if (!g_thread_supported())
2601 g_thread_init(NULL);
2602 gdk_threads_init();
2603
2604 gtk_init(argc, argv);
2605 settings = gtk_settings_get_default();
2606 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
2607 g_type_init();
2608
2609 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2610 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
2611 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
2612
2613 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
2614 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
2615
2616 ui->vbox = gtk_vbox_new(FALSE, 0);
2617 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
2618
Jens Axboe02421e62012-03-12 12:05:50 +01002619 ui->uimanager = gtk_ui_manager_new();
2620 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
2621 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002622
2623 ui->notebook = gtk_notebook_new();
2624 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01002625 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01002626 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002627 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
2628
2629 vbox = new_main_page(ui);
2630
2631 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
2632
Jens Axboe9b260bd2012-03-06 11:02:52 +01002633 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002634
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002635 gtk_widget_show_all(ui->window);
2636}
2637
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002638int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002639{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01002640 if (initialize_fio(envp))
2641 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01002642 if (fio_init_options())
2643 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01002644
Jens Axboe2f99deb2012-03-09 14:37:29 +01002645 memset(&main_ui, 0, sizeof(main_ui));
2646 INIT_FLIST_HEAD(&main_ui.list);
2647
2648 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01002649
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002650 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002651 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01002652 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002653 return 0;
2654}