blob: 15fce1df96a1afd5a52ab0181885776b5a284afd [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 Axboe53e0e852012-03-15 19:38:01 +010033#include "gfio.h"
34#include "ghelpers.h"
Jens Axboe9af4a242012-03-16 10:13:49 +010035#include "goptions.h"
Jens Axboe2fd3bb02012-03-07 08:07:39 +010036#include "graph.h"
Stephen M. Cameron8232e282012-02-24 08:17:31 +010037
Jens Axboe63a130b2012-03-06 20:08:59 +010038static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010039static const char *gfio_graph_font;
Jens Axboe8577f4f2012-03-09 19:28:27 +010040static unsigned int gfio_graph_limit = 100;
Stephen M. Cameron814479d2012-03-15 07:58:14 +010041static GdkColor white;
Jens Axboe63a130b2012-03-06 20:08:59 +010042
Jens Axboe6b79c802012-03-08 10:51:36 +010043static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010044
Stephen M. Cameronf3074002012-02-24 08:17:30 +010045typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
46
Jens Axboe3e47bd22012-02-29 13:45:02 +010047static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010048static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010049static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010050
51static struct button_spec {
52 const char *buttontext;
53 clickfunction f;
Jens Axboe014f4022012-03-15 14:03:01 +010054 const char *tooltiptext[2];
55 const int start_sensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010056} buttonspeclist[] = {
Jens Axboe53e0e852012-03-15 19:38:01 +010057 {
58 .buttontext = "Connect",
59 .f = connect_clicked,
60 .tooltiptext = { "Disconnect from host", "Connect to host" },
61 .start_sensitive = 1,
62 },
63 {
64 .buttontext = "Send",
65 .f = send_clicked,
66 .tooltiptext = { "Send job description to host", NULL },
67 .start_sensitive = 0,
68 },
69 {
70 .buttontext = "Start Job",
71 .f = start_job_clicked,
72 .tooltiptext = { "Start the current job on the server", NULL },
73 .start_sensitive = 0,
74 },
Jens Axboee0681f32012-03-06 12:14:42 +010075};
76
Jens Axboe9988ca72012-03-09 15:14:06 +010077static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
78static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboe49c34172012-03-16 12:05:17 +010079static void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +010080
Jens Axboe2f99deb2012-03-09 14:37:29 +010081static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010082{
Jens Axboe2f99deb2012-03-09 14:37:29 +010083 struct graph *g;
84
85 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +010086 graph_title(g, "IOPS (IOs/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +010087 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +010088 graph_add_label(g, "Read IOPS");
89 graph_add_label(g, "Write IOPS");
90 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
91 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +010092 line_graph_set_data_count_limit(g, gfio_graph_limit);
Jens Axboed8fbeef2012-03-14 10:25:44 +010093 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +010094 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +010095}
96
Jens Axboe2f99deb2012-03-09 14:37:29 +010097static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010098{
Jens Axboe2f99deb2012-03-09 14:37:29 +010099 struct graph *g;
100
101 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100102 graph_title(g, "Bandwidth (bytes/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100103 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100104 graph_add_label(g, "Read Bandwidth");
105 graph_add_label(g, "Write Bandwidth");
106 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
107 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100108 graph_set_base_offset(g, 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100109 line_graph_set_data_count_limit(g, 100);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100110 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100111 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100112}
113
Jens Axboe2f99deb2012-03-09 14:37:29 +0100114static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100115{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100116 g->iops_graph = setup_iops_graph();
117 g->bandwidth_graph = setup_bandwidth_graph();
118}
119
120static void clear_ge_ui_info(struct gui_entry *ge)
121{
122 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
123 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
124 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
125 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100126#if 0
127 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100128 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100129#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100130 multitext_update_entry(&ge->eta.iotype, 0, "");
Jens Axboe99d633a2012-03-15 15:55:04 +0100131 multitext_update_entry(&ge->eta.bs, 0, "");
Jens Axboec80b74b2012-03-12 10:23:28 +0100132 multitext_update_entry(&ge->eta.ioengine, 0, "");
133 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100134 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
135 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
136 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
137 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
138 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
139 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100140}
141
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100142static void show_info_dialog(struct gui *ui, const char *title,
143 const char *message)
144{
145 GtkWidget *dialog, *content, *label;
146
147 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
148 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
149 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
150
151 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
152 label = gtk_label_new(message);
153 gtk_container_add(GTK_CONTAINER(content), label);
154 gtk_widget_show_all(dialog);
155 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
156 gtk_dialog_run(GTK_DIALOG(dialog));
157 gtk_widget_destroy(dialog);
158}
159
Jens Axboe781ccba2012-03-15 09:44:42 +0100160static void set_menu_entry_text(struct gui *ui, const char *path,
161 const char *text)
162{
163 GtkWidget *w;
164
165 w = gtk_ui_manager_get_widget(ui->uimanager, path);
166 if (w)
167 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
168 else
169 fprintf(stderr, "gfio: can't find path %s\n", path);
170}
171
172
173static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
174{
175 GtkWidget *w;
176
177 w = gtk_ui_manager_get_widget(ui->uimanager, path);
178 if (w)
179 gtk_widget_set_sensitive(w, show);
180 else
181 fprintf(stderr, "gfio: can't find path %s\n", path);
182}
183
184static void set_job_menu_visible(struct gui *ui, int visible)
185{
186 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
187}
188
189static void set_view_results_visible(struct gui *ui, int visible)
190{
191 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
192}
193
Jens Axboe014f4022012-03-15 14:03:01 +0100194static const char *get_button_tooltip(struct button_spec *s, int sensitive)
195{
196 if (s->tooltiptext[sensitive])
197 return s->tooltiptext[sensitive];
198
199 return s->tooltiptext[0];
200}
201
202static GtkWidget *add_button(GtkWidget *buttonbox,
203 struct button_spec *buttonspec, gpointer data)
204{
205 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
206 gboolean sens = buttonspec->start_sensitive;
207
208 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
209 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
210
211 sens = buttonspec->start_sensitive;
212 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
213 gtk_widget_set_sensitive(button, sens);
214
215 return button;
216}
217
218static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
219 int nbuttons)
220{
221 int i;
222
223 for (i = 0; i < nbuttons; i++)
224 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
225}
226
Jens Axboe85dd01e2012-03-12 14:33:16 +0100227/*
228 * Update sensitivity of job buttons and job menu items, based on the
229 * state of the client.
230 */
231static void update_button_states(struct gui *ui, struct gui_entry *ge)
232{
233 unsigned int connect_state, send_state, start_state, edit_state;
234 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100235
236 switch (ge->state) {
237 default: {
238 char tmp[80];
239
240 sprintf(tmp, "Bad client state: %u\n", ge->state);
241 show_info_dialog(ui, "Error", tmp);
242 /* fall through to new state */
243 }
244
245 case GE_STATE_NEW:
246 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100247 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100248 connect_str = "Connect";
249 send_state = 0;
250 start_state = 0;
251 break;
252 case GE_STATE_CONNECTED:
253 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100254 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100255 connect_str = "Disconnect";
256 send_state = 1;
257 start_state = 0;
258 break;
259 case GE_STATE_JOB_SENT:
260 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100261 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100262 connect_str = "Disconnect";
263 send_state = 0;
264 start_state = 1;
265 break;
266 case GE_STATE_JOB_STARTED:
267 connect_state = 1;
268 edit_state = 1;
269 connect_str = "Disconnect";
270 send_state = 0;
271 start_state = 1;
272 break;
273 case GE_STATE_JOB_RUNNING:
274 connect_state = 1;
275 edit_state = 0;
276 connect_str = "Disconnect";
277 send_state = 0;
278 start_state = 0;
279 break;
280 case GE_STATE_JOB_DONE:
281 connect_state = 1;
282 edit_state = 0;
283 connect_str = "Connect";
284 send_state = 0;
285 start_state = 0;
286 break;
287 }
288
Jens Axboe53e0e852012-03-15 19:38:01 +0100289 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
290 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
291 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
292 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
293 gtk_widget_set_tooltip_text(ge->button[GFIO_BUTTON_CONNECT], get_button_tooltip(&buttonspeclist[GFIO_BUTTON_CONNECT], connect_state));
Jens Axboe85dd01e2012-03-12 14:33:16 +0100294
Jens Axboe781ccba2012-03-15 09:44:42 +0100295 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
296 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100297
Jens Axboe781ccba2012-03-15 09:44:42 +0100298 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
299 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
300 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100301
Jens Axboe781ccba2012-03-15 09:44:42 +0100302 if (ge->client && ge->client->nr_results)
303 set_view_results_visible(ui, 1);
304 else
305 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100306}
307
308static void gfio_set_state(struct gui_entry *ge, unsigned int state)
309{
310 ge->state = state;
311 update_button_states(ge->ui, ge);
312}
313
Jens Axboe9b260bd2012-03-06 11:02:52 +0100314static void gfio_ui_setup_log(struct gui *ui)
315{
316 GtkTreeSelection *selection;
317 GtkListStore *model;
318 GtkWidget *tree_view;
319
320 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
321
322 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
323 gtk_widget_set_can_focus(tree_view, FALSE);
324
325 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
326 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100327 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
328 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100329
330 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
331 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
332 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100333 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100334
335 ui->log_model = model;
336 ui->log_tree = tree_view;
337}
338
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100339static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
340 fio_fp64_t *plist,
341 unsigned int len,
342 double xdim, double ydim)
343{
344 struct graph *g;
345 int i;
346
347 g = graph_new(xdim, ydim, gfio_graph_font);
348 graph_title(g, title);
349 graph_x_title(g, "Percentile");
350
351 for (i = 0; i < len; i++) {
352 char fbuf[8];
353
354 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
355 graph_add_label(g, fbuf);
356 graph_add_data(g, fbuf, (double) ovals[i]);
357 }
358
359 return g;
360}
361
Jens Axboea2697902012-03-05 16:43:49 +0100362static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
363 fio_fp64_t *plist,
364 unsigned int len,
365 const char *base,
366 unsigned int scale)
367{
368 GType types[FIO_IO_U_LIST_MAX_LEN];
369 GtkWidget *tree_view;
370 GtkTreeSelection *selection;
371 GtkListStore *model;
372 GtkTreeIter iter;
373 int i;
374
375 for (i = 0; i < len; i++)
376 types[i] = G_TYPE_INT;
377
378 model = gtk_list_store_newv(len, types);
379
380 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
381 gtk_widget_set_can_focus(tree_view, FALSE);
382
Jens Axboe661f7412012-03-06 13:55:45 +0100383 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
384 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
385
Jens Axboea2697902012-03-05 16:43:49 +0100386 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
387 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
388
389 for (i = 0; i < len; i++) {
390 char fbuf[8];
391
392 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
393 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
394 }
395
396 gtk_list_store_append(model, &iter);
397
Jens Axboee0681f32012-03-06 12:14:42 +0100398 for (i = 0; i < len; i++) {
399 if (scale)
400 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100401 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100402 }
Jens Axboea2697902012-03-05 16:43:49 +0100403
404 return tree_view;
405}
406
Jens Axboe09d574e2012-03-15 10:45:48 +0100407static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100408{
409 struct graph *g = p;
410 cairo_t *cr;
411
412 cr = gdk_cairo_create(w->window);
413#if 0
414 if (graph_has_tooltips(g)) {
415 g_object_set(w, "has-tooltip", TRUE, NULL);
416 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
417 }
418#endif
419 cairo_set_source_rgb(cr, 0, 0, 0);
420 bar_graph_draw(g, cr);
421 cairo_destroy(cr);
422
423 return FALSE;
424}
425
Jens Axboe09d574e2012-03-15 10:45:48 +0100426static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
427 gpointer data)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100428{
429 struct graph *g = data;
430
431 graph_set_size(g, w->allocation.width, w->allocation.height);
432 graph_set_size(g, w->allocation.width, w->allocation.height);
433 graph_set_position(g, 0, 0);
434 return TRUE;
435}
436
437static void gfio_show_clat_percentiles(struct gfio_client *gc,
438 GtkWidget *vbox, struct thread_stat *ts,
Jens Axboea2697902012-03-05 16:43:49 +0100439 int ddir)
440{
441 unsigned int *io_u_plat = ts->io_u_plat[ddir];
442 unsigned long nr = ts->clat_stat[ddir].samples;
443 fio_fp64_t *plist = ts->percentile_list;
444 unsigned int *ovals, len, minv, maxv, scale_down;
445 const char *base;
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100446 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
Jens Axboe09d574e2012-03-15 10:45:48 +0100447 struct gui_entry *ge = gc->ge;
Jens Axboea2697902012-03-05 16:43:49 +0100448 char tmp[64];
449
450 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
451 if (!len)
452 goto out;
453
454 /*
455 * We default to usecs, but if the value range is such that we
456 * should scale down to msecs, do that.
457 */
458 if (minv > 2000 && maxv > 99999) {
459 scale_down = 1;
460 base = "msec";
461 } else {
462 scale_down = 0;
463 base = "usec";
464 }
465
Jens Axboea2697902012-03-05 16:43:49 +0100466 sprintf(tmp, "Completion percentiles (%s)", base);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100467 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
Jens Axboe09d574e2012-03-15 10:45:48 +0100468 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100469
Jens Axboea2697902012-03-05 16:43:49 +0100470 frame = gtk_frame_new(tmp);
471 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
472
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100473 completion_vbox = gtk_vbox_new(FALSE, 3);
474 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
Jens Axboea2697902012-03-05 16:43:49 +0100475 hbox = gtk_hbox_new(FALSE, 3);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100476 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
477 drawing_area = gtk_drawing_area_new();
478 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
479 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
480 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
Jens Axboe09d574e2012-03-15 10:45:48 +0100481 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
482 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->clat_graph);
Jens Axboea2697902012-03-05 16:43:49 +0100483
484 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
485out:
486 if (ovals)
487 free(ovals);
488}
489
Jens Axboe3650a3c2012-03-05 14:09:03 +0100490static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
491 unsigned long max, double mean, double dev)
492{
493 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100494 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100495 char *minp, *maxp;
496 char tmp[64];
497
498 if (!usec_to_msec(&min, &max, &mean, &dev))
499 base = "(msec)";
500
501 minp = num2str(min, 6, 1, 0);
502 maxp = num2str(max, 6, 1, 0);
503
Jens Axboe3650a3c2012-03-05 14:09:03 +0100504 sprintf(tmp, "%s %s", name, base);
505 frame = gtk_frame_new(tmp);
506 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
507
Jens Axboe3650a3c2012-03-05 14:09:03 +0100508 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100509 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100510
511 label = new_info_label_in_frame(hbox, "Minimum");
512 gtk_label_set_text(GTK_LABEL(label), minp);
513 label = new_info_label_in_frame(hbox, "Maximum");
514 gtk_label_set_text(GTK_LABEL(label), maxp);
515 label = new_info_label_in_frame(hbox, "Average");
516 sprintf(tmp, "%5.02f", mean);
517 gtk_label_set_text(GTK_LABEL(label), tmp);
518 label = new_info_label_in_frame(hbox, "Standard deviation");
519 sprintf(tmp, "%5.02f", dev);
520 gtk_label_set_text(GTK_LABEL(label), tmp);
521
522 free(minp);
523 free(maxp);
524
525}
526
Jens Axboeca850992012-03-05 20:04:43 +0100527#define GFIO_CLAT 1
528#define GFIO_SLAT 2
529#define GFIO_LAT 4
530
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100531static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
532 struct group_run_stats *rs,
Jens Axboe3650a3c2012-03-05 14:09:03 +0100533 struct thread_stat *ts, int ddir)
534{
535 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100536 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100537 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100538 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100539 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100540 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100541 char *io_p, *bw_p, *iops_p;
542 int i2p;
543
544 if (!ts->runtime[ddir])
545 return;
546
547 i2p = is_power_of_2(rs->kb_base);
548 runt = ts->runtime[ddir];
549
550 bw = (1000 * ts->io_bytes[ddir]) / runt;
551 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
552 bw_p = num2str(bw, 6, 1, i2p);
553
554 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
555 iops_p = num2str(iops, 6, 1, 0);
556
557 box = gtk_hbox_new(FALSE, 3);
558 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
559
560 frame = gtk_frame_new(ddir_label[ddir]);
561 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
562
Jens Axboe0b761302012-03-05 20:44:11 +0100563 main_vbox = gtk_vbox_new(FALSE, 3);
564 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100565
566 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100567 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100568
569 label = new_info_label_in_frame(box, "IO");
570 gtk_label_set_text(GTK_LABEL(label), io_p);
571 label = new_info_label_in_frame(box, "Bandwidth");
572 gtk_label_set_text(GTK_LABEL(label), bw_p);
573 label = new_info_label_in_frame(box, "IOPS");
574 gtk_label_set_text(GTK_LABEL(label), iops_p);
575 label = new_info_label_in_frame(box, "Runtime (msec)");
576 label_set_int_value(label, ts->runtime[ddir]);
577
Jens Axboee0681f32012-03-06 12:14:42 +0100578 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100579 double p_of_agg = 100.0;
580 const char *bw_str = "KB";
581 char tmp[32];
582
583 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100584 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100585 if (p_of_agg > 100.0)
586 p_of_agg = 100.0;
587 }
588
Jens Axboee0681f32012-03-06 12:14:42 +0100589 if (mean[0] > 999999.9) {
590 min[0] /= 1000.0;
591 max[0] /= 1000.0;
592 mean[0] /= 1000.0;
593 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100594 bw_str = "MB";
595 }
596
Jens Axboe0b761302012-03-05 20:44:11 +0100597 sprintf(tmp, "Bandwidth (%s)", bw_str);
598 frame = gtk_frame_new(tmp);
599 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100600
Jens Axboe0b761302012-03-05 20:44:11 +0100601 box = gtk_hbox_new(FALSE, 3);
602 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100603
Jens Axboe0b761302012-03-05 20:44:11 +0100604 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100605 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100606 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100607 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100608 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100609 sprintf(tmp, "%3.2f%%", p_of_agg);
610 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100611 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100612 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100613 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100614 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100615 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100616 gtk_label_set_text(GTK_LABEL(label), tmp);
617 }
618
Jens Axboee0681f32012-03-06 12:14:42 +0100619 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100620 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100621 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100622 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100623 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100624 flags |= GFIO_LAT;
625
626 if (flags) {
627 frame = gtk_frame_new("Latency");
628 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
629
630 vbox = gtk_vbox_new(FALSE, 3);
631 gtk_container_add(GTK_CONTAINER(frame), vbox);
632
633 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100634 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100635 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100636 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100637 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100638 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100639 }
640
641 if (ts->clat_percentiles)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100642 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
Jens Axboe2b089892012-03-06 08:09:17 +0100643
Jens Axboe3650a3c2012-03-05 14:09:03 +0100644 free(io_p);
645 free(bw_p);
646 free(iops_p);
647}
648
Jens Axboe09d574e2012-03-15 10:45:48 +0100649static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
650 const char **labels,
651 unsigned int len,
652 double xdim, double ydim)
653{
654 struct graph *g;
655 int i;
656
657 g = graph_new(xdim, ydim, gfio_graph_font);
658 graph_title(g, title);
659 graph_x_title(g, "Buckets");
660
661 for (i = 0; i < len; i++) {
662 graph_add_label(g, labels[i]);
663 graph_add_data(g, labels[i], lat[i]);
664 }
665
666 return g;
667}
668
669static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
670 int num)
Jens Axboee5bd1342012-03-05 21:38:12 +0100671{
672 GtkWidget *tree_view;
673 GtkTreeSelection *selection;
674 GtkListStore *model;
675 GtkTreeIter iter;
676 GType *types;
Jens Axboe09d574e2012-03-15 10:45:48 +0100677 int i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100678
679 types = malloc(num * sizeof(GType));
680
681 for (i = 0; i < num; i++)
682 types[i] = G_TYPE_STRING;
683
684 model = gtk_list_store_newv(num, types);
685 free(types);
686 types = NULL;
687
688 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
689 gtk_widget_set_can_focus(tree_view, FALSE);
690
Jens Axboe661f7412012-03-06 13:55:45 +0100691 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
692 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
693
Jens Axboee5bd1342012-03-05 21:38:12 +0100694 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
695 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
696
697 for (i = 0; i < num; i++)
698 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
699
700 gtk_list_store_append(model, &iter);
701
702 for (i = 0; i < num; i++) {
703 char fbuf[32];
704
705 if (lat[i] <= 0.0)
706 sprintf(fbuf, "0.00");
707 else
708 sprintf(fbuf, "%3.2f%%", lat[i]);
709
710 gtk_list_store_set(model, &iter, i, fbuf, -1);
711 }
712
713 return tree_view;
714}
715
Jens Axboe09d574e2012-03-15 10:45:48 +0100716static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
717 struct thread_stat *ts)
Jens Axboee5bd1342012-03-05 21:38:12 +0100718{
Jens Axboe09d574e2012-03-15 10:45:48 +0100719 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
720 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
721 "250u", "500u", "750u", "1m", "2m",
722 "4m", "10m", "20m", "50m", "100m",
723 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
724 int start, end, i;
725 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
726 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
727 struct gui_entry *ge = gc->ge;
Jens Axboee5bd1342012-03-05 21:38:12 +0100728
Jens Axboe09d574e2012-03-15 10:45:48 +0100729 stat_calc_lat_u(ts, io_u_lat);
730 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
Jens Axboee5bd1342012-03-05 21:38:12 +0100731
Jens Axboe09d574e2012-03-15 10:45:48 +0100732 /*
733 * Found out which first bucket has entries, and which last bucket
734 */
735 start = end = -1U;
736 for (i = 0; i < total; i++) {
737 if (io_u_lat[i] == 0.00)
738 continue;
Jens Axboee5bd1342012-03-05 21:38:12 +0100739
Jens Axboe09d574e2012-03-15 10:45:48 +0100740 if (start == -1U)
741 start = i;
742 end = i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100743 }
744
Jens Axboe09d574e2012-03-15 10:45:48 +0100745 /*
746 * No entries...
747 */
748 if (start == -1U)
749 return;
750
751 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
752 ge->lat_bucket_graph = setup_lat_bucket_graph("Latency Buckets", &io_u_lat[start], &ranges[start], end - start + 1, 700.0, 300.0);
Jens Axboee5bd1342012-03-05 21:38:12 +0100753
Jens Axboe09d574e2012-03-15 10:45:48 +0100754 frame = gtk_frame_new("Latency buckets");
755 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
756
757 completion_vbox = gtk_vbox_new(FALSE, 3);
758 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
759 hbox = gtk_hbox_new(FALSE, 3);
760 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
761
762 drawing_area = gtk_drawing_area_new();
763 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
764 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
765 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
766 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
767 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
768
769 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
Jens Axboee5bd1342012-03-05 21:38:12 +0100770}
771
Jens Axboe2e331012012-03-05 22:07:54 +0100772static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
773{
774 GtkWidget *box, *frame, *entry;
775 double usr_cpu, sys_cpu;
776 unsigned long runtime;
777 char tmp[32];
778
779 runtime = ts->total_run_time;
780 if (runtime) {
781 double runt = (double) runtime;
782
783 usr_cpu = (double) ts->usr_time * 100 / runt;
784 sys_cpu = (double) ts->sys_time * 100 / runt;
785 } else {
786 usr_cpu = 0;
787 sys_cpu = 0;
788 }
789
790 frame = gtk_frame_new("OS resources");
791 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
792
793 box = gtk_hbox_new(FALSE, 3);
794 gtk_container_add(GTK_CONTAINER(frame), box);
795
796 entry = new_info_entry_in_frame(box, "User CPU");
797 sprintf(tmp, "%3.2f%%", usr_cpu);
798 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
799 entry = new_info_entry_in_frame(box, "System CPU");
800 sprintf(tmp, "%3.2f%%", sys_cpu);
801 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
802 entry = new_info_entry_in_frame(box, "Context switches");
803 entry_set_int_value(entry, ts->ctx);
804 entry = new_info_entry_in_frame(box, "Major faults");
805 entry_set_int_value(entry, ts->majf);
806 entry = new_info_entry_in_frame(box, "Minor faults");
807 entry_set_int_value(entry, ts->minf);
808}
Jens Axboe19998db2012-03-06 09:17:59 +0100809static void gfio_add_sc_depths_tree(GtkListStore *model,
810 struct thread_stat *ts, unsigned int len,
811 int submit)
812{
813 double io_u_dist[FIO_IO_U_MAP_NR];
814 GtkTreeIter iter;
815 /* Bits 0, and 3-8 */
816 const int add_mask = 0x1f9;
817 int i, j;
818
819 if (submit)
820 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
821 else
822 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
823
824 gtk_list_store_append(model, &iter);
825
826 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
827
828 for (i = 1, j = 0; i < len; i++) {
829 char fbuf[32];
830
831 if (!(add_mask & (1UL << (i - 1))))
832 sprintf(fbuf, "0.0%%");
833 else {
834 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
835 j++;
836 }
837
838 gtk_list_store_set(model, &iter, i, fbuf, -1);
839 }
840
841}
842
843static void gfio_add_total_depths_tree(GtkListStore *model,
844 struct thread_stat *ts, unsigned int len)
845{
846 double io_u_dist[FIO_IO_U_MAP_NR];
847 GtkTreeIter iter;
848 /* Bits 1-6, and 8 */
849 const int add_mask = 0x17e;
850 int i, j;
851
852 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
853
854 gtk_list_store_append(model, &iter);
855
856 gtk_list_store_set(model, &iter, 0, "Total", -1);
857
858 for (i = 1, j = 0; i < len; i++) {
859 char fbuf[32];
860
861 if (!(add_mask & (1UL << (i - 1))))
862 sprintf(fbuf, "0.0%%");
863 else {
864 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
865 j++;
866 }
867
868 gtk_list_store_set(model, &iter, i, fbuf, -1);
869 }
870
871}
Jens Axboe2e331012012-03-05 22:07:54 +0100872
873static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
874{
Jens Axboe2e331012012-03-05 22:07:54 +0100875 GtkWidget *frame, *box, *tree_view;
876 GtkTreeSelection *selection;
877 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100878 GType types[FIO_IO_U_MAP_NR + 1];
879 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100880#define NR_LABELS 10
881 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100882
883 frame = gtk_frame_new("IO depths");
884 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
885
886 box = gtk_hbox_new(FALSE, 3);
887 gtk_container_add(GTK_CONTAINER(frame), box);
888
Jens Axboe19998db2012-03-06 09:17:59 +0100889 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100890 types[i] = G_TYPE_STRING;
891
Jens Axboe19998db2012-03-06 09:17:59 +0100892 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100893
894 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
895 gtk_widget_set_can_focus(tree_view, FALSE);
896
Jens Axboe661f7412012-03-06 13:55:45 +0100897 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
898 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
899
Jens Axboe2e331012012-03-05 22:07:54 +0100900 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
901 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
902
Jens Axboe19998db2012-03-06 09:17:59 +0100903 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100904 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
905
Jens Axboe19998db2012-03-06 09:17:59 +0100906 gfio_add_total_depths_tree(model, ts, NR_LABELS);
907 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
908 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +0100909
910 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
911}
912
Jens Axboef9d40b42012-03-06 09:52:49 +0100913static gboolean results_window_delete(GtkWidget *w, gpointer data)
914{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100915 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +0100916
917 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100918 ge->results_window = NULL;
919 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +0100920 return TRUE;
921}
922
Jens Axboe17b97212012-03-14 20:17:57 +0100923static void results_close(GtkWidget *w, gpointer *data)
924{
925 struct gui_entry *ge = (struct gui_entry *) data;
926
927 gtk_widget_destroy(ge->results_window);
928}
929
930static GtkActionEntry results_menu_items[] = {
931 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
932 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
933 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
934};
935static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
936
937static const gchar *results_ui_string = " \
938 <ui> \
939 <menubar name=\"MainMenu\"> \
940 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
941 <menuitem name=\"Close\" action=\"CloseFile\" /> \
942 </menu> \
943 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
944 </menu>\
945 </menubar> \
946 </ui> \
947";
948
949static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
950{
951 GtkActionGroup *action_group;
952 GtkWidget *widget;
953 GError *error = 0;
954
955 ge->results_uimanager = gtk_ui_manager_new();
956
957 action_group = gtk_action_group_new("ResultsMenu");
958 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
959
960 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
961 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
962
963 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
964
965 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
966 return widget;
967}
968
Jens Axboe2f99deb2012-03-09 14:37:29 +0100969static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +0100970{
Jens Axboe17b97212012-03-14 20:17:57 +0100971 GtkWidget *win, *notebook, *vbox;
Jens Axboef9d40b42012-03-06 09:52:49 +0100972
Jens Axboe2f99deb2012-03-09 14:37:29 +0100973 if (ge->results_window)
974 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100975
976 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
977 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +0100978 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100979 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
980 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +0100981
Jens Axboe17b97212012-03-14 20:17:57 +0100982 vbox = gtk_vbox_new(FALSE, 0);
983 gtk_container_add(GTK_CONTAINER(win), vbox);
984
985 ge->results_menu = get_results_menubar(win, ge);
986 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
987
Jens Axboef9d40b42012-03-06 09:52:49 +0100988 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +0100989 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
990 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboe17b97212012-03-14 20:17:57 +0100991 gtk_container_add(GTK_CONTAINER(vbox), notebook);
Jens Axboef9d40b42012-03-06 09:52:49 +0100992
Jens Axboe2f99deb2012-03-09 14:37:29 +0100993 ge->results_window = win;
994 ge->results_notebook = notebook;
995 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +0100996}
997
Jens Axboe7da23b42012-03-15 13:45:02 +0100998static void disk_util_destroy(GtkWidget *w, gpointer data)
Jens Axboe3650a3c2012-03-05 14:09:03 +0100999{
Jens Axboe7da23b42012-03-15 13:45:02 +01001000 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001001
Jens Axboe7da23b42012-03-15 13:45:02 +01001002 ge->disk_util_vbox = NULL;
1003 gtk_widget_destroy(w);
Jens Axboe781ccba2012-03-15 09:44:42 +01001004}
Jens Axboe3650a3c2012-03-05 14:09:03 +01001005
Jens Axboef0602d72012-03-15 15:38:02 +01001006static GtkWidget *get_scrolled_window(gint border_width)
1007{
1008 GtkWidget *scroll;
1009
1010 scroll = gtk_scrolled_window_new(NULL, NULL);
1011 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1012 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1013
1014 return scroll;
1015}
1016
1017static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1018{
1019 GtkWidget *vbox, *box, *scroll, *res_notebook;
1020
1021 if (ge->disk_util_vbox)
1022 return ge->disk_util_vbox;
1023
1024 scroll = get_scrolled_window(5);
1025 vbox = gtk_vbox_new(FALSE, 3);
1026 box = gtk_hbox_new(FALSE, 0);
1027 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1028
1029 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1030 res_notebook = get_results_window(ge);
1031
1032 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1033 ge->disk_util_vbox = box;
1034 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1035
1036 return ge->disk_util_vbox;
1037}
1038
Jens Axboe7da23b42012-03-15 13:45:02 +01001039static int __gfio_disk_util_show(GtkWidget *res_notebook,
1040 struct gfio_client *gc, struct cmd_du_pdu *p)
Jens Axboe781ccba2012-03-15 09:44:42 +01001041{
Jens Axboef0602d72012-03-15 15:38:02 +01001042 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
Jens Axboe7da23b42012-03-15 13:45:02 +01001043 struct gui_entry *ge = gc->ge;
Jens Axboe604cfe32012-03-07 19:51:36 +01001044 double util;
1045 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001046
Jens Axboef0602d72012-03-15 15:38:02 +01001047 util_vbox = gfio_disk_util_get_vbox(ge);
Jens Axboee0681f32012-03-06 12:14:42 +01001048
1049 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboef0602d72012-03-15 15:38:02 +01001050 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
Jens Axboee0681f32012-03-06 12:14:42 +01001051
1052 frame = gtk_frame_new((char *) p->dus.name);
1053 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1054
1055 box = gtk_vbox_new(FALSE, 3);
1056 gtk_container_add(GTK_CONTAINER(frame), box);
1057
1058 frame = gtk_frame_new("Read");
1059 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1060 vbox = gtk_hbox_new(TRUE, 3);
1061 gtk_container_add(GTK_CONTAINER(frame), vbox);
1062 entry = new_info_entry_in_frame(vbox, "IOs");
1063 entry_set_int_value(entry, p->dus.ios[0]);
1064 entry = new_info_entry_in_frame(vbox, "Merges");
1065 entry_set_int_value(entry, p->dus.merges[0]);
1066 entry = new_info_entry_in_frame(vbox, "Sectors");
1067 entry_set_int_value(entry, p->dus.sectors[0]);
1068 entry = new_info_entry_in_frame(vbox, "Ticks");
1069 entry_set_int_value(entry, p->dus.ticks[0]);
1070
1071 frame = gtk_frame_new("Write");
1072 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1073 vbox = gtk_hbox_new(TRUE, 3);
1074 gtk_container_add(GTK_CONTAINER(frame), vbox);
1075 entry = new_info_entry_in_frame(vbox, "IOs");
1076 entry_set_int_value(entry, p->dus.ios[1]);
1077 entry = new_info_entry_in_frame(vbox, "Merges");
1078 entry_set_int_value(entry, p->dus.merges[1]);
1079 entry = new_info_entry_in_frame(vbox, "Sectors");
1080 entry_set_int_value(entry, p->dus.sectors[1]);
1081 entry = new_info_entry_in_frame(vbox, "Ticks");
1082 entry_set_int_value(entry, p->dus.ticks[1]);
1083
1084 frame = gtk_frame_new("Shared");
1085 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1086 vbox = gtk_hbox_new(TRUE, 3);
1087 gtk_container_add(GTK_CONTAINER(frame), vbox);
1088 entry = new_info_entry_in_frame(vbox, "IO ticks");
1089 entry_set_int_value(entry, p->dus.io_ticks);
1090 entry = new_info_entry_in_frame(vbox, "Time in queue");
1091 entry_set_int_value(entry, p->dus.time_in_queue);
1092
Jens Axboe604cfe32012-03-07 19:51:36 +01001093 util = 0.0;
1094 if (p->dus.msec)
1095 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1096 if (util > 100.0)
1097 util = 100.0;
1098
1099 sprintf(tmp, "%3.2f%%", util);
1100 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1101 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1102
Jens Axboe7da23b42012-03-15 13:45:02 +01001103 gtk_widget_show_all(ge->results_window);
1104 return 0;
1105}
1106
1107static int gfio_disk_util_show(struct gfio_client *gc)
1108{
1109 struct gui_entry *ge = gc->ge;
1110 GtkWidget *res_notebook;
1111 int i;
1112
1113 if (!gc->nr_du)
1114 return 1;
1115
1116 res_notebook = get_results_window(ge);
1117
1118 for (i = 0; i < gc->nr_du; i++) {
1119 struct cmd_du_pdu *p = &gc->du[i];
1120
1121 __gfio_disk_util_show(res_notebook, gc, p);
1122 }
1123
1124 gtk_widget_show_all(ge->results_window);
1125 return 0;
1126}
1127
1128static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1129 struct group_run_stats *rs)
1130{
1131 unsigned int nr = gc->nr_results;
1132
1133 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1134 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1135 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1136 gc->nr_results++;
1137}
1138
1139static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1140 struct thread_stat *ts,
1141 struct group_run_stats *rs)
1142{
1143 GtkWidget *box, *vbox, *entry, *scroll;
1144
1145 scroll = gtk_scrolled_window_new(NULL, NULL);
1146 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1147 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1148
1149 vbox = gtk_vbox_new(FALSE, 3);
1150
1151 box = gtk_hbox_new(FALSE, 0);
1152 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1153
1154 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1155
1156 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1157
1158 entry = new_info_entry_in_frame(box, "Name");
1159 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1160 if (strlen(ts->description)) {
1161 entry = new_info_entry_in_frame(box, "Description");
1162 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1163 }
1164 entry = new_info_entry_in_frame(box, "Group ID");
1165 entry_set_int_value(entry, ts->groupid);
1166 entry = new_info_entry_in_frame(box, "Jobs");
1167 entry_set_int_value(entry, ts->members);
1168 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1169 entry_set_int_value(entry, ts->error);
1170 entry = new_info_entry_in_frame(box, "PID");
1171 entry_set_int_value(entry, ts->pid);
1172
1173 if (ts->io_bytes[DDIR_READ])
1174 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1175 if (ts->io_bytes[DDIR_WRITE])
1176 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1177
1178 gfio_show_latency_buckets(gc, vbox, ts);
1179 gfio_show_cpu_usage(vbox, ts);
1180 gfio_show_io_depths(vbox, ts);
1181}
1182
1183static void gfio_display_end_results(struct gfio_client *gc)
1184{
1185 struct gui_entry *ge = gc->ge;
1186 GtkWidget *res_notebook;
1187 int i;
1188
1189 res_notebook = get_results_window(ge);
1190
1191 for (i = 0; i < gc->nr_results; i++) {
1192 struct end_results *e = &gc->results[i];
1193
1194 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1195 }
1196
1197 if (gfio_disk_util_show(gc))
1198 gtk_widget_show_all(ge->results_window);
1199}
1200
1201static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1202 struct group_run_stats *rs)
1203{
1204 struct gfio_client *gc = client->client_data;
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001205 struct gui_entry *ge = gc->ge;
Jens Axboe7da23b42012-03-15 13:45:02 +01001206
1207 gfio_add_end_results(gc, ts, rs);
1208
1209 gdk_threads_enter();
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001210 if (ge->results_window)
1211 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1212 else
1213 gfio_display_end_results(gc);
Jens Axboe7da23b42012-03-15 13:45:02 +01001214 gdk_threads_leave();
1215}
1216
1217static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1218{
1219 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1220 struct gui *ui = &main_ui;
1221 GtkTreeIter iter;
1222 struct tm *tm;
1223 time_t sec;
1224 char tmp[64], timebuf[80];
1225
1226 sec = p->log_sec;
1227 tm = localtime(&sec);
1228 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1229 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1230
1231 gdk_threads_enter();
1232
1233 gtk_list_store_append(ui->log_model, &iter);
1234 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1235 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1236 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1237 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1238
1239 if (p->level == FIO_LOG_ERR)
1240 view_log(NULL, (gpointer) ui);
1241
1242 gdk_threads_leave();
1243}
1244
1245static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1246{
1247 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1248 struct gfio_client *gc = client->client_data;
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001249 struct gui_entry *ge = gc->ge;
Jens Axboe7da23b42012-03-15 13:45:02 +01001250 unsigned int nr = gc->nr_du;
1251
1252 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1253 memcpy(&gc->du[nr], p, sizeof(*p));
1254 gc->nr_du++;
1255
1256 gdk_threads_enter();
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001257 if (ge->results_window)
1258 __gfio_disk_util_show(ge->results_notebook, gc, p);
1259 else
1260 gfio_disk_util_show(gc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001261 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001262}
1263
Jens Axboe3650a3c2012-03-05 14:09:03 +01001264extern int sum_stat_clients;
1265extern struct thread_stat client_ts;
1266extern struct group_run_stats client_gs;
1267
1268static int sum_stat_nr;
1269
Jens Axboe89e5fad2012-03-05 09:21:12 +01001270static void gfio_thread_status_op(struct fio_client *client,
1271 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001272{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001273 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1274
1275 gfio_display_ts(client, &p->ts, &p->rs);
1276
1277 if (sum_stat_clients == 1)
1278 return;
1279
1280 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1281 sum_group_stats(&client_gs, &p->rs);
1282
1283 client_ts.members++;
Jens Axboe2f122b12012-03-15 13:10:19 +01001284 client_ts.thread_number = p->ts.thread_number;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001285 client_ts.groupid = p->ts.groupid;
1286
1287 if (++sum_stat_nr == sum_stat_clients) {
1288 strcpy(client_ts.name, "All clients");
1289 gfio_display_ts(client, &client_ts, &client_gs);
1290 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001291}
1292
Jens Axboe89e5fad2012-03-05 09:21:12 +01001293static void gfio_group_stats_op(struct fio_client *client,
1294 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001295{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001296 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001297}
1298
Jens Axboe2f99deb2012-03-09 14:37:29 +01001299static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1300 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001301{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001302 struct gfio_graphs *g = data;
1303
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001304 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1305 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1306 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1307 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001308 return TRUE;
1309}
1310
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001311static void draw_graph(struct graph *g, cairo_t *cr)
1312{
1313 line_graph_draw(g, cr);
1314 cairo_stroke(cr);
1315}
1316
Jens Axboe93e2db22012-03-13 09:45:22 +01001317static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1318 gboolean keyboard_mode, GtkTooltip *tooltip,
1319 gpointer data)
1320{
1321 struct gfio_graphs *g = data;
1322 const char *text = NULL;
1323
1324 if (graph_contains_xy(g->iops_graph, x, y))
1325 text = graph_find_tooltip(g->iops_graph, x, y);
1326 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1327 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1328
1329 if (text) {
1330 gtk_tooltip_set_text(tooltip, text);
1331 return TRUE;
1332 }
1333
1334 return FALSE;
1335}
1336
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001337static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1338{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001339 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001340 cairo_t *cr;
1341
1342 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001343
1344 if (graph_has_tooltips(g->iops_graph) ||
1345 graph_has_tooltips(g->bandwidth_graph)) {
1346 g_object_set(w, "has-tooltip", TRUE, NULL);
1347 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1348 }
1349
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001350 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001351 draw_graph(g->iops_graph, cr);
1352 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001353 cairo_destroy(cr);
1354
1355 return FALSE;
1356}
1357
Jens Axboe2f99deb2012-03-09 14:37:29 +01001358/*
1359 * Client specific ETA
1360 */
1361static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001362{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001363 struct gfio_client *gc = client->client_data;
1364 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001365 static int eta_good;
1366 char eta_str[128];
1367 char output[256];
1368 char tmp[32];
1369 double perc = 0.0;
1370 int i2p = 0;
1371
Jens Axboe0050e5f2012-03-06 09:23:27 +01001372 gdk_threads_enter();
1373
Jens Axboe3e47bd22012-02-29 13:45:02 +01001374 eta_str[0] = '\0';
1375 output[0] = '\0';
1376
1377 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1378 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1379 eta_to_str(eta_str, je->eta_sec);
1380 }
1381
1382 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001383 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001384 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001385 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001386
1387#if 0
1388 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1389 if (je->m_rate || je->t_rate) {
1390 char *tr, *mr;
1391
1392 mr = num2str(je->m_rate, 4, 0, i2p);
1393 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001394 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001395 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1396 free(tr);
1397 free(mr);
1398 } else if (je->m_iops || je->t_iops)
1399 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001400
Jens Axboe2f99deb2012-03-09 14:37:29 +01001401 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1402 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1403 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1404 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001405#endif
1406
1407 if (je->eta_sec != INT_MAX && je->nr_running) {
1408 char *iops_str[2];
1409 char *rate_str[2];
1410
1411 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1412 strcpy(output, "-.-% done");
1413 else {
1414 eta_good = 1;
1415 perc *= 100.0;
1416 sprintf(output, "%3.1f%% done", perc);
1417 }
1418
1419 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1420 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1421
1422 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1423 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1424
Jens Axboe2f99deb2012-03-09 14:37:29 +01001425 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1426 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1427 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1428 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001429
Jens Axboe93e2db22012-03-13 09:45:22 +01001430 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1431 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1432 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1433 graph_add_xy_data(ge->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001434
1435 free(rate_str[0]);
1436 free(rate_str[1]);
1437 free(iops_str[0]);
1438 free(iops_str[1]);
1439 }
1440
1441 if (eta_str[0]) {
1442 char *dst = output + strlen(output);
1443
1444 sprintf(dst, " - %s", eta_str);
1445 }
1446
Jens Axboe9988ca72012-03-09 15:14:06 +01001447 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001448 gdk_threads_leave();
1449}
1450
1451/*
1452 * Update ETA in main window for all clients
1453 */
1454static void gfio_update_all_eta(struct jobs_eta *je)
1455{
1456 struct gui *ui = &main_ui;
1457 static int eta_good;
1458 char eta_str[128];
1459 char output[256];
1460 double perc = 0.0;
1461 int i2p = 0;
1462
1463 gdk_threads_enter();
1464
1465 eta_str[0] = '\0';
1466 output[0] = '\0';
1467
1468 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1469 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1470 eta_to_str(eta_str, je->eta_sec);
1471 }
1472
1473#if 0
1474 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1475 if (je->m_rate || je->t_rate) {
1476 char *tr, *mr;
1477
1478 mr = num2str(je->m_rate, 4, 0, i2p);
1479 tr = num2str(je->t_rate, 4, 0, i2p);
1480 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1481 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1482 free(tr);
1483 free(mr);
1484 } else if (je->m_iops || je->t_iops)
1485 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1486
1487 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1488 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1489 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1490 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1491#endif
1492
Jens Axboe3863d1a2012-03-09 17:39:05 +01001493 entry_set_int_value(ui->eta.jobs, je->nr_running);
1494
Jens Axboe2f99deb2012-03-09 14:37:29 +01001495 if (je->eta_sec != INT_MAX && je->nr_running) {
1496 char *iops_str[2];
1497 char *rate_str[2];
1498
1499 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1500 strcpy(output, "-.-% done");
1501 else {
1502 eta_good = 1;
1503 perc *= 100.0;
1504 sprintf(output, "%3.1f%% done", perc);
1505 }
1506
1507 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1508 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1509
1510 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1511 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1512
1513 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1514 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1515 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1516 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1517
Jens Axboe93e2db22012-03-13 09:45:22 +01001518 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1519 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1520 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1521 graph_add_xy_data(ui->graphs.bandwidth_graph, "Write Bandwidth", je->elapsed_sec, je->rate[1], rate_str[1]);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001522
Jens Axboe3e47bd22012-02-29 13:45:02 +01001523 free(rate_str[0]);
1524 free(rate_str[1]);
1525 free(iops_str[0]);
1526 free(iops_str[1]);
1527 }
1528
1529 if (eta_str[0]) {
1530 char *dst = output + strlen(output);
1531
1532 sprintf(dst, " - %s", eta_str);
1533 }
1534
Jens Axboe9988ca72012-03-09 15:14:06 +01001535 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001536 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001537}
1538
Stephen M. Camerona1820202012-02-24 08:17:31 +01001539static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1540{
Jens Axboe843ad232012-02-29 11:44:53 +01001541 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001542 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001543 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001544 const char *os, *arch;
1545 char buf[64];
1546
1547 os = fio_get_os_string(probe->os);
1548 if (!os)
1549 os = "unknown";
1550
1551 arch = fio_get_arch_string(probe->arch);
1552 if (!arch)
1553 os = "unknown";
1554
1555 if (!client->name)
1556 client->name = strdup((char *) probe->hostname);
1557
Jens Axboe0050e5f2012-03-06 09:23:27 +01001558 gdk_threads_enter();
1559
Jens Axboe2f99deb2012-03-09 14:37:29 +01001560 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1561 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1562 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001563 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001564 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001565
Jens Axboe85dd01e2012-03-12 14:33:16 +01001566 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001567
1568 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001569}
1570
Jens Axboe9988ca72012-03-09 15:14:06 +01001571static void gfio_update_thread_status(struct gui_entry *ge,
1572 char *status_message, double perc)
1573{
1574 static char message[100];
1575 const char *m = message;
1576
1577 strncpy(message, status_message, sizeof(message) - 1);
1578 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1579 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1580 gtk_widget_queue_draw(main_ui.window);
1581}
1582
1583static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001584{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001585 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001586 static char message[100];
1587 const char *m = message;
1588
1589 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001590 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1591 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1592 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001593}
1594
Jens Axboe35c0ba72012-03-14 10:56:40 +01001595static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001596{
Jens Axboee0681f32012-03-06 12:14:42 +01001597 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001598
Jens Axboe0050e5f2012-03-06 09:23:27 +01001599 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001600 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001601 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001602}
1603
Jens Axboe807f9972012-03-02 10:25:24 +01001604static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1605{
1606 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001607 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001608 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001609 struct gui_entry *ge = gc->ge;
Jens Axboe99d633a2012-03-15 15:55:04 +01001610 char *c1, *c2, *c3, *c4;
1611 char tmp[80];
Jens Axboe807f9972012-03-02 10:25:24 +01001612
Jens Axboe2f122b12012-03-15 13:10:19 +01001613 p->thread_number = le32_to_cpu(p->thread_number);
1614 p->groupid = le32_to_cpu(p->groupid);
Jens Axboedcaeb602012-03-08 19:45:37 +01001615 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001616
Jens Axboe0050e5f2012-03-06 09:23:27 +01001617 gdk_threads_enter();
1618
Jens Axboe2f99deb2012-03-09 14:37:29 +01001619 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1620
Jens Axboe3863d1a2012-03-09 17:39:05 +01001621 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1622 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1623
Jens Axboeddbafc12012-03-15 18:57:03 +01001624 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1625 multitext_add_entry(&ge->eta.iotype, tmp);
Jens Axboe99d633a2012-03-15 15:55:04 +01001626
1627 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1628 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1629 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1630 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1631 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1632 free(c1);
1633 free(c2);
1634 free(c3);
1635 free(c4);
1636 multitext_add_entry(&ge->eta.bs, tmp);
1637
Jens Axboec80b74b2012-03-12 10:23:28 +01001638 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001639
Jens Axboedcaeb602012-03-08 19:45:37 +01001640 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001641 multitext_add_entry(&ge->eta.iodepth, tmp);
1642
1643 multitext_set_entry(&ge->eta.iotype, 0);
Jens Axboe99d633a2012-03-15 15:55:04 +01001644 multitext_set_entry(&ge->eta.bs, 0);
Jens Axboec80b74b2012-03-12 10:23:28 +01001645 multitext_set_entry(&ge->eta.ioengine, 0);
1646 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001647
Jens Axboe85dd01e2012-03-12 14:33:16 +01001648 gfio_set_state(ge, GE_STATE_JOB_SENT);
1649
Jens Axboe0050e5f2012-03-06 09:23:27 +01001650 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001651}
1652
Jens Axboeed727a42012-03-02 12:14:40 +01001653static void gfio_client_timed_out(struct fio_client *client)
1654{
Jens Axboee0681f32012-03-06 12:14:42 +01001655 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001656 char buf[256];
1657
1658 gdk_threads_enter();
1659
Jens Axboe85dd01e2012-03-12 14:33:16 +01001660 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001661 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001662
1663 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001664 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001665
1666 gdk_threads_leave();
1667}
1668
Jens Axboe6b79c802012-03-08 10:51:36 +01001669static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1670{
1671 struct gfio_client *gc = client->client_data;
1672
1673 gdk_threads_enter();
1674
Jens Axboe85dd01e2012-03-12 14:33:16 +01001675 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001676
1677 if (gc->err_entry)
1678 entry_set_int_value(gc->err_entry, client->error);
1679
1680 gdk_threads_leave();
1681}
1682
Jens Axboe85dd01e2012-03-12 14:33:16 +01001683static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1684{
1685 struct gfio_client *gc = client->client_data;
1686
1687 gdk_threads_enter();
1688 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1689 gdk_threads_leave();
1690}
1691
1692static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1693{
1694 struct gfio_client *gc = client->client_data;
1695
1696 gdk_threads_enter();
1697 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1698 gdk_threads_leave();
1699}
1700
Jens Axboe1b427252012-03-14 15:03:03 +01001701static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1702{
Jens Axboe284b1e62012-03-14 21:55:07 +01001703 printf("got iolog: name=%s, type=%u, entries=%u\n", pdu->name, pdu->log_type, pdu->nr_samples);
Jens Axboe1b427252012-03-14 15:03:03 +01001704 free(pdu);
1705}
1706
Stephen M. Camerona1820202012-02-24 08:17:31 +01001707struct client_ops gfio_client_ops = {
Jens Axboe35c0ba72012-03-14 10:56:40 +01001708 .text = gfio_text_op,
Jens Axboe0420ba62012-02-29 11:16:52 +01001709 .disk_util = gfio_disk_util_op,
1710 .thread_status = gfio_thread_status_op,
1711 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001712 .jobs_eta = gfio_update_client_eta,
1713 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001714 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001715 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001716 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001717 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001718 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001719 .start = gfio_client_start,
1720 .job_start = gfio_client_job_start,
Jens Axboe1b427252012-03-14 15:03:03 +01001721 .iolog = gfio_client_iolog,
Jens Axboe6433ee02012-03-09 20:10:51 +01001722 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001723 .stay_connected = 1,
Jens Axboe46bcd492012-03-14 11:31:21 +01001724 .client_type = FIO_CLIENT_TYPE_GUI,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001725};
1726
Jens Axboe0fd18982012-03-14 10:34:48 +01001727/*
1728 * FIXME: need more handling here
1729 */
1730static void ge_destroy(struct gui_entry *ge)
1731{
1732 struct gfio_client *gc = ge->client;
1733
1734 if (gc && gc->client) {
1735 if (ge->state >= GE_STATE_CONNECTED)
1736 fio_client_terminate(gc->client);
1737
1738 fio_put_client(gc->client);
1739 }
1740
1741 flist_del(&ge->list);
1742 free(ge);
1743}
1744
1745static void ge_widget_destroy(GtkWidget *w, gpointer data)
1746{
Jens Axboe0fd18982012-03-14 10:34:48 +01001747}
1748
1749static void gfio_quit(struct gui *ui)
1750{
1751 struct gui_entry *ge;
1752
1753 while (!flist_empty(&ui->list)) {
1754 ge = flist_entry(ui->list.next, struct gui_entry, list);
1755 ge_destroy(ge);
1756 }
1757
1758 gtk_main_quit();
1759}
1760
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001761static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1762 __attribute__((unused)) gpointer data)
1763{
Jens Axboe0fd18982012-03-14 10:34:48 +01001764 gfio_quit(data);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001765}
1766
Stephen M. Cameron25927252012-02-24 08:17:31 +01001767static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001768{
Jens Axboea9eccde2012-03-09 14:59:42 +01001769 struct gui *ui = arg;
1770
1771 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001772 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001773 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001774 return NULL;
1775}
1776
Jens Axboe2f99deb2012-03-09 14:37:29 +01001777static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001778{
Jens Axboe9988ca72012-03-09 15:14:06 +01001779 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001780 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001781
Jens Axboe2f99deb2012-03-09 14:37:29 +01001782 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001783 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001784 if (ret < 0) {
1785 GError *error;
1786
1787 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1788 report_error(error);
1789 g_error_free(error);
1790 break;
1791 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001792 break;
1793
Jens Axboe2f99deb2012-03-09 14:37:29 +01001794 free(ge->job_files[i]);
1795 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001796 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001797 while (i < ge->nr_job_files) {
1798 free(ge->job_files[i]);
1799 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001800 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001801 }
1802
Jens Axboe2c77d832012-03-13 19:02:04 +01001803 free(ge->job_files);
1804 ge->job_files = NULL;
Jens Axboe3af45202012-03-13 09:59:53 +01001805 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01001806 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001807}
1808
Jens Axboe63a130b2012-03-06 20:08:59 +01001809static void *server_thread(void *arg)
1810{
1811 is_backend = 1;
1812 gfio_server_running = 1;
1813 fio_start_server(NULL);
1814 gfio_server_running = 0;
1815 return NULL;
1816}
1817
Jens Axboe2f99deb2012-03-09 14:37:29 +01001818static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001819{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001820 struct gui *ui = &main_ui;
1821
Jens Axboe63a130b2012-03-06 20:08:59 +01001822 if (!gfio_server_running) {
1823 gfio_server_running = 1;
1824 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001825 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001826 }
1827}
1828
Stephen M. Cameron25927252012-02-24 08:17:31 +01001829static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1830 gpointer data)
1831{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001832 struct gui_entry *ge = data;
1833 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001834
Jens Axboe78cb2fe2012-03-12 23:05:29 +01001835 if (gc)
1836 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001837}
1838
Jens Axboedf06f222012-03-02 13:32:04 +01001839static void file_open(GtkWidget *w, gpointer data);
1840
1841static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001842{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001843 struct gui_entry *ge = data;
1844 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001845
Jens Axboe85dd01e2012-03-12 14:33:16 +01001846 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01001847 int ret;
1848
Jens Axboe2f99deb2012-03-09 14:37:29 +01001849 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01001850 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001851 if (!ge->nr_job_files)
1852 return;
1853
Jens Axboed3b70f32012-03-15 14:05:01 +01001854 gc = ge->client;
1855
Jens Axboe2f99deb2012-03-09 14:37:29 +01001856 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1857 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001858 ret = fio_client_connect(gc->client);
1859 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001860 if (!ge->ui->handler_running)
1861 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001862 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01001863 } else {
1864 GError *error;
1865
1866 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1867 report_error(error);
1868 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001869 }
Jens Axboedf06f222012-03-02 13:32:04 +01001870 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001871 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001872 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001873 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001874 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001875}
1876
Jens Axboeb9d2f302012-03-08 20:36:28 +01001877static void send_clicked(GtkWidget *widget, gpointer data)
1878{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001879 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001880
Jens Axboe2f99deb2012-03-09 14:37:29 +01001881 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001882 GError *error;
1883
1884 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);
1885 report_error(error);
1886 g_error_free(error);
1887
Jens Axboe53e0e852012-03-15 19:38:01 +01001888 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001889 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01001890}
1891
Jens Axboe0420ba62012-02-29 11:16:52 +01001892static void on_info_bar_response(GtkWidget *widget, gint response,
1893 gpointer data)
1894{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001895 struct gui *ui = &main_ui;
1896
Jens Axboe0420ba62012-02-29 11:16:52 +01001897 if (response == GTK_RESPONSE_OK) {
1898 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001899 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001900 }
1901}
1902
Jens Axboe49c34172012-03-16 12:05:17 +01001903static void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001904{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001905 struct gui *ui = &main_ui;
1906
1907 if (ui->error_info_bar == NULL) {
1908 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01001909 GTK_RESPONSE_OK,
1910 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001911 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
1912 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01001913 GTK_MESSAGE_ERROR);
1914
Jens Axboe2f99deb2012-03-09 14:37:29 +01001915 ui->error_label = gtk_label_new(error->message);
1916 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
1917 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01001918
Jens Axboe2f99deb2012-03-09 14:37:29 +01001919 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
1920 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01001921 } else {
1922 char buffer[256];
1923 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01001924 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01001925 }
1926}
1927
Jens Axboe62bc9372012-03-07 11:45:07 +01001928struct connection_widgets
1929{
1930 GtkWidget *hentry;
1931 GtkWidget *combo;
1932 GtkWidget *button;
1933};
1934
1935static void hostname_cb(GtkEntry *entry, gpointer data)
1936{
1937 struct connection_widgets *cw = data;
1938 int uses_net = 0, is_localhost = 0;
1939 const gchar *text;
1940 gchar *ctext;
1941
1942 /*
1943 * Check whether to display the 'auto start backend' box
1944 * or not. Show it if we are a localhost and using network,
1945 * or using a socket.
1946 */
1947 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
1948 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
1949 uses_net = 1;
1950 g_free(ctext);
1951
1952 if (uses_net) {
1953 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
1954 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
1955 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
1956 !strcmp(text, "ip6-loopback"))
1957 is_localhost = 1;
1958 }
1959
1960 if (!uses_net || is_localhost) {
1961 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
1962 gtk_widget_set_sensitive(cw->button, 1);
1963 } else {
1964 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
1965 gtk_widget_set_sensitive(cw->button, 0);
1966 }
1967}
1968
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01001969static int get_connection_details(char **host, int *port, int *type,
1970 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01001971{
Jens Axboe62bc9372012-03-07 11:45:07 +01001972 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
1973 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001974 char *typeentry;
1975
1976 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01001977 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01001978 GTK_DIALOG_DESTROY_WITH_PARENT,
1979 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1980 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1981
1982 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01001983 /* gtk_dialog_get_content_area() is 2.14 and newer */
1984 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01001985 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1986
1987 box = gtk_vbox_new(FALSE, 6);
1988 gtk_container_add(GTK_CONTAINER(frame), box);
1989
1990 hbox = gtk_hbox_new(TRUE, 10);
1991 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01001992 cw.hentry = gtk_entry_new();
1993 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
1994 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01001995
1996 frame = gtk_frame_new("Port");
1997 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
1998 box = gtk_vbox_new(FALSE, 10);
1999 gtk_container_add(GTK_CONTAINER(frame), box);
2000
2001 hbox = gtk_hbox_new(TRUE, 4);
2002 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2003 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2004
2005 frame = gtk_frame_new("Type");
2006 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2007 box = gtk_vbox_new(FALSE, 10);
2008 gtk_container_add(GTK_CONTAINER(frame), box);
2009
2010 hbox = gtk_hbox_new(TRUE, 4);
2011 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2012
Jens Axboe62bc9372012-03-07 11:45:07 +01002013 cw.combo = gtk_combo_box_new_text();
2014 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2015 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2016 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2017 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002018
Jens Axboe62bc9372012-03-07 11:45:07 +01002019 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002020
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002021 frame = gtk_frame_new("Options");
2022 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2023 box = gtk_vbox_new(FALSE, 10);
2024 gtk_container_add(GTK_CONTAINER(frame), box);
2025
2026 hbox = gtk_hbox_new(TRUE, 4);
2027 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2028
Jens Axboe62bc9372012-03-07 11:45:07 +01002029 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2030 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2031 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.");
2032 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2033
2034 /*
2035 * Connect edit signal, so we can show/not-show the auto start button
2036 */
2037 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2038 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002039
Jens Axboea7a42ce2012-03-02 13:12:04 +01002040 gtk_widget_show_all(dialog);
2041
2042 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2043 gtk_widget_destroy(dialog);
2044 return 1;
2045 }
2046
Jens Axboe62bc9372012-03-07 11:45:07 +01002047 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002048 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2049
Jens Axboe62bc9372012-03-07 11:45:07 +01002050 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002051 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2052 *type = Fio_client_ipv4;
2053 else if (!strncmp(typeentry, "IPv6", 4))
2054 *type = Fio_client_ipv6;
2055 else
2056 *type = Fio_client_socket;
2057 g_free(typeentry);
2058
Jens Axboe62bc9372012-03-07 11:45:07 +01002059 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002060
Jens Axboea7a42ce2012-03-02 13:12:04 +01002061 gtk_widget_destroy(dialog);
2062 return 0;
2063}
2064
Jens Axboe2f99deb2012-03-09 14:37:29 +01002065static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002066{
2067 struct gfio_client *gc;
2068
2069 gc = malloc(sizeof(*gc));
2070 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002071 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002072 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002073
Jens Axboe2f99deb2012-03-09 14:37:29 +01002074 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002075
2076 client->client_data = gc;
2077}
2078
Jens Axboe2f99deb2012-03-09 14:37:29 +01002079static GtkWidget *new_client_page(struct gui_entry *ge);
2080
2081static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2082{
2083 struct gui_entry *ge;
2084
2085 ge = malloc(sizeof(*ge));
2086 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002087 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002088 INIT_FLIST_HEAD(&ge->list);
2089 flist_add_tail(&ge->list, &ui->list);
2090 ge->ui = ui;
2091 return ge;
2092}
2093
Jens Axboe2f99deb2012-03-09 14:37:29 +01002094static struct gui_entry *get_new_ge_with_tab(const char *name)
2095{
2096 struct gui_entry *ge;
2097
2098 ge = alloc_new_gui_entry(&main_ui);
2099
2100 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002101 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002102
2103 ge->page_label = gtk_label_new(name);
2104 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2105
2106 gtk_widget_show_all(main_ui.window);
2107 return ge;
2108}
2109
2110static void file_new(GtkWidget *w, gpointer data)
2111{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002112 struct gui *ui = (struct gui *) data;
2113 struct gui_entry *ge;
2114
2115 ge = get_new_ge_with_tab("Untitled");
2116 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002117}
2118
2119/*
2120 * Return the 'ge' corresponding to the tab. If the active tab is the
2121 * main tab, open a new tab.
2122 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002123static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002124{
2125 struct flist_head *entry;
2126 struct gui_entry *ge;
2127
Jens Axboe38634cb2012-03-13 12:26:41 +01002128 if (!cur_page) {
2129 if (created)
2130 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002131 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002132 }
2133
2134 if (created)
2135 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002136
2137 flist_for_each(entry, &main_ui.list) {
2138 ge = flist_entry(entry, struct gui_entry, list);
2139 if (ge->page_num == cur_page)
2140 return ge;
2141 }
2142
2143 return NULL;
2144}
2145
Jens Axboe85dd01e2012-03-12 14:33:16 +01002146static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2147{
2148 gint cur_page;
2149
2150 /*
2151 * Main tab is tab 0, so any current page other than 0 holds
2152 * a ge entry.
2153 */
2154 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2155 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002156 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002157
2158 return NULL;
2159}
2160
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002161static void file_close(GtkWidget *w, gpointer data)
2162{
2163 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002164 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002165
2166 /*
2167 * Can't close the main tab
2168 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002169 ge = get_ge_from_cur_tab(ui);
2170 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002171 gtk_widget_destroy(ge->vbox);
2172 return;
2173 }
2174
Jens Axboef5c67262012-03-13 08:20:41 +01002175 if (!flist_empty(&ui->list)) {
2176 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2177 return;
2178 }
2179
Jens Axboe0fd18982012-03-14 10:34:48 +01002180 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002181}
2182
Jens Axboe38634cb2012-03-13 12:26:41 +01002183static void file_add_recent(struct gui *ui, const gchar *uri)
2184{
Jens Axboea217ba72012-03-13 20:29:39 +01002185 GtkRecentData grd;
2186
2187 memset(&grd, 0, sizeof(grd));
2188 grd.display_name = strdup("gfio");
2189 grd.description = strdup("Fio job file");
2190 grd.mime_type = strdup(GFIO_MIME);
2191 grd.app_name = strdup(g_get_application_name());
2192 grd.app_exec = strdup("gfio %f/%u");
2193
2194 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002195}
2196
2197static gchar *get_filename_from_uri(const gchar *uri)
2198{
2199 if (strncmp(uri, "file://", 7))
2200 return strdup(uri);
2201
2202 return strdup(uri + 7);
2203}
2204
2205static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2206 int type, int port)
2207{
2208 struct fio_client *client;
2209 gchar *filename;
2210
2211 filename = get_filename_from_uri(uri);
2212
2213 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2214 ge->job_files[ge->nr_job_files] = strdup(filename);
2215 ge->nr_job_files++;
2216
2217 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2218 if (!client) {
2219 GError *error;
2220
2221 error = g_error_new(g_quark_from_string("fio"), 1,
2222 "Failed to add client %s", host);
2223 report_error(error);
2224 g_error_free(error);
2225 return 1;
2226 }
2227
2228 gfio_client_added(ge, client);
2229 file_add_recent(ge->ui, uri);
2230 return 0;
2231}
2232
Jens Axboea6790902012-03-13 15:16:11 +01002233static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002234{
Jens Axboea6790902012-03-13 15:16:11 +01002235 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002236 struct gui_entry *ge;
2237 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002238 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002239 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002240
2241 /*
2242 * Creates new tab if current tab is the main window, or the
2243 * current tab already has a client.
2244 */
2245 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002246 ge = get_ge_from_page(cur_page, &ge_is_new);
2247 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002248 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002249 ge_is_new = 1;
2250 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002251
2252 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002253
Jens Axboea6790902012-03-13 15:16:11 +01002254 if (get_connection_details(&host, &port, &type, &server_start)) {
2255 if (ge_is_new)
2256 gtk_widget_destroy(ge->vbox);
2257
2258 return 1;
2259 }
2260
2261 ret = do_file_open(ge, uri, host, type, port);
2262
2263 free(host);
2264
2265 if (!ret) {
2266 if (server_start)
2267 gfio_start_server();
2268 } else {
2269 if (ge_is_new)
2270 gtk_widget_destroy(ge->vbox);
2271 }
2272
2273 return ret;
2274}
2275
2276static void recent_open(GtkAction *action, gpointer data)
2277{
2278 struct gui *ui = (struct gui *) data;
2279 GtkRecentInfo *info;
2280 const gchar *uri;
2281
2282 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2283 uri = gtk_recent_info_get_uri(info);
2284
2285 do_file_open_with_tab(ui, uri);
2286}
2287
2288static void file_open(GtkWidget *w, gpointer data)
2289{
2290 struct gui *ui = data;
2291 GtkWidget *dialog;
2292 GSList *filenames, *fn_glist;
2293 GtkFileFilter *filter;
2294
Jens Axboe0420ba62012-02-29 11:16:52 +01002295 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002296 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002297 GTK_FILE_CHOOSER_ACTION_OPEN,
2298 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2299 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2300 NULL);
2301 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2302
2303 filter = gtk_file_filter_new();
2304 gtk_file_filter_add_pattern(filter, "*.fio");
2305 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002306 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002307 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002308 gtk_file_filter_set_name(filter, "Fio job file");
2309 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2310
2311 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2312 gtk_widget_destroy(dialog);
2313 return;
2314 }
2315
2316 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002317
2318 gtk_widget_destroy(dialog);
2319
Jens Axboe0420ba62012-02-29 11:16:52 +01002320 filenames = fn_glist;
2321 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002322 if (do_file_open_with_tab(ui, filenames->data))
2323 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002324 filenames = g_slist_next(filenames);
2325 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002326
Jens Axboe0420ba62012-02-29 11:16:52 +01002327 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002328}
2329
2330static void file_save(GtkWidget *w, gpointer data)
2331{
Jens Axboe63a130b2012-03-06 20:08:59 +01002332 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002333 GtkWidget *dialog;
2334
2335 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002336 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002337 GTK_FILE_CHOOSER_ACTION_SAVE,
2338 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2339 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2340 NULL);
2341
2342 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2343 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2344
2345 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2346 char *filename;
2347
2348 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2349 // save_job_file(filename);
2350 g_free(filename);
2351 }
2352 gtk_widget_destroy(dialog);
2353}
2354
Jens Axboe9b260bd2012-03-06 11:02:52 +01002355static void view_log_destroy(GtkWidget *w, gpointer data)
2356{
2357 struct gui *ui = (struct gui *) data;
2358
2359 gtk_widget_ref(ui->log_tree);
2360 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2361 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002362 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002363}
2364
2365static void view_log(GtkWidget *w, gpointer data)
2366{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002367 GtkWidget *win, *scroll, *vbox, *box;
2368 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002369
Jens Axboe4cbe7212012-03-06 13:36:17 +01002370 if (ui->log_view)
2371 return;
2372
2373 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002374 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002375 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002376
Jens Axboe4cbe7212012-03-06 13:36:17 +01002377 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002378
Jens Axboe4cbe7212012-03-06 13:36:17 +01002379 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2380
2381 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2382
2383 box = gtk_hbox_new(TRUE, 0);
2384 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2385 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2386 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2387
2388 vbox = gtk_vbox_new(TRUE, 5);
2389 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2390
2391 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002392 gtk_widget_show_all(win);
2393}
2394
Jens Axboe85dd01e2012-03-12 14:33:16 +01002395static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002396{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002397 struct gui *ui = (struct gui *) data;
2398 struct gui_entry *ge;
2399
2400 ge = get_ge_from_cur_tab(ui);
2401 if (ge)
2402 connect_clicked(w, ge);
2403}
2404
2405static void send_job_entry(GtkWidget *w, gpointer data)
2406{
2407 struct gui *ui = (struct gui *) data;
2408 struct gui_entry *ge;
2409
2410 ge = get_ge_from_cur_tab(ui);
2411 if (ge)
2412 send_clicked(w, ge);
2413
2414}
2415
2416static void edit_job_entry(GtkWidget *w, gpointer data)
2417{
Jens Axboe9af4a242012-03-16 10:13:49 +01002418 struct gui *ui = (struct gui *) data;
Jens Axboe789f4cc2012-03-16 14:56:44 +01002419 struct gui_entry *ge;
Jens Axboe9af4a242012-03-16 10:13:49 +01002420
Jens Axboe789f4cc2012-03-16 14:56:44 +01002421 ge = get_ge_from_cur_tab(ui);
2422 if (ge && ge->client)
2423 gopt_get_options_window(ui->window, &ge->client->o);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002424}
2425
2426static void start_job_entry(GtkWidget *w, gpointer data)
2427{
2428 struct gui *ui = (struct gui *) data;
2429 struct gui_entry *ge;
2430
2431 ge = get_ge_from_cur_tab(ui);
2432 if (ge)
2433 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002434}
2435
Jens Axboe781ccba2012-03-15 09:44:42 +01002436static void view_results(GtkWidget *w, gpointer data)
2437{
2438 struct gui *ui = (struct gui *) data;
2439 struct gfio_client *gc;
2440 struct gui_entry *ge;
2441
2442 ge = get_ge_from_cur_tab(ui);
2443 if (!ge)
2444 return;
2445
2446 if (ge->results_window)
2447 return;
2448
2449 gc = ge->client;
2450 if (gc && gc->nr_results)
2451 gfio_display_end_results(gc);
2452}
2453
Jens Axboe8577f4f2012-03-09 19:28:27 +01002454static void __update_graph_limits(struct gfio_graphs *g)
2455{
2456 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2457 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2458}
2459
2460static void update_graph_limits(void)
2461{
2462 struct flist_head *entry;
2463 struct gui_entry *ge;
2464
2465 __update_graph_limits(&main_ui.graphs);
2466
2467 flist_for_each(entry, &main_ui.list) {
2468 ge = flist_entry(entry, struct gui_entry, list);
2469 __update_graph_limits(&ge->graphs);
2470 }
2471}
2472
Jens Axboe46974a72012-03-02 19:34:13 +01002473static void preferences(GtkWidget *w, gpointer data)
2474{
Jens Axboef3e84402012-03-07 13:14:32 +01002475 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002476 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002477 int i;
2478
2479 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002480 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002481 GTK_DIALOG_DESTROY_WITH_PARENT,
2482 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2483 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2484 NULL);
2485
Jens Axboe8577f4f2012-03-09 19:28:27 +01002486 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002487 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2488 vbox = gtk_vbox_new(FALSE, 6);
2489 gtk_container_add(GTK_CONTAINER(frame), vbox);
2490
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002491 hbox = gtk_hbox_new(FALSE, 5);
2492 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2493 entry = gtk_label_new("Font face to use for graph labels");
2494 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2495
Jens Axboef3e84402012-03-07 13:14:32 +01002496 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002497 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002498
Jens Axboe8577f4f2012-03-09 19:28:27 +01002499 box = gtk_vbox_new(FALSE, 6);
2500 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2501
2502 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002503 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002504 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2505 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2506
Jens Axboec05d9052012-03-11 13:05:35 +01002507 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002508
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002509 box = gtk_vbox_new(FALSE, 6);
2510 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2511
2512 hbox = gtk_hbox_new(FALSE, 5);
2513 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2514 entry = gtk_label_new("Client ETA request interval (msec)");
2515 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2516
2517 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002518 frame = gtk_frame_new("Debug logging");
2519 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2520 vbox = gtk_vbox_new(FALSE, 6);
2521 gtk_container_add(GTK_CONTAINER(frame), vbox);
2522
2523 box = gtk_hbox_new(FALSE, 6);
2524 gtk_container_add(GTK_CONTAINER(vbox), box);
2525
2526 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2527
2528 for (i = 0; i < FD_DEBUG_MAX; i++) {
2529 if (i == 7) {
2530 box = gtk_hbox_new(FALSE, 6);
2531 gtk_container_add(GTK_CONTAINER(vbox), box);
2532 }
2533
2534
2535 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2536 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2537 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2538 }
2539
Jens Axboe46974a72012-03-02 19:34:13 +01002540 gtk_widget_show_all(dialog);
2541
2542 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2543 gtk_widget_destroy(dialog);
2544 return;
2545 }
2546
2547 for (i = 0; i < FD_DEBUG_MAX; i++) {
2548 int set;
2549
2550 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2551 if (set)
2552 fio_debug |= (1UL << i);
2553 }
2554
Jens Axboef3e84402012-03-07 13:14:32 +01002555 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002556 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2557 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002558 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002559
Jens Axboe46974a72012-03-02 19:34:13 +01002560 gtk_widget_destroy(dialog);
2561}
2562
Jens Axboe0420ba62012-02-29 11:16:52 +01002563static void about_dialog(GtkWidget *w, gpointer data)
2564{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002565 const char *authors[] = {
2566 "Jens Axboe <axboe@kernel.dk>",
2567 "Stephen Carmeron <stephenmcameron@gmail.com>",
2568 NULL
2569 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002570 const char *license[] = {
2571 "Fio is free software; you can redistribute it and/or modify "
2572 "it under the terms of the GNU General Public License as published by "
2573 "the Free Software Foundation; either version 2 of the License, or "
2574 "(at your option) any later version.\n",
2575 "Fio is distributed in the hope that it will be useful, "
2576 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2577 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2578 "GNU General Public License for more details.\n",
2579 "You should have received a copy of the GNU General Public License "
2580 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2581 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2582 };
2583 char *license_trans;
2584
2585 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2586 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002587
Jens Axboe0420ba62012-02-29 11:16:52 +01002588 gtk_show_about_dialog(NULL,
2589 "program-name", "gfio",
2590 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002591 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002592 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2593 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002594 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002595 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002596 "logo-icon-name", "fio",
2597 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002598 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002599 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002600
Jens Axboe2f99deb2012-03-09 14:37:29 +01002601 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002602}
2603
2604static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002605 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002606 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002607 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002608 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002609 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002610 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002611 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2612 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2613 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002614 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01002615 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboebc271d82012-03-15 21:57:40 +01002616 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002617 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2618 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2619 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002620 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2621 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002622};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002623static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002624
2625static const gchar *ui_string = " \
2626 <ui> \
2627 <menubar name=\"MainMenu\"> \
2628 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002629 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01002630 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002631 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002632 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002633 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002634 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002635 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2636 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002637 <placeholder name=\"FileRecentFiles\"/> \
2638 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002639 <menuitem name=\"Quit\" action=\"Quit\" /> \
2640 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002641 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002642 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002643 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002644 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2645 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002646 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002647 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002648 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002649 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01002650 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2651 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002652 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2653 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002654 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2655 <menuitem name=\"About\" action=\"About\" /> \
2656 </menu> \
2657 </menubar> \
2658 </ui> \
2659";
2660
Jens Axboe4cbe7212012-03-06 13:36:17 +01002661static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2662 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002663{
Jens Axboeca664f42012-03-14 19:49:40 +01002664 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002665 GError *error = 0;
2666
2667 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002668 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002669
2670 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2671 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2672
2673 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002674
Jens Axboe0420ba62012-02-29 11:16:52 +01002675 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2676}
2677
2678void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2679 GtkWidget *vbox, GtkUIManager *ui_manager)
2680{
2681 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2682}
2683
Jens Axboec80b74b2012-03-12 10:23:28 +01002684static void combo_entry_changed(GtkComboBox *box, gpointer data)
2685{
2686 struct gui_entry *ge = (struct gui_entry *) data;
2687 gint index;
2688
2689 index = gtk_combo_box_get_active(box);
2690
2691 multitext_set_entry(&ge->eta.iotype, index);
Jens Axboe99d633a2012-03-15 15:55:04 +01002692 multitext_set_entry(&ge->eta.bs, index);
Jens Axboec80b74b2012-03-12 10:23:28 +01002693 multitext_set_entry(&ge->eta.ioengine, index);
2694 multitext_set_entry(&ge->eta.iodepth, index);
2695}
2696
2697static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2698{
2699 struct gui_entry *ge = (struct gui_entry *) data;
2700
2701 multitext_free(&ge->eta.iotype);
Jens Axboe99d633a2012-03-15 15:55:04 +01002702 multitext_free(&ge->eta.bs);
Jens Axboec80b74b2012-03-12 10:23:28 +01002703 multitext_free(&ge->eta.ioengine);
2704 multitext_free(&ge->eta.iodepth);
2705}
2706
Jens Axboe2f99deb2012-03-09 14:37:29 +01002707static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002708{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002709 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002710 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01002711
Jens Axboe2f99deb2012-03-09 14:37:29 +01002712 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002713
Jens Axboe65476332012-03-13 10:37:04 +01002714 top_align = gtk_alignment_new(0, 0, 1, 0);
2715 top_vbox = gtk_vbox_new(FALSE, 3);
2716 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2717 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002718
Jens Axboe3e47bd22012-02-29 13:45:02 +01002719 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002720 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002721 probe_frame = gtk_vbox_new(FALSE, 3);
2722 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2723
2724 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002725 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2726 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2727 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2728 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2729 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002730
Jens Axboe3e47bd22012-02-29 13:45:02 +01002731 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002732 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2733
Jens Axboe3863d1a2012-03-09 17:39:05 +01002734 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002735 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2736 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2737 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
Jens Axboe99d633a2012-03-15 15:55:04 +01002738 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
Jens Axboec80b74b2012-03-12 10:23:28 +01002739 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2740 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002741 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2742 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2743
2744 probe_box = gtk_hbox_new(FALSE, 3);
2745 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2746 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2747 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2748 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2749 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2750
2751 /*
2752 * Only add this if we have a commit rate
2753 */
2754#if 0
2755 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002756 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002757
Jens Axboe2f99deb2012-03-09 14:37:29 +01002758 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2759 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2760
2761 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2762 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2763#endif
2764
2765 /*
2766 * Set up a drawing area and IOPS and bandwidth graphs
2767 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002768 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002769 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002770 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002771 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2772 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2773 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2774 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2775 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002776 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2777 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002778 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002779 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002780 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002781 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002782
2783 setup_graphs(&ge->graphs);
2784
2785 /*
2786 * Set up alignments for widgets at the bottom of ui,
2787 * align bottom left, expand horizontally but not vertically
2788 */
Jens Axboe65476332012-03-13 10:37:04 +01002789 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002790 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002791 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2792 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002793
2794 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2795
2796 /*
2797 * Set up thread status progress bar
2798 */
2799 ge->thread_status_pb = gtk_progress_bar_new();
2800 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2801 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2802 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2803
2804
2805 return main_vbox;
2806}
2807
2808static GtkWidget *new_main_page(struct gui *ui)
2809{
2810 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002811 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002812
2813 main_vbox = gtk_vbox_new(FALSE, 3);
2814
2815 /*
2816 * Set up alignments for widgets at the top of ui,
2817 * align top left, expand horizontally but not vertically
2818 */
Jens Axboe65476332012-03-13 10:37:04 +01002819 top_align = gtk_alignment_new(0, 0, 1, 0);
2820 top_vbox = gtk_vbox_new(FALSE, 0);
2821 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2822 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002823
2824 probe = gtk_frame_new("Run statistics");
2825 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2826 probe_frame = gtk_vbox_new(FALSE, 3);
2827 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002828
2829 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002830 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01002831 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01002832 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2833 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2834 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2835 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002836
2837 /*
2838 * Only add this if we have a commit rate
2839 */
2840#if 0
2841 probe_box = gtk_hbox_new(FALSE, 3);
2842 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2843
Jens Axboe3e47bd22012-02-29 13:45:02 +01002844 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2845 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2846
Jens Axboe3e47bd22012-02-29 13:45:02 +01002847 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2848 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002849#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002850
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002851 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002852 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002853 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002854 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002855 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002856 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002857 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2858 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2859 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2860 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2861 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002862 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2863 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002864 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002865 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002866 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002867 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002868 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002869
Jens Axboe2f99deb2012-03-09 14:37:29 +01002870 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002871
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002872 /*
2873 * Set up alignments for widgets at the bottom of ui,
2874 * align bottom left, expand horizontally but not vertically
2875 */
Jens Axboe65476332012-03-13 10:37:04 +01002876 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002877 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002878 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2879 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002880
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002881 /*
2882 * Set up thread status progress bar
2883 */
2884 ui->thread_status_pb = gtk_progress_bar_new();
2885 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002886 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002887 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2888
Jens Axboe2f99deb2012-03-09 14:37:29 +01002889 return main_vbox;
2890}
2891
2892static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2893 guint page, gpointer data)
2894
2895{
Jens Axboe02421e62012-03-12 12:05:50 +01002896 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002897 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01002898
Jens Axboe85dd01e2012-03-12 14:33:16 +01002899 if (!page) {
2900 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01002901 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002902 return TRUE;
2903 }
2904
2905 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01002906 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002907 if (ge)
2908 update_button_states(ui, ge);
2909
Jens Axboe2f99deb2012-03-09 14:37:29 +01002910 return TRUE;
2911}
2912
Jens Axboe38634cb2012-03-13 12:26:41 +01002913static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
2914{
2915 time_t time_a = gtk_recent_info_get_visited(a);
2916 time_t time_b = gtk_recent_info_get_visited(b);
2917
2918 return time_b - time_a;
2919}
2920
2921static void add_recent_file_items(struct gui *ui)
2922{
2923 const gchar *gfio = g_get_application_name();
2924 GList *items, *item;
2925 int i = 0;
2926
2927 if (ui->recent_ui_id) {
2928 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
2929 gtk_ui_manager_ensure_update(ui->uimanager);
2930 }
2931 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
2932
2933 if (ui->actiongroup) {
2934 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
2935 g_object_unref(ui->actiongroup);
2936 }
2937 ui->actiongroup = gtk_action_group_new("RecentFileActions");
2938
2939 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
2940
2941 items = gtk_recent_manager_get_items(ui->recentmanager);
2942 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
2943
2944 for (item = items; item && item->data; item = g_list_next(item)) {
2945 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
2946 gchar *action_name;
2947 const gchar *label;
2948 GtkAction *action;
2949
2950 if (!gtk_recent_info_has_application(info, gfio))
2951 continue;
2952
2953 /*
2954 * We only support local files for now
2955 */
2956 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
2957 continue;
2958
2959 action_name = g_strdup_printf("RecentFile%u", i++);
2960 label = gtk_recent_info_get_display_name(info);
2961
2962 action = g_object_new(GTK_TYPE_ACTION,
2963 "name", action_name,
2964 "label", label, NULL);
2965
2966 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
2967 gtk_recent_info_ref(info),
2968 (GDestroyNotify) gtk_recent_info_unref);
2969
2970
2971 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
2972
2973 gtk_action_group_add_action(ui->actiongroup, action);
2974 g_object_unref(action);
2975
2976 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
2977 "/MainMenu/FileMenu/FileRecentFiles",
2978 label, action_name,
2979 GTK_UI_MANAGER_MENUITEM, FALSE);
2980
2981 g_free(action_name);
2982
2983 if (i == 8)
2984 break;
2985 }
2986
2987 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
2988 g_list_free(items);
2989}
2990
Jens Axboea6790902012-03-13 15:16:11 +01002991static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
2992 gint x, gint y, GtkSelectionData *data,
2993 guint info, guint time)
2994{
2995 struct gui *ui = &main_ui;
2996 gchar **uris;
2997 GtkWidget *source;
2998 int i;
2999
3000 source = gtk_drag_get_source_widget(ctx);
3001 if (source && widget == gtk_widget_get_toplevel(source)) {
3002 gtk_drag_finish(ctx, FALSE, FALSE, time);
3003 return;
3004 }
3005
3006 uris = gtk_selection_data_get_uris(data);
3007 if (!uris) {
3008 gtk_drag_finish(ctx, FALSE, FALSE, time);
3009 return;
3010 }
3011
3012 i = 0;
3013 while (uris[i]) {
3014 if (do_file_open_with_tab(ui, uris[i]))
3015 break;
3016 i++;
3017 }
3018
3019 gtk_drag_finish(ctx, TRUE, FALSE, time);
3020 g_strfreev(uris);
3021}
3022
Jens Axboe2f99deb2012-03-09 14:37:29 +01003023static void init_ui(int *argc, char **argv[], struct gui *ui)
3024{
3025 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003026 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003027
3028 /* Magical g*thread incantation, you just need this thread stuff.
3029 * Without it, the update that happens in gfio_update_thread_status
3030 * doesn't really happen in a timely fashion, you need expose events
3031 */
3032 if (!g_thread_supported())
3033 g_thread_init(NULL);
3034 gdk_threads_init();
3035
3036 gtk_init(argc, argv);
3037 settings = gtk_settings_get_default();
3038 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3039 g_type_init();
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003040 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003041
3042 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003043 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003044 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3045
3046 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3047 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3048
3049 ui->vbox = gtk_vbox_new(FALSE, 0);
3050 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3051
Jens Axboe02421e62012-03-12 12:05:50 +01003052 ui->uimanager = gtk_ui_manager_new();
3053 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3054 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003055
Jens Axboe38634cb2012-03-13 12:26:41 +01003056 ui->recentmanager = gtk_recent_manager_get_default();
3057 add_recent_file_items(ui);
3058
Jens Axboe2f99deb2012-03-09 14:37:29 +01003059 ui->notebook = gtk_notebook_new();
3060 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003061 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003062 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003063 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3064
3065 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003066 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3067 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3068 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003069
3070 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3071
Jens Axboe9b260bd2012-03-06 11:02:52 +01003072 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003073
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003074 gtk_widget_show_all(ui->window);
3075}
3076
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003077int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003078{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003079 if (initialize_fio(envp))
3080 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003081 if (fio_init_options())
3082 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003083
Jens Axboe2f99deb2012-03-09 14:37:29 +01003084 memset(&main_ui, 0, sizeof(main_ui));
3085 INIT_FLIST_HEAD(&main_ui.list);
3086
3087 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003088
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003089 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003090 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003091 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003092 return 0;
3093}