blob: 9998077ec619c4319dd804cb6a1f1db4b00b2fd9 [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 Axboe2fd3bb02012-03-07 08:07:39 +010035#include "graph.h"
Stephen M. Cameron8232e282012-02-24 08:17:31 +010036
Jens Axboe63a130b2012-03-06 20:08:59 +010037static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010038static const char *gfio_graph_font;
Jens Axboe8577f4f2012-03-09 19:28:27 +010039static unsigned int gfio_graph_limit = 100;
Stephen M. Cameron814479d2012-03-15 07:58:14 +010040static GdkColor white;
Jens Axboe63a130b2012-03-06 20:08:59 +010041
Jens Axboe6b79c802012-03-08 10:51:36 +010042static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010043
Stephen M. Cameronf3074002012-02-24 08:17:30 +010044typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
45
Jens Axboe3e47bd22012-02-29 13:45:02 +010046static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010047static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010048static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010049
50static struct button_spec {
51 const char *buttontext;
52 clickfunction f;
Jens Axboe014f4022012-03-15 14:03:01 +010053 const char *tooltiptext[2];
54 const int start_sensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010055} buttonspeclist[] = {
Jens Axboe53e0e852012-03-15 19:38:01 +010056 {
57 .buttontext = "Connect",
58 .f = connect_clicked,
59 .tooltiptext = { "Disconnect from host", "Connect to host" },
60 .start_sensitive = 1,
61 },
62 {
63 .buttontext = "Send",
64 .f = send_clicked,
65 .tooltiptext = { "Send job description to host", NULL },
66 .start_sensitive = 0,
67 },
68 {
69 .buttontext = "Start Job",
70 .f = start_job_clicked,
71 .tooltiptext = { "Start the current job on the server", NULL },
72 .start_sensitive = 0,
73 },
Jens Axboee0681f32012-03-06 12:14:42 +010074};
75
Jens Axboe9988ca72012-03-09 15:14:06 +010076static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
77static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +010078void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +010079
Jens Axboe2f99deb2012-03-09 14:37:29 +010080static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010081{
Jens Axboe2f99deb2012-03-09 14:37:29 +010082 struct graph *g;
83
84 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +010085 graph_title(g, "IOPS (IOs/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +010086 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +010087 graph_add_label(g, "Read IOPS");
88 graph_add_label(g, "Write IOPS");
89 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
90 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +010091 line_graph_set_data_count_limit(g, gfio_graph_limit);
Jens Axboed8fbeef2012-03-14 10:25:44 +010092 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +010093 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +010094}
95
Jens Axboe2f99deb2012-03-09 14:37:29 +010096static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010097{
Jens Axboe2f99deb2012-03-09 14:37:29 +010098 struct graph *g;
99
100 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100101 graph_title(g, "Bandwidth (bytes/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100102 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100103 graph_add_label(g, "Read Bandwidth");
104 graph_add_label(g, "Write Bandwidth");
105 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
106 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100107 graph_set_base_offset(g, 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100108 line_graph_set_data_count_limit(g, 100);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100109 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100110 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100111}
112
Jens Axboe2f99deb2012-03-09 14:37:29 +0100113static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100114{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100115 g->iops_graph = setup_iops_graph();
116 g->bandwidth_graph = setup_bandwidth_graph();
117}
118
Jens Axboec80b74b2012-03-12 10:23:28 +0100119static void multitext_add_entry(struct multitext_widget *mt, const char *text)
120{
121 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
122 mt->text[mt->max_text] = strdup(text);
123 mt->max_text++;
124}
125
126static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
127{
128 if (index >= mt->max_text)
129 return;
Jens Axboeda185432012-03-12 11:05:46 +0100130 if (!mt->text || !mt->text[index])
Jens Axboec80b74b2012-03-12 10:23:28 +0100131 return;
132
133 mt->cur_text = index;
134 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
135}
136
137static void multitext_update_entry(struct multitext_widget *mt,
138 unsigned int index, const char *text)
139{
Jens Axboeda185432012-03-12 11:05:46 +0100140 if (!mt->text)
141 return;
142
Jens Axboec80b74b2012-03-12 10:23:28 +0100143 if (mt->text[index])
144 free(mt->text[index]);
145
146 mt->text[index] = strdup(text);
147 if (mt->cur_text == index)
148 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
149}
150
151static void multitext_free(struct multitext_widget *mt)
152{
153 int i;
154
155 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
156
157 for (i = 0; i < mt->max_text; i++) {
158 if (mt->text[i])
159 free(mt->text[i]);
160 }
161
162 free(mt->text);
163 mt->cur_text = -1;
164 mt->max_text = 0;
165}
166
Jens Axboe2f99deb2012-03-09 14:37:29 +0100167static void clear_ge_ui_info(struct gui_entry *ge)
168{
169 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
170 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
171 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
172 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100173#if 0
174 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100175 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100176#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100177 multitext_update_entry(&ge->eta.iotype, 0, "");
Jens Axboe99d633a2012-03-15 15:55:04 +0100178 multitext_update_entry(&ge->eta.bs, 0, "");
Jens Axboec80b74b2012-03-12 10:23:28 +0100179 multitext_update_entry(&ge->eta.ioengine, 0, "");
180 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100181 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
182 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
183 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
185 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100187}
188
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100189static void show_info_dialog(struct gui *ui, const char *title,
190 const char *message)
191{
192 GtkWidget *dialog, *content, *label;
193
194 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
195 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
196 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
197
198 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
199 label = gtk_label_new(message);
200 gtk_container_add(GTK_CONTAINER(content), label);
201 gtk_widget_show_all(dialog);
202 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
203 gtk_dialog_run(GTK_DIALOG(dialog));
204 gtk_widget_destroy(dialog);
205}
206
Jens Axboe781ccba2012-03-15 09:44:42 +0100207static void set_menu_entry_text(struct gui *ui, const char *path,
208 const char *text)
209{
210 GtkWidget *w;
211
212 w = gtk_ui_manager_get_widget(ui->uimanager, path);
213 if (w)
214 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
215 else
216 fprintf(stderr, "gfio: can't find path %s\n", path);
217}
218
219
220static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
221{
222 GtkWidget *w;
223
224 w = gtk_ui_manager_get_widget(ui->uimanager, path);
225 if (w)
226 gtk_widget_set_sensitive(w, show);
227 else
228 fprintf(stderr, "gfio: can't find path %s\n", path);
229}
230
231static void set_job_menu_visible(struct gui *ui, int visible)
232{
233 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
234}
235
236static void set_view_results_visible(struct gui *ui, int visible)
237{
238 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
239}
240
Jens Axboe014f4022012-03-15 14:03:01 +0100241static const char *get_button_tooltip(struct button_spec *s, int sensitive)
242{
243 if (s->tooltiptext[sensitive])
244 return s->tooltiptext[sensitive];
245
246 return s->tooltiptext[0];
247}
248
249static GtkWidget *add_button(GtkWidget *buttonbox,
250 struct button_spec *buttonspec, gpointer data)
251{
252 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
253 gboolean sens = buttonspec->start_sensitive;
254
255 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
256 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
257
258 sens = buttonspec->start_sensitive;
259 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
260 gtk_widget_set_sensitive(button, sens);
261
262 return button;
263}
264
265static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
266 int nbuttons)
267{
268 int i;
269
270 for (i = 0; i < nbuttons; i++)
271 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
272}
273
Jens Axboe85dd01e2012-03-12 14:33:16 +0100274/*
275 * Update sensitivity of job buttons and job menu items, based on the
276 * state of the client.
277 */
278static void update_button_states(struct gui *ui, struct gui_entry *ge)
279{
280 unsigned int connect_state, send_state, start_state, edit_state;
281 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100282
283 switch (ge->state) {
284 default: {
285 char tmp[80];
286
287 sprintf(tmp, "Bad client state: %u\n", ge->state);
288 show_info_dialog(ui, "Error", tmp);
289 /* fall through to new state */
290 }
291
292 case GE_STATE_NEW:
293 connect_state = 1;
294 edit_state = 0;
295 connect_str = "Connect";
296 send_state = 0;
297 start_state = 0;
298 break;
299 case GE_STATE_CONNECTED:
300 connect_state = 1;
301 edit_state = 0;
302 connect_str = "Disconnect";
303 send_state = 1;
304 start_state = 0;
305 break;
306 case GE_STATE_JOB_SENT:
307 connect_state = 1;
308 edit_state = 0;
309 connect_str = "Disconnect";
310 send_state = 0;
311 start_state = 1;
312 break;
313 case GE_STATE_JOB_STARTED:
314 connect_state = 1;
315 edit_state = 1;
316 connect_str = "Disconnect";
317 send_state = 0;
318 start_state = 1;
319 break;
320 case GE_STATE_JOB_RUNNING:
321 connect_state = 1;
322 edit_state = 0;
323 connect_str = "Disconnect";
324 send_state = 0;
325 start_state = 0;
326 break;
327 case GE_STATE_JOB_DONE:
328 connect_state = 1;
329 edit_state = 0;
330 connect_str = "Connect";
331 send_state = 0;
332 start_state = 0;
333 break;
334 }
335
Jens Axboe53e0e852012-03-15 19:38:01 +0100336 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
337 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
338 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
339 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
340 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 +0100341
Jens Axboe781ccba2012-03-15 09:44:42 +0100342 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
343 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100344
Jens Axboe781ccba2012-03-15 09:44:42 +0100345 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
346 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
347 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100348
Jens Axboe781ccba2012-03-15 09:44:42 +0100349 if (ge->client && ge->client->nr_results)
350 set_view_results_visible(ui, 1);
351 else
352 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100353}
354
355static void gfio_set_state(struct gui_entry *ge, unsigned int state)
356{
357 ge->state = state;
358 update_button_states(ge->ui, ge);
359}
360
Jens Axboea2697902012-03-05 16:43:49 +0100361#define ALIGN_LEFT 1
362#define ALIGN_RIGHT 2
363#define INVISIBLE 4
364#define UNSORTABLE 8
365
366GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
367{
368 GtkCellRenderer *renderer;
369 GtkTreeViewColumn *col;
370 double xalign = 0.0; /* left as default */
371 PangoAlignment align;
372 gboolean visible;
373
374 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
375 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
376 PANGO_ALIGN_CENTER;
377 visible = !(flags & INVISIBLE);
378
379 renderer = gtk_cell_renderer_text_new();
380 col = gtk_tree_view_column_new();
381
382 gtk_tree_view_column_set_title(col, title);
383 if (!(flags & UNSORTABLE))
384 gtk_tree_view_column_set_sort_column_id(col, index);
385 gtk_tree_view_column_set_resizable(col, TRUE);
386 gtk_tree_view_column_pack_start(col, renderer, TRUE);
387 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
388 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
389 switch (align) {
390 case PANGO_ALIGN_LEFT:
391 xalign = 0.0;
392 break;
393 case PANGO_ALIGN_CENTER:
394 xalign = 0.5;
395 break;
396 case PANGO_ALIGN_RIGHT:
397 xalign = 1.0;
398 break;
399 }
400 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
401 gtk_tree_view_column_set_visible(col, visible);
402 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
403 return col;
404}
405
Jens Axboe9b260bd2012-03-06 11:02:52 +0100406static void gfio_ui_setup_log(struct gui *ui)
407{
408 GtkTreeSelection *selection;
409 GtkListStore *model;
410 GtkWidget *tree_view;
411
412 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
413
414 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
415 gtk_widget_set_can_focus(tree_view, FALSE);
416
417 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
418 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100419 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
420 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100421
422 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
423 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
424 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100425 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100426
427 ui->log_model = model;
428 ui->log_tree = tree_view;
429}
430
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100431static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
432 fio_fp64_t *plist,
433 unsigned int len,
434 double xdim, double ydim)
435{
436 struct graph *g;
437 int i;
438
439 g = graph_new(xdim, ydim, gfio_graph_font);
440 graph_title(g, title);
441 graph_x_title(g, "Percentile");
442
443 for (i = 0; i < len; i++) {
444 char fbuf[8];
445
446 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
447 graph_add_label(g, fbuf);
448 graph_add_data(g, fbuf, (double) ovals[i]);
449 }
450
451 return g;
452}
453
Jens Axboea2697902012-03-05 16:43:49 +0100454static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
455 fio_fp64_t *plist,
456 unsigned int len,
457 const char *base,
458 unsigned int scale)
459{
460 GType types[FIO_IO_U_LIST_MAX_LEN];
461 GtkWidget *tree_view;
462 GtkTreeSelection *selection;
463 GtkListStore *model;
464 GtkTreeIter iter;
465 int i;
466
467 for (i = 0; i < len; i++)
468 types[i] = G_TYPE_INT;
469
470 model = gtk_list_store_newv(len, types);
471
472 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
473 gtk_widget_set_can_focus(tree_view, FALSE);
474
Jens Axboe661f7412012-03-06 13:55:45 +0100475 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
476 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
477
Jens Axboea2697902012-03-05 16:43:49 +0100478 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
479 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
480
481 for (i = 0; i < len; i++) {
482 char fbuf[8];
483
484 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
485 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
486 }
487
488 gtk_list_store_append(model, &iter);
489
Jens Axboee0681f32012-03-06 12:14:42 +0100490 for (i = 0; i < len; i++) {
491 if (scale)
492 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100493 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100494 }
Jens Axboea2697902012-03-05 16:43:49 +0100495
496 return tree_view;
497}
498
Jens Axboe09d574e2012-03-15 10:45:48 +0100499static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100500{
501 struct graph *g = p;
502 cairo_t *cr;
503
504 cr = gdk_cairo_create(w->window);
505#if 0
506 if (graph_has_tooltips(g)) {
507 g_object_set(w, "has-tooltip", TRUE, NULL);
508 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
509 }
510#endif
511 cairo_set_source_rgb(cr, 0, 0, 0);
512 bar_graph_draw(g, cr);
513 cairo_destroy(cr);
514
515 return FALSE;
516}
517
Jens Axboe09d574e2012-03-15 10:45:48 +0100518static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
519 gpointer data)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100520{
521 struct graph *g = data;
522
523 graph_set_size(g, w->allocation.width, w->allocation.height);
524 graph_set_size(g, w->allocation.width, w->allocation.height);
525 graph_set_position(g, 0, 0);
526 return TRUE;
527}
528
529static void gfio_show_clat_percentiles(struct gfio_client *gc,
530 GtkWidget *vbox, struct thread_stat *ts,
Jens Axboea2697902012-03-05 16:43:49 +0100531 int ddir)
532{
533 unsigned int *io_u_plat = ts->io_u_plat[ddir];
534 unsigned long nr = ts->clat_stat[ddir].samples;
535 fio_fp64_t *plist = ts->percentile_list;
536 unsigned int *ovals, len, minv, maxv, scale_down;
537 const char *base;
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100538 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
Jens Axboe09d574e2012-03-15 10:45:48 +0100539 struct gui_entry *ge = gc->ge;
Jens Axboea2697902012-03-05 16:43:49 +0100540 char tmp[64];
541
542 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
543 if (!len)
544 goto out;
545
546 /*
547 * We default to usecs, but if the value range is such that we
548 * should scale down to msecs, do that.
549 */
550 if (minv > 2000 && maxv > 99999) {
551 scale_down = 1;
552 base = "msec";
553 } else {
554 scale_down = 0;
555 base = "usec";
556 }
557
Jens Axboea2697902012-03-05 16:43:49 +0100558 sprintf(tmp, "Completion percentiles (%s)", base);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100559 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
Jens Axboe09d574e2012-03-15 10:45:48 +0100560 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100561
Jens Axboea2697902012-03-05 16:43:49 +0100562 frame = gtk_frame_new(tmp);
563 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
564
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100565 completion_vbox = gtk_vbox_new(FALSE, 3);
566 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
Jens Axboea2697902012-03-05 16:43:49 +0100567 hbox = gtk_hbox_new(FALSE, 3);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100568 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
569 drawing_area = gtk_drawing_area_new();
570 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
571 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
572 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
Jens Axboe09d574e2012-03-15 10:45:48 +0100573 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
574 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 +0100575
576 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
577out:
578 if (ovals)
579 free(ovals);
580}
581
Jens Axboe3650a3c2012-03-05 14:09:03 +0100582static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
583 unsigned long max, double mean, double dev)
584{
585 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100586 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100587 char *minp, *maxp;
588 char tmp[64];
589
590 if (!usec_to_msec(&min, &max, &mean, &dev))
591 base = "(msec)";
592
593 minp = num2str(min, 6, 1, 0);
594 maxp = num2str(max, 6, 1, 0);
595
Jens Axboe3650a3c2012-03-05 14:09:03 +0100596 sprintf(tmp, "%s %s", name, base);
597 frame = gtk_frame_new(tmp);
598 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
599
Jens Axboe3650a3c2012-03-05 14:09:03 +0100600 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100601 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100602
603 label = new_info_label_in_frame(hbox, "Minimum");
604 gtk_label_set_text(GTK_LABEL(label), minp);
605 label = new_info_label_in_frame(hbox, "Maximum");
606 gtk_label_set_text(GTK_LABEL(label), maxp);
607 label = new_info_label_in_frame(hbox, "Average");
608 sprintf(tmp, "%5.02f", mean);
609 gtk_label_set_text(GTK_LABEL(label), tmp);
610 label = new_info_label_in_frame(hbox, "Standard deviation");
611 sprintf(tmp, "%5.02f", dev);
612 gtk_label_set_text(GTK_LABEL(label), tmp);
613
614 free(minp);
615 free(maxp);
616
617}
618
Jens Axboeca850992012-03-05 20:04:43 +0100619#define GFIO_CLAT 1
620#define GFIO_SLAT 2
621#define GFIO_LAT 4
622
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100623static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
624 struct group_run_stats *rs,
Jens Axboe3650a3c2012-03-05 14:09:03 +0100625 struct thread_stat *ts, int ddir)
626{
627 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100628 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100629 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100630 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100631 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100632 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100633 char *io_p, *bw_p, *iops_p;
634 int i2p;
635
636 if (!ts->runtime[ddir])
637 return;
638
639 i2p = is_power_of_2(rs->kb_base);
640 runt = ts->runtime[ddir];
641
642 bw = (1000 * ts->io_bytes[ddir]) / runt;
643 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
644 bw_p = num2str(bw, 6, 1, i2p);
645
646 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
647 iops_p = num2str(iops, 6, 1, 0);
648
649 box = gtk_hbox_new(FALSE, 3);
650 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
651
652 frame = gtk_frame_new(ddir_label[ddir]);
653 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
654
Jens Axboe0b761302012-03-05 20:44:11 +0100655 main_vbox = gtk_vbox_new(FALSE, 3);
656 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100657
658 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100659 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100660
661 label = new_info_label_in_frame(box, "IO");
662 gtk_label_set_text(GTK_LABEL(label), io_p);
663 label = new_info_label_in_frame(box, "Bandwidth");
664 gtk_label_set_text(GTK_LABEL(label), bw_p);
665 label = new_info_label_in_frame(box, "IOPS");
666 gtk_label_set_text(GTK_LABEL(label), iops_p);
667 label = new_info_label_in_frame(box, "Runtime (msec)");
668 label_set_int_value(label, ts->runtime[ddir]);
669
Jens Axboee0681f32012-03-06 12:14:42 +0100670 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100671 double p_of_agg = 100.0;
672 const char *bw_str = "KB";
673 char tmp[32];
674
675 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100676 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100677 if (p_of_agg > 100.0)
678 p_of_agg = 100.0;
679 }
680
Jens Axboee0681f32012-03-06 12:14:42 +0100681 if (mean[0] > 999999.9) {
682 min[0] /= 1000.0;
683 max[0] /= 1000.0;
684 mean[0] /= 1000.0;
685 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100686 bw_str = "MB";
687 }
688
Jens Axboe0b761302012-03-05 20:44:11 +0100689 sprintf(tmp, "Bandwidth (%s)", bw_str);
690 frame = gtk_frame_new(tmp);
691 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100692
Jens Axboe0b761302012-03-05 20:44:11 +0100693 box = gtk_hbox_new(FALSE, 3);
694 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100695
Jens Axboe0b761302012-03-05 20:44:11 +0100696 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100697 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100698 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100699 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100700 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100701 sprintf(tmp, "%3.2f%%", p_of_agg);
702 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100703 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100704 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100705 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100706 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100707 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100708 gtk_label_set_text(GTK_LABEL(label), tmp);
709 }
710
Jens Axboee0681f32012-03-06 12:14:42 +0100711 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100712 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100713 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100714 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100715 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100716 flags |= GFIO_LAT;
717
718 if (flags) {
719 frame = gtk_frame_new("Latency");
720 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
721
722 vbox = gtk_vbox_new(FALSE, 3);
723 gtk_container_add(GTK_CONTAINER(frame), vbox);
724
725 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100726 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100727 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100728 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100729 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100730 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100731 }
732
733 if (ts->clat_percentiles)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100734 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
Jens Axboe2b089892012-03-06 08:09:17 +0100735
Jens Axboe3650a3c2012-03-05 14:09:03 +0100736 free(io_p);
737 free(bw_p);
738 free(iops_p);
739}
740
Jens Axboe09d574e2012-03-15 10:45:48 +0100741static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
742 const char **labels,
743 unsigned int len,
744 double xdim, double ydim)
745{
746 struct graph *g;
747 int i;
748
749 g = graph_new(xdim, ydim, gfio_graph_font);
750 graph_title(g, title);
751 graph_x_title(g, "Buckets");
752
753 for (i = 0; i < len; i++) {
754 graph_add_label(g, labels[i]);
755 graph_add_data(g, labels[i], lat[i]);
756 }
757
758 return g;
759}
760
761static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
762 int num)
Jens Axboee5bd1342012-03-05 21:38:12 +0100763{
764 GtkWidget *tree_view;
765 GtkTreeSelection *selection;
766 GtkListStore *model;
767 GtkTreeIter iter;
768 GType *types;
Jens Axboe09d574e2012-03-15 10:45:48 +0100769 int i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100770
771 types = malloc(num * sizeof(GType));
772
773 for (i = 0; i < num; i++)
774 types[i] = G_TYPE_STRING;
775
776 model = gtk_list_store_newv(num, types);
777 free(types);
778 types = NULL;
779
780 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
781 gtk_widget_set_can_focus(tree_view, FALSE);
782
Jens Axboe661f7412012-03-06 13:55:45 +0100783 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
784 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
785
Jens Axboee5bd1342012-03-05 21:38:12 +0100786 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
787 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
788
789 for (i = 0; i < num; i++)
790 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
791
792 gtk_list_store_append(model, &iter);
793
794 for (i = 0; i < num; i++) {
795 char fbuf[32];
796
797 if (lat[i] <= 0.0)
798 sprintf(fbuf, "0.00");
799 else
800 sprintf(fbuf, "%3.2f%%", lat[i]);
801
802 gtk_list_store_set(model, &iter, i, fbuf, -1);
803 }
804
805 return tree_view;
806}
807
Jens Axboe09d574e2012-03-15 10:45:48 +0100808static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
809 struct thread_stat *ts)
Jens Axboee5bd1342012-03-05 21:38:12 +0100810{
Jens Axboe09d574e2012-03-15 10:45:48 +0100811 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
812 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
813 "250u", "500u", "750u", "1m", "2m",
814 "4m", "10m", "20m", "50m", "100m",
815 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
816 int start, end, i;
817 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
818 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
819 struct gui_entry *ge = gc->ge;
Jens Axboee5bd1342012-03-05 21:38:12 +0100820
Jens Axboe09d574e2012-03-15 10:45:48 +0100821 stat_calc_lat_u(ts, io_u_lat);
822 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
Jens Axboee5bd1342012-03-05 21:38:12 +0100823
Jens Axboe09d574e2012-03-15 10:45:48 +0100824 /*
825 * Found out which first bucket has entries, and which last bucket
826 */
827 start = end = -1U;
828 for (i = 0; i < total; i++) {
829 if (io_u_lat[i] == 0.00)
830 continue;
Jens Axboee5bd1342012-03-05 21:38:12 +0100831
Jens Axboe09d574e2012-03-15 10:45:48 +0100832 if (start == -1U)
833 start = i;
834 end = i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100835 }
836
Jens Axboe09d574e2012-03-15 10:45:48 +0100837 /*
838 * No entries...
839 */
840 if (start == -1U)
841 return;
842
843 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
844 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 +0100845
Jens Axboe09d574e2012-03-15 10:45:48 +0100846 frame = gtk_frame_new("Latency buckets");
847 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
848
849 completion_vbox = gtk_vbox_new(FALSE, 3);
850 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
851 hbox = gtk_hbox_new(FALSE, 3);
852 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
853
854 drawing_area = gtk_drawing_area_new();
855 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
856 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
857 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
858 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
859 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
860
861 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
Jens Axboee5bd1342012-03-05 21:38:12 +0100862}
863
Jens Axboe2e331012012-03-05 22:07:54 +0100864static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
865{
866 GtkWidget *box, *frame, *entry;
867 double usr_cpu, sys_cpu;
868 unsigned long runtime;
869 char tmp[32];
870
871 runtime = ts->total_run_time;
872 if (runtime) {
873 double runt = (double) runtime;
874
875 usr_cpu = (double) ts->usr_time * 100 / runt;
876 sys_cpu = (double) ts->sys_time * 100 / runt;
877 } else {
878 usr_cpu = 0;
879 sys_cpu = 0;
880 }
881
882 frame = gtk_frame_new("OS resources");
883 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
884
885 box = gtk_hbox_new(FALSE, 3);
886 gtk_container_add(GTK_CONTAINER(frame), box);
887
888 entry = new_info_entry_in_frame(box, "User CPU");
889 sprintf(tmp, "%3.2f%%", usr_cpu);
890 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
891 entry = new_info_entry_in_frame(box, "System CPU");
892 sprintf(tmp, "%3.2f%%", sys_cpu);
893 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
894 entry = new_info_entry_in_frame(box, "Context switches");
895 entry_set_int_value(entry, ts->ctx);
896 entry = new_info_entry_in_frame(box, "Major faults");
897 entry_set_int_value(entry, ts->majf);
898 entry = new_info_entry_in_frame(box, "Minor faults");
899 entry_set_int_value(entry, ts->minf);
900}
Jens Axboe19998db2012-03-06 09:17:59 +0100901static void gfio_add_sc_depths_tree(GtkListStore *model,
902 struct thread_stat *ts, unsigned int len,
903 int submit)
904{
905 double io_u_dist[FIO_IO_U_MAP_NR];
906 GtkTreeIter iter;
907 /* Bits 0, and 3-8 */
908 const int add_mask = 0x1f9;
909 int i, j;
910
911 if (submit)
912 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
913 else
914 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
915
916 gtk_list_store_append(model, &iter);
917
918 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
919
920 for (i = 1, j = 0; i < len; i++) {
921 char fbuf[32];
922
923 if (!(add_mask & (1UL << (i - 1))))
924 sprintf(fbuf, "0.0%%");
925 else {
926 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
927 j++;
928 }
929
930 gtk_list_store_set(model, &iter, i, fbuf, -1);
931 }
932
933}
934
935static void gfio_add_total_depths_tree(GtkListStore *model,
936 struct thread_stat *ts, unsigned int len)
937{
938 double io_u_dist[FIO_IO_U_MAP_NR];
939 GtkTreeIter iter;
940 /* Bits 1-6, and 8 */
941 const int add_mask = 0x17e;
942 int i, j;
943
944 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
945
946 gtk_list_store_append(model, &iter);
947
948 gtk_list_store_set(model, &iter, 0, "Total", -1);
949
950 for (i = 1, j = 0; i < len; i++) {
951 char fbuf[32];
952
953 if (!(add_mask & (1UL << (i - 1))))
954 sprintf(fbuf, "0.0%%");
955 else {
956 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
957 j++;
958 }
959
960 gtk_list_store_set(model, &iter, i, fbuf, -1);
961 }
962
963}
Jens Axboe2e331012012-03-05 22:07:54 +0100964
965static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
966{
Jens Axboe2e331012012-03-05 22:07:54 +0100967 GtkWidget *frame, *box, *tree_view;
968 GtkTreeSelection *selection;
969 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100970 GType types[FIO_IO_U_MAP_NR + 1];
971 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100972#define NR_LABELS 10
973 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100974
975 frame = gtk_frame_new("IO depths");
976 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
977
978 box = gtk_hbox_new(FALSE, 3);
979 gtk_container_add(GTK_CONTAINER(frame), box);
980
Jens Axboe19998db2012-03-06 09:17:59 +0100981 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100982 types[i] = G_TYPE_STRING;
983
Jens Axboe19998db2012-03-06 09:17:59 +0100984 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100985
986 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
987 gtk_widget_set_can_focus(tree_view, FALSE);
988
Jens Axboe661f7412012-03-06 13:55:45 +0100989 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
990 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
991
Jens Axboe2e331012012-03-05 22:07:54 +0100992 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
993 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
994
Jens Axboe19998db2012-03-06 09:17:59 +0100995 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100996 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
997
Jens Axboe19998db2012-03-06 09:17:59 +0100998 gfio_add_total_depths_tree(model, ts, NR_LABELS);
999 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1000 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001001
1002 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1003}
1004
Jens Axboef9d40b42012-03-06 09:52:49 +01001005static gboolean results_window_delete(GtkWidget *w, gpointer data)
1006{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001007 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001008
1009 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001010 ge->results_window = NULL;
1011 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001012 return TRUE;
1013}
1014
Jens Axboe17b97212012-03-14 20:17:57 +01001015static void results_close(GtkWidget *w, gpointer *data)
1016{
1017 struct gui_entry *ge = (struct gui_entry *) data;
1018
1019 gtk_widget_destroy(ge->results_window);
1020}
1021
1022static GtkActionEntry results_menu_items[] = {
1023 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1024 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1025 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1026};
1027static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1028
1029static const gchar *results_ui_string = " \
1030 <ui> \
1031 <menubar name=\"MainMenu\"> \
1032 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1033 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1034 </menu> \
1035 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1036 </menu>\
1037 </menubar> \
1038 </ui> \
1039";
1040
1041static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1042{
1043 GtkActionGroup *action_group;
1044 GtkWidget *widget;
1045 GError *error = 0;
1046
1047 ge->results_uimanager = gtk_ui_manager_new();
1048
1049 action_group = gtk_action_group_new("ResultsMenu");
1050 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1051
1052 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1053 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1054
1055 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1056
1057 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1058 return widget;
1059}
1060
Jens Axboe2f99deb2012-03-09 14:37:29 +01001061static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001062{
Jens Axboe17b97212012-03-14 20:17:57 +01001063 GtkWidget *win, *notebook, *vbox;
Jens Axboef9d40b42012-03-06 09:52:49 +01001064
Jens Axboe2f99deb2012-03-09 14:37:29 +01001065 if (ge->results_window)
1066 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001067
1068 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1069 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001070 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001071 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1072 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001073
Jens Axboe17b97212012-03-14 20:17:57 +01001074 vbox = gtk_vbox_new(FALSE, 0);
1075 gtk_container_add(GTK_CONTAINER(win), vbox);
1076
1077 ge->results_menu = get_results_menubar(win, ge);
1078 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1079
Jens Axboef9d40b42012-03-06 09:52:49 +01001080 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001081 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1082 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboe17b97212012-03-14 20:17:57 +01001083 gtk_container_add(GTK_CONTAINER(vbox), notebook);
Jens Axboef9d40b42012-03-06 09:52:49 +01001084
Jens Axboe2f99deb2012-03-09 14:37:29 +01001085 ge->results_window = win;
1086 ge->results_notebook = notebook;
1087 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001088}
1089
Jens Axboe7da23b42012-03-15 13:45:02 +01001090static void disk_util_destroy(GtkWidget *w, gpointer data)
Jens Axboe3650a3c2012-03-05 14:09:03 +01001091{
Jens Axboe7da23b42012-03-15 13:45:02 +01001092 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001093
Jens Axboe7da23b42012-03-15 13:45:02 +01001094 ge->disk_util_vbox = NULL;
1095 gtk_widget_destroy(w);
Jens Axboe781ccba2012-03-15 09:44:42 +01001096}
Jens Axboe3650a3c2012-03-05 14:09:03 +01001097
Jens Axboef0602d72012-03-15 15:38:02 +01001098static GtkWidget *get_scrolled_window(gint border_width)
1099{
1100 GtkWidget *scroll;
1101
1102 scroll = gtk_scrolled_window_new(NULL, NULL);
1103 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1104 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1105
1106 return scroll;
1107}
1108
1109static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1110{
1111 GtkWidget *vbox, *box, *scroll, *res_notebook;
1112
1113 if (ge->disk_util_vbox)
1114 return ge->disk_util_vbox;
1115
1116 scroll = get_scrolled_window(5);
1117 vbox = gtk_vbox_new(FALSE, 3);
1118 box = gtk_hbox_new(FALSE, 0);
1119 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1120
1121 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1122 res_notebook = get_results_window(ge);
1123
1124 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1125 ge->disk_util_vbox = box;
1126 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1127
1128 return ge->disk_util_vbox;
1129}
1130
Jens Axboe7da23b42012-03-15 13:45:02 +01001131static int __gfio_disk_util_show(GtkWidget *res_notebook,
1132 struct gfio_client *gc, struct cmd_du_pdu *p)
Jens Axboe781ccba2012-03-15 09:44:42 +01001133{
Jens Axboef0602d72012-03-15 15:38:02 +01001134 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
Jens Axboe7da23b42012-03-15 13:45:02 +01001135 struct gui_entry *ge = gc->ge;
Jens Axboe604cfe32012-03-07 19:51:36 +01001136 double util;
1137 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001138
Jens Axboef0602d72012-03-15 15:38:02 +01001139 util_vbox = gfio_disk_util_get_vbox(ge);
Jens Axboee0681f32012-03-06 12:14:42 +01001140
1141 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboef0602d72012-03-15 15:38:02 +01001142 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
Jens Axboee0681f32012-03-06 12:14:42 +01001143
1144 frame = gtk_frame_new((char *) p->dus.name);
1145 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1146
1147 box = gtk_vbox_new(FALSE, 3);
1148 gtk_container_add(GTK_CONTAINER(frame), box);
1149
1150 frame = gtk_frame_new("Read");
1151 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1152 vbox = gtk_hbox_new(TRUE, 3);
1153 gtk_container_add(GTK_CONTAINER(frame), vbox);
1154 entry = new_info_entry_in_frame(vbox, "IOs");
1155 entry_set_int_value(entry, p->dus.ios[0]);
1156 entry = new_info_entry_in_frame(vbox, "Merges");
1157 entry_set_int_value(entry, p->dus.merges[0]);
1158 entry = new_info_entry_in_frame(vbox, "Sectors");
1159 entry_set_int_value(entry, p->dus.sectors[0]);
1160 entry = new_info_entry_in_frame(vbox, "Ticks");
1161 entry_set_int_value(entry, p->dus.ticks[0]);
1162
1163 frame = gtk_frame_new("Write");
1164 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1165 vbox = gtk_hbox_new(TRUE, 3);
1166 gtk_container_add(GTK_CONTAINER(frame), vbox);
1167 entry = new_info_entry_in_frame(vbox, "IOs");
1168 entry_set_int_value(entry, p->dus.ios[1]);
1169 entry = new_info_entry_in_frame(vbox, "Merges");
1170 entry_set_int_value(entry, p->dus.merges[1]);
1171 entry = new_info_entry_in_frame(vbox, "Sectors");
1172 entry_set_int_value(entry, p->dus.sectors[1]);
1173 entry = new_info_entry_in_frame(vbox, "Ticks");
1174 entry_set_int_value(entry, p->dus.ticks[1]);
1175
1176 frame = gtk_frame_new("Shared");
1177 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1178 vbox = gtk_hbox_new(TRUE, 3);
1179 gtk_container_add(GTK_CONTAINER(frame), vbox);
1180 entry = new_info_entry_in_frame(vbox, "IO ticks");
1181 entry_set_int_value(entry, p->dus.io_ticks);
1182 entry = new_info_entry_in_frame(vbox, "Time in queue");
1183 entry_set_int_value(entry, p->dus.time_in_queue);
1184
Jens Axboe604cfe32012-03-07 19:51:36 +01001185 util = 0.0;
1186 if (p->dus.msec)
1187 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1188 if (util > 100.0)
1189 util = 100.0;
1190
1191 sprintf(tmp, "%3.2f%%", util);
1192 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1193 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1194
Jens Axboe7da23b42012-03-15 13:45:02 +01001195 gtk_widget_show_all(ge->results_window);
1196 return 0;
1197}
1198
1199static int gfio_disk_util_show(struct gfio_client *gc)
1200{
1201 struct gui_entry *ge = gc->ge;
1202 GtkWidget *res_notebook;
1203 int i;
1204
1205 if (!gc->nr_du)
1206 return 1;
1207
1208 res_notebook = get_results_window(ge);
1209
1210 for (i = 0; i < gc->nr_du; i++) {
1211 struct cmd_du_pdu *p = &gc->du[i];
1212
1213 __gfio_disk_util_show(res_notebook, gc, p);
1214 }
1215
1216 gtk_widget_show_all(ge->results_window);
1217 return 0;
1218}
1219
1220static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1221 struct group_run_stats *rs)
1222{
1223 unsigned int nr = gc->nr_results;
1224
1225 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1226 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1227 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1228 gc->nr_results++;
1229}
1230
1231static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1232 struct thread_stat *ts,
1233 struct group_run_stats *rs)
1234{
1235 GtkWidget *box, *vbox, *entry, *scroll;
1236
1237 scroll = gtk_scrolled_window_new(NULL, NULL);
1238 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1239 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1240
1241 vbox = gtk_vbox_new(FALSE, 3);
1242
1243 box = gtk_hbox_new(FALSE, 0);
1244 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1245
1246 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1247
1248 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1249
1250 entry = new_info_entry_in_frame(box, "Name");
1251 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1252 if (strlen(ts->description)) {
1253 entry = new_info_entry_in_frame(box, "Description");
1254 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1255 }
1256 entry = new_info_entry_in_frame(box, "Group ID");
1257 entry_set_int_value(entry, ts->groupid);
1258 entry = new_info_entry_in_frame(box, "Jobs");
1259 entry_set_int_value(entry, ts->members);
1260 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1261 entry_set_int_value(entry, ts->error);
1262 entry = new_info_entry_in_frame(box, "PID");
1263 entry_set_int_value(entry, ts->pid);
1264
1265 if (ts->io_bytes[DDIR_READ])
1266 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1267 if (ts->io_bytes[DDIR_WRITE])
1268 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1269
1270 gfio_show_latency_buckets(gc, vbox, ts);
1271 gfio_show_cpu_usage(vbox, ts);
1272 gfio_show_io_depths(vbox, ts);
1273}
1274
1275static void gfio_display_end_results(struct gfio_client *gc)
1276{
1277 struct gui_entry *ge = gc->ge;
1278 GtkWidget *res_notebook;
1279 int i;
1280
1281 res_notebook = get_results_window(ge);
1282
1283 for (i = 0; i < gc->nr_results; i++) {
1284 struct end_results *e = &gc->results[i];
1285
1286 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1287 }
1288
1289 if (gfio_disk_util_show(gc))
1290 gtk_widget_show_all(ge->results_window);
1291}
1292
1293static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1294 struct group_run_stats *rs)
1295{
1296 struct gfio_client *gc = client->client_data;
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001297 struct gui_entry *ge = gc->ge;
Jens Axboe7da23b42012-03-15 13:45:02 +01001298
1299 gfio_add_end_results(gc, ts, rs);
1300
1301 gdk_threads_enter();
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001302 if (ge->results_window)
1303 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1304 else
1305 gfio_display_end_results(gc);
Jens Axboe7da23b42012-03-15 13:45:02 +01001306 gdk_threads_leave();
1307}
1308
1309static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1310{
1311 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1312 struct gui *ui = &main_ui;
1313 GtkTreeIter iter;
1314 struct tm *tm;
1315 time_t sec;
1316 char tmp[64], timebuf[80];
1317
1318 sec = p->log_sec;
1319 tm = localtime(&sec);
1320 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1321 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1322
1323 gdk_threads_enter();
1324
1325 gtk_list_store_append(ui->log_model, &iter);
1326 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1327 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1328 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1329 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1330
1331 if (p->level == FIO_LOG_ERR)
1332 view_log(NULL, (gpointer) ui);
1333
1334 gdk_threads_leave();
1335}
1336
1337static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1338{
1339 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1340 struct gfio_client *gc = client->client_data;
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001341 struct gui_entry *ge = gc->ge;
Jens Axboe7da23b42012-03-15 13:45:02 +01001342 unsigned int nr = gc->nr_du;
1343
1344 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1345 memcpy(&gc->du[nr], p, sizeof(*p));
1346 gc->nr_du++;
1347
1348 gdk_threads_enter();
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001349 if (ge->results_window)
1350 __gfio_disk_util_show(ge->results_notebook, gc, p);
1351 else
1352 gfio_disk_util_show(gc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001353 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001354}
1355
Jens Axboe3650a3c2012-03-05 14:09:03 +01001356extern int sum_stat_clients;
1357extern struct thread_stat client_ts;
1358extern struct group_run_stats client_gs;
1359
1360static int sum_stat_nr;
1361
Jens Axboe89e5fad2012-03-05 09:21:12 +01001362static void gfio_thread_status_op(struct fio_client *client,
1363 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001364{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001365 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1366
1367 gfio_display_ts(client, &p->ts, &p->rs);
1368
1369 if (sum_stat_clients == 1)
1370 return;
1371
1372 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1373 sum_group_stats(&client_gs, &p->rs);
1374
1375 client_ts.members++;
Jens Axboe2f122b12012-03-15 13:10:19 +01001376 client_ts.thread_number = p->ts.thread_number;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001377 client_ts.groupid = p->ts.groupid;
1378
1379 if (++sum_stat_nr == sum_stat_clients) {
1380 strcpy(client_ts.name, "All clients");
1381 gfio_display_ts(client, &client_ts, &client_gs);
1382 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001383}
1384
Jens Axboe89e5fad2012-03-05 09:21:12 +01001385static void gfio_group_stats_op(struct fio_client *client,
1386 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001387{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001388 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001389}
1390
Jens Axboe2f99deb2012-03-09 14:37:29 +01001391static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1392 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001393{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001394 struct gfio_graphs *g = data;
1395
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001396 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1397 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1398 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1399 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001400 return TRUE;
1401}
1402
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001403static void draw_graph(struct graph *g, cairo_t *cr)
1404{
1405 line_graph_draw(g, cr);
1406 cairo_stroke(cr);
1407}
1408
Jens Axboe93e2db22012-03-13 09:45:22 +01001409static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1410 gboolean keyboard_mode, GtkTooltip *tooltip,
1411 gpointer data)
1412{
1413 struct gfio_graphs *g = data;
1414 const char *text = NULL;
1415
1416 if (graph_contains_xy(g->iops_graph, x, y))
1417 text = graph_find_tooltip(g->iops_graph, x, y);
1418 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1419 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1420
1421 if (text) {
1422 gtk_tooltip_set_text(tooltip, text);
1423 return TRUE;
1424 }
1425
1426 return FALSE;
1427}
1428
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001429static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1430{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001431 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001432 cairo_t *cr;
1433
1434 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001435
1436 if (graph_has_tooltips(g->iops_graph) ||
1437 graph_has_tooltips(g->bandwidth_graph)) {
1438 g_object_set(w, "has-tooltip", TRUE, NULL);
1439 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1440 }
1441
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001442 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001443 draw_graph(g->iops_graph, cr);
1444 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001445 cairo_destroy(cr);
1446
1447 return FALSE;
1448}
1449
Jens Axboe2f99deb2012-03-09 14:37:29 +01001450/*
1451 * Client specific ETA
1452 */
1453static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001454{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001455 struct gfio_client *gc = client->client_data;
1456 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001457 static int eta_good;
1458 char eta_str[128];
1459 char output[256];
1460 char tmp[32];
1461 double perc = 0.0;
1462 int i2p = 0;
1463
Jens Axboe0050e5f2012-03-06 09:23:27 +01001464 gdk_threads_enter();
1465
Jens Axboe3e47bd22012-02-29 13:45:02 +01001466 eta_str[0] = '\0';
1467 output[0] = '\0';
1468
1469 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1470 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1471 eta_to_str(eta_str, je->eta_sec);
1472 }
1473
1474 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001475 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001476 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001477 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001478
1479#if 0
1480 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1481 if (je->m_rate || je->t_rate) {
1482 char *tr, *mr;
1483
1484 mr = num2str(je->m_rate, 4, 0, i2p);
1485 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001486 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001487 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1488 free(tr);
1489 free(mr);
1490 } else if (je->m_iops || je->t_iops)
1491 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001492
Jens Axboe2f99deb2012-03-09 14:37:29 +01001493 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1494 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1495 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1496 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001497#endif
1498
1499 if (je->eta_sec != INT_MAX && je->nr_running) {
1500 char *iops_str[2];
1501 char *rate_str[2];
1502
1503 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1504 strcpy(output, "-.-% done");
1505 else {
1506 eta_good = 1;
1507 perc *= 100.0;
1508 sprintf(output, "%3.1f%% done", perc);
1509 }
1510
1511 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1512 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1513
1514 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1515 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1516
Jens Axboe2f99deb2012-03-09 14:37:29 +01001517 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1518 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1519 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1520 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001521
Jens Axboe93e2db22012-03-13 09:45:22 +01001522 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1523 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1524 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1525 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 +01001526
1527 free(rate_str[0]);
1528 free(rate_str[1]);
1529 free(iops_str[0]);
1530 free(iops_str[1]);
1531 }
1532
1533 if (eta_str[0]) {
1534 char *dst = output + strlen(output);
1535
1536 sprintf(dst, " - %s", eta_str);
1537 }
1538
Jens Axboe9988ca72012-03-09 15:14:06 +01001539 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001540 gdk_threads_leave();
1541}
1542
1543/*
1544 * Update ETA in main window for all clients
1545 */
1546static void gfio_update_all_eta(struct jobs_eta *je)
1547{
1548 struct gui *ui = &main_ui;
1549 static int eta_good;
1550 char eta_str[128];
1551 char output[256];
1552 double perc = 0.0;
1553 int i2p = 0;
1554
1555 gdk_threads_enter();
1556
1557 eta_str[0] = '\0';
1558 output[0] = '\0';
1559
1560 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1561 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1562 eta_to_str(eta_str, je->eta_sec);
1563 }
1564
1565#if 0
1566 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1567 if (je->m_rate || je->t_rate) {
1568 char *tr, *mr;
1569
1570 mr = num2str(je->m_rate, 4, 0, i2p);
1571 tr = num2str(je->t_rate, 4, 0, i2p);
1572 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1573 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1574 free(tr);
1575 free(mr);
1576 } else if (je->m_iops || je->t_iops)
1577 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1578
1579 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1580 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1581 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1582 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1583#endif
1584
Jens Axboe3863d1a2012-03-09 17:39:05 +01001585 entry_set_int_value(ui->eta.jobs, je->nr_running);
1586
Jens Axboe2f99deb2012-03-09 14:37:29 +01001587 if (je->eta_sec != INT_MAX && je->nr_running) {
1588 char *iops_str[2];
1589 char *rate_str[2];
1590
1591 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1592 strcpy(output, "-.-% done");
1593 else {
1594 eta_good = 1;
1595 perc *= 100.0;
1596 sprintf(output, "%3.1f%% done", perc);
1597 }
1598
1599 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1600 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1601
1602 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1603 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1604
1605 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1606 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1607 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1608 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1609
Jens Axboe93e2db22012-03-13 09:45:22 +01001610 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1611 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1612 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1613 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 +01001614
Jens Axboe3e47bd22012-02-29 13:45:02 +01001615 free(rate_str[0]);
1616 free(rate_str[1]);
1617 free(iops_str[0]);
1618 free(iops_str[1]);
1619 }
1620
1621 if (eta_str[0]) {
1622 char *dst = output + strlen(output);
1623
1624 sprintf(dst, " - %s", eta_str);
1625 }
1626
Jens Axboe9988ca72012-03-09 15:14:06 +01001627 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001628 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001629}
1630
Stephen M. Camerona1820202012-02-24 08:17:31 +01001631static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1632{
Jens Axboe843ad232012-02-29 11:44:53 +01001633 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001634 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001635 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001636 const char *os, *arch;
1637 char buf[64];
1638
1639 os = fio_get_os_string(probe->os);
1640 if (!os)
1641 os = "unknown";
1642
1643 arch = fio_get_arch_string(probe->arch);
1644 if (!arch)
1645 os = "unknown";
1646
1647 if (!client->name)
1648 client->name = strdup((char *) probe->hostname);
1649
Jens Axboe0050e5f2012-03-06 09:23:27 +01001650 gdk_threads_enter();
1651
Jens Axboe2f99deb2012-03-09 14:37:29 +01001652 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1653 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1654 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001655 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001656 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001657
Jens Axboe85dd01e2012-03-12 14:33:16 +01001658 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001659
1660 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001661}
1662
Jens Axboe9988ca72012-03-09 15:14:06 +01001663static void gfio_update_thread_status(struct gui_entry *ge,
1664 char *status_message, double perc)
1665{
1666 static char message[100];
1667 const char *m = message;
1668
1669 strncpy(message, status_message, sizeof(message) - 1);
1670 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1671 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1672 gtk_widget_queue_draw(main_ui.window);
1673}
1674
1675static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001676{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001677 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001678 static char message[100];
1679 const char *m = message;
1680
1681 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001682 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1683 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1684 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001685}
1686
Jens Axboe35c0ba72012-03-14 10:56:40 +01001687static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001688{
Jens Axboee0681f32012-03-06 12:14:42 +01001689 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001690
Jens Axboe0050e5f2012-03-06 09:23:27 +01001691 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001692 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001693 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001694}
1695
Jens Axboe807f9972012-03-02 10:25:24 +01001696static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1697{
1698 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001699 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001700 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001701 struct gui_entry *ge = gc->ge;
Jens Axboe99d633a2012-03-15 15:55:04 +01001702 char *c1, *c2, *c3, *c4;
1703 char tmp[80];
Jens Axboe807f9972012-03-02 10:25:24 +01001704
Jens Axboe2f122b12012-03-15 13:10:19 +01001705 p->thread_number = le32_to_cpu(p->thread_number);
1706 p->groupid = le32_to_cpu(p->groupid);
Jens Axboedcaeb602012-03-08 19:45:37 +01001707 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001708
Jens Axboe0050e5f2012-03-06 09:23:27 +01001709 gdk_threads_enter();
1710
Jens Axboe2f99deb2012-03-09 14:37:29 +01001711 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1712
Jens Axboe3863d1a2012-03-09 17:39:05 +01001713 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1714 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1715
Jens Axboeddbafc12012-03-15 18:57:03 +01001716 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1717 multitext_add_entry(&ge->eta.iotype, tmp);
Jens Axboe99d633a2012-03-15 15:55:04 +01001718
1719 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1720 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1721 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1722 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1723 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1724 free(c1);
1725 free(c2);
1726 free(c3);
1727 free(c4);
1728 multitext_add_entry(&ge->eta.bs, tmp);
1729
Jens Axboec80b74b2012-03-12 10:23:28 +01001730 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001731
Jens Axboedcaeb602012-03-08 19:45:37 +01001732 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001733 multitext_add_entry(&ge->eta.iodepth, tmp);
1734
1735 multitext_set_entry(&ge->eta.iotype, 0);
Jens Axboe99d633a2012-03-15 15:55:04 +01001736 multitext_set_entry(&ge->eta.bs, 0);
Jens Axboec80b74b2012-03-12 10:23:28 +01001737 multitext_set_entry(&ge->eta.ioengine, 0);
1738 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001739
Jens Axboe85dd01e2012-03-12 14:33:16 +01001740 gfio_set_state(ge, GE_STATE_JOB_SENT);
1741
Jens Axboe0050e5f2012-03-06 09:23:27 +01001742 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001743}
1744
Jens Axboeed727a42012-03-02 12:14:40 +01001745static void gfio_client_timed_out(struct fio_client *client)
1746{
Jens Axboee0681f32012-03-06 12:14:42 +01001747 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001748 char buf[256];
1749
1750 gdk_threads_enter();
1751
Jens Axboe85dd01e2012-03-12 14:33:16 +01001752 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001753 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001754
1755 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001756 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001757
1758 gdk_threads_leave();
1759}
1760
Jens Axboe6b79c802012-03-08 10:51:36 +01001761static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1762{
1763 struct gfio_client *gc = client->client_data;
1764
1765 gdk_threads_enter();
1766
Jens Axboe85dd01e2012-03-12 14:33:16 +01001767 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001768
1769 if (gc->err_entry)
1770 entry_set_int_value(gc->err_entry, client->error);
1771
1772 gdk_threads_leave();
1773}
1774
Jens Axboe85dd01e2012-03-12 14:33:16 +01001775static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1776{
1777 struct gfio_client *gc = client->client_data;
1778
1779 gdk_threads_enter();
1780 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1781 gdk_threads_leave();
1782}
1783
1784static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1785{
1786 struct gfio_client *gc = client->client_data;
1787
1788 gdk_threads_enter();
1789 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1790 gdk_threads_leave();
1791}
1792
Jens Axboe1b427252012-03-14 15:03:03 +01001793static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1794{
Jens Axboe284b1e62012-03-14 21:55:07 +01001795 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 +01001796 free(pdu);
1797}
1798
Stephen M. Camerona1820202012-02-24 08:17:31 +01001799struct client_ops gfio_client_ops = {
Jens Axboe35c0ba72012-03-14 10:56:40 +01001800 .text = gfio_text_op,
Jens Axboe0420ba62012-02-29 11:16:52 +01001801 .disk_util = gfio_disk_util_op,
1802 .thread_status = gfio_thread_status_op,
1803 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001804 .jobs_eta = gfio_update_client_eta,
1805 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001806 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001807 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001808 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001809 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001810 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001811 .start = gfio_client_start,
1812 .job_start = gfio_client_job_start,
Jens Axboe1b427252012-03-14 15:03:03 +01001813 .iolog = gfio_client_iolog,
Jens Axboe6433ee02012-03-09 20:10:51 +01001814 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001815 .stay_connected = 1,
Jens Axboe46bcd492012-03-14 11:31:21 +01001816 .client_type = FIO_CLIENT_TYPE_GUI,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001817};
1818
Jens Axboe0fd18982012-03-14 10:34:48 +01001819/*
1820 * FIXME: need more handling here
1821 */
1822static void ge_destroy(struct gui_entry *ge)
1823{
1824 struct gfio_client *gc = ge->client;
1825
1826 if (gc && gc->client) {
1827 if (ge->state >= GE_STATE_CONNECTED)
1828 fio_client_terminate(gc->client);
1829
1830 fio_put_client(gc->client);
1831 }
1832
1833 flist_del(&ge->list);
1834 free(ge);
1835}
1836
1837static void ge_widget_destroy(GtkWidget *w, gpointer data)
1838{
Jens Axboe0fd18982012-03-14 10:34:48 +01001839}
1840
1841static void gfio_quit(struct gui *ui)
1842{
1843 struct gui_entry *ge;
1844
1845 while (!flist_empty(&ui->list)) {
1846 ge = flist_entry(ui->list.next, struct gui_entry, list);
1847 ge_destroy(ge);
1848 }
1849
1850 gtk_main_quit();
1851}
1852
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001853static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1854 __attribute__((unused)) gpointer data)
1855{
Jens Axboe0fd18982012-03-14 10:34:48 +01001856 gfio_quit(data);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001857}
1858
Stephen M. Cameron25927252012-02-24 08:17:31 +01001859static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001860{
Jens Axboea9eccde2012-03-09 14:59:42 +01001861 struct gui *ui = arg;
1862
1863 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001864 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001865 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001866 return NULL;
1867}
1868
Jens Axboe2f99deb2012-03-09 14:37:29 +01001869static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001870{
Jens Axboe9988ca72012-03-09 15:14:06 +01001871 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001872 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001873
Jens Axboe2f99deb2012-03-09 14:37:29 +01001874 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001875 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001876 if (ret < 0) {
1877 GError *error;
1878
1879 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1880 report_error(error);
1881 g_error_free(error);
1882 break;
1883 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001884 break;
1885
Jens Axboe2f99deb2012-03-09 14:37:29 +01001886 free(ge->job_files[i]);
1887 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001888 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001889 while (i < ge->nr_job_files) {
1890 free(ge->job_files[i]);
1891 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001892 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001893 }
1894
Jens Axboe2c77d832012-03-13 19:02:04 +01001895 free(ge->job_files);
1896 ge->job_files = NULL;
Jens Axboe3af45202012-03-13 09:59:53 +01001897 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01001898 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001899}
1900
Jens Axboe63a130b2012-03-06 20:08:59 +01001901static void *server_thread(void *arg)
1902{
1903 is_backend = 1;
1904 gfio_server_running = 1;
1905 fio_start_server(NULL);
1906 gfio_server_running = 0;
1907 return NULL;
1908}
1909
Jens Axboe2f99deb2012-03-09 14:37:29 +01001910static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001911{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001912 struct gui *ui = &main_ui;
1913
Jens Axboe63a130b2012-03-06 20:08:59 +01001914 if (!gfio_server_running) {
1915 gfio_server_running = 1;
1916 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001917 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001918 }
1919}
1920
Stephen M. Cameron25927252012-02-24 08:17:31 +01001921static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1922 gpointer data)
1923{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001924 struct gui_entry *ge = data;
1925 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001926
Jens Axboe78cb2fe2012-03-12 23:05:29 +01001927 if (gc)
1928 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001929}
1930
Jens Axboedf06f222012-03-02 13:32:04 +01001931static void file_open(GtkWidget *w, gpointer data);
1932
1933static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001934{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001935 struct gui_entry *ge = data;
1936 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001937
Jens Axboe85dd01e2012-03-12 14:33:16 +01001938 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01001939 int ret;
1940
Jens Axboe2f99deb2012-03-09 14:37:29 +01001941 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01001942 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001943 if (!ge->nr_job_files)
1944 return;
1945
Jens Axboed3b70f32012-03-15 14:05:01 +01001946 gc = ge->client;
1947
Jens Axboe2f99deb2012-03-09 14:37:29 +01001948 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1949 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001950 ret = fio_client_connect(gc->client);
1951 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001952 if (!ge->ui->handler_running)
1953 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001954 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01001955 } else {
1956 GError *error;
1957
1958 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1959 report_error(error);
1960 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001961 }
Jens Axboedf06f222012-03-02 13:32:04 +01001962 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001963 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001964 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001965 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001966 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001967}
1968
Jens Axboeb9d2f302012-03-08 20:36:28 +01001969static void send_clicked(GtkWidget *widget, gpointer data)
1970{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001971 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001972
Jens Axboe2f99deb2012-03-09 14:37:29 +01001973 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001974 GError *error;
1975
1976 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);
1977 report_error(error);
1978 g_error_free(error);
1979
Jens Axboe53e0e852012-03-15 19:38:01 +01001980 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001981 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01001982}
1983
Jens Axboe0420ba62012-02-29 11:16:52 +01001984static void on_info_bar_response(GtkWidget *widget, gint response,
1985 gpointer data)
1986{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001987 struct gui *ui = &main_ui;
1988
Jens Axboe0420ba62012-02-29 11:16:52 +01001989 if (response == GTK_RESPONSE_OK) {
1990 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001991 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001992 }
1993}
1994
Jens Axboedf06f222012-03-02 13:32:04 +01001995void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001996{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001997 struct gui *ui = &main_ui;
1998
1999 if (ui->error_info_bar == NULL) {
2000 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01002001 GTK_RESPONSE_OK,
2002 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002003 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2004 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01002005 GTK_MESSAGE_ERROR);
2006
Jens Axboe2f99deb2012-03-09 14:37:29 +01002007 ui->error_label = gtk_label_new(error->message);
2008 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2009 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01002010
Jens Axboe2f99deb2012-03-09 14:37:29 +01002011 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2012 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01002013 } else {
2014 char buffer[256];
2015 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002016 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01002017 }
2018}
2019
Jens Axboe62bc9372012-03-07 11:45:07 +01002020struct connection_widgets
2021{
2022 GtkWidget *hentry;
2023 GtkWidget *combo;
2024 GtkWidget *button;
2025};
2026
2027static void hostname_cb(GtkEntry *entry, gpointer data)
2028{
2029 struct connection_widgets *cw = data;
2030 int uses_net = 0, is_localhost = 0;
2031 const gchar *text;
2032 gchar *ctext;
2033
2034 /*
2035 * Check whether to display the 'auto start backend' box
2036 * or not. Show it if we are a localhost and using network,
2037 * or using a socket.
2038 */
2039 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2040 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2041 uses_net = 1;
2042 g_free(ctext);
2043
2044 if (uses_net) {
2045 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2046 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2047 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2048 !strcmp(text, "ip6-loopback"))
2049 is_localhost = 1;
2050 }
2051
2052 if (!uses_net || is_localhost) {
2053 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2054 gtk_widget_set_sensitive(cw->button, 1);
2055 } else {
2056 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2057 gtk_widget_set_sensitive(cw->button, 0);
2058 }
2059}
2060
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002061static int get_connection_details(char **host, int *port, int *type,
2062 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01002063{
Jens Axboe62bc9372012-03-07 11:45:07 +01002064 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2065 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002066 char *typeentry;
2067
2068 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002069 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01002070 GTK_DIALOG_DESTROY_WITH_PARENT,
2071 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2072 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2073
2074 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01002075 /* gtk_dialog_get_content_area() is 2.14 and newer */
2076 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002077 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2078
2079 box = gtk_vbox_new(FALSE, 6);
2080 gtk_container_add(GTK_CONTAINER(frame), box);
2081
2082 hbox = gtk_hbox_new(TRUE, 10);
2083 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01002084 cw.hentry = gtk_entry_new();
2085 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2086 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002087
2088 frame = gtk_frame_new("Port");
2089 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2090 box = gtk_vbox_new(FALSE, 10);
2091 gtk_container_add(GTK_CONTAINER(frame), box);
2092
2093 hbox = gtk_hbox_new(TRUE, 4);
2094 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2095 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2096
2097 frame = gtk_frame_new("Type");
2098 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2099 box = gtk_vbox_new(FALSE, 10);
2100 gtk_container_add(GTK_CONTAINER(frame), box);
2101
2102 hbox = gtk_hbox_new(TRUE, 4);
2103 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2104
Jens Axboe62bc9372012-03-07 11:45:07 +01002105 cw.combo = gtk_combo_box_new_text();
2106 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2107 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2108 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2109 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002110
Jens Axboe62bc9372012-03-07 11:45:07 +01002111 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002112
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002113 frame = gtk_frame_new("Options");
2114 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2115 box = gtk_vbox_new(FALSE, 10);
2116 gtk_container_add(GTK_CONTAINER(frame), box);
2117
2118 hbox = gtk_hbox_new(TRUE, 4);
2119 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2120
Jens Axboe62bc9372012-03-07 11:45:07 +01002121 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2122 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2123 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.");
2124 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2125
2126 /*
2127 * Connect edit signal, so we can show/not-show the auto start button
2128 */
2129 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2130 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002131
Jens Axboea7a42ce2012-03-02 13:12:04 +01002132 gtk_widget_show_all(dialog);
2133
2134 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2135 gtk_widget_destroy(dialog);
2136 return 1;
2137 }
2138
Jens Axboe62bc9372012-03-07 11:45:07 +01002139 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002140 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2141
Jens Axboe62bc9372012-03-07 11:45:07 +01002142 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002143 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2144 *type = Fio_client_ipv4;
2145 else if (!strncmp(typeentry, "IPv6", 4))
2146 *type = Fio_client_ipv6;
2147 else
2148 *type = Fio_client_socket;
2149 g_free(typeentry);
2150
Jens Axboe62bc9372012-03-07 11:45:07 +01002151 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002152
Jens Axboea7a42ce2012-03-02 13:12:04 +01002153 gtk_widget_destroy(dialog);
2154 return 0;
2155}
2156
Jens Axboe2f99deb2012-03-09 14:37:29 +01002157static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002158{
2159 struct gfio_client *gc;
2160
2161 gc = malloc(sizeof(*gc));
2162 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002163 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002164 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002165
Jens Axboe2f99deb2012-03-09 14:37:29 +01002166 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002167
2168 client->client_data = gc;
2169}
2170
Jens Axboe2f99deb2012-03-09 14:37:29 +01002171static GtkWidget *new_client_page(struct gui_entry *ge);
2172
2173static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2174{
2175 struct gui_entry *ge;
2176
2177 ge = malloc(sizeof(*ge));
2178 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002179 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002180 INIT_FLIST_HEAD(&ge->list);
2181 flist_add_tail(&ge->list, &ui->list);
2182 ge->ui = ui;
2183 return ge;
2184}
2185
Jens Axboe2f99deb2012-03-09 14:37:29 +01002186static struct gui_entry *get_new_ge_with_tab(const char *name)
2187{
2188 struct gui_entry *ge;
2189
2190 ge = alloc_new_gui_entry(&main_ui);
2191
2192 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002193 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002194
2195 ge->page_label = gtk_label_new(name);
2196 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2197
2198 gtk_widget_show_all(main_ui.window);
2199 return ge;
2200}
2201
2202static void file_new(GtkWidget *w, gpointer data)
2203{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002204 struct gui *ui = (struct gui *) data;
2205 struct gui_entry *ge;
2206
2207 ge = get_new_ge_with_tab("Untitled");
2208 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002209}
2210
2211/*
2212 * Return the 'ge' corresponding to the tab. If the active tab is the
2213 * main tab, open a new tab.
2214 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002215static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002216{
2217 struct flist_head *entry;
2218 struct gui_entry *ge;
2219
Jens Axboe38634cb2012-03-13 12:26:41 +01002220 if (!cur_page) {
2221 if (created)
2222 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002223 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002224 }
2225
2226 if (created)
2227 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002228
2229 flist_for_each(entry, &main_ui.list) {
2230 ge = flist_entry(entry, struct gui_entry, list);
2231 if (ge->page_num == cur_page)
2232 return ge;
2233 }
2234
2235 return NULL;
2236}
2237
Jens Axboe85dd01e2012-03-12 14:33:16 +01002238static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2239{
2240 gint cur_page;
2241
2242 /*
2243 * Main tab is tab 0, so any current page other than 0 holds
2244 * a ge entry.
2245 */
2246 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2247 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002248 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002249
2250 return NULL;
2251}
2252
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002253static void file_close(GtkWidget *w, gpointer data)
2254{
2255 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002256 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002257
2258 /*
2259 * Can't close the main tab
2260 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002261 ge = get_ge_from_cur_tab(ui);
2262 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002263 gtk_widget_destroy(ge->vbox);
2264 return;
2265 }
2266
Jens Axboef5c67262012-03-13 08:20:41 +01002267 if (!flist_empty(&ui->list)) {
2268 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2269 return;
2270 }
2271
Jens Axboe0fd18982012-03-14 10:34:48 +01002272 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002273}
2274
Jens Axboe38634cb2012-03-13 12:26:41 +01002275static void file_add_recent(struct gui *ui, const gchar *uri)
2276{
Jens Axboea217ba72012-03-13 20:29:39 +01002277 GtkRecentData grd;
2278
2279 memset(&grd, 0, sizeof(grd));
2280 grd.display_name = strdup("gfio");
2281 grd.description = strdup("Fio job file");
2282 grd.mime_type = strdup(GFIO_MIME);
2283 grd.app_name = strdup(g_get_application_name());
2284 grd.app_exec = strdup("gfio %f/%u");
2285
2286 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002287}
2288
2289static gchar *get_filename_from_uri(const gchar *uri)
2290{
2291 if (strncmp(uri, "file://", 7))
2292 return strdup(uri);
2293
2294 return strdup(uri + 7);
2295}
2296
2297static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2298 int type, int port)
2299{
2300 struct fio_client *client;
2301 gchar *filename;
2302
2303 filename = get_filename_from_uri(uri);
2304
2305 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2306 ge->job_files[ge->nr_job_files] = strdup(filename);
2307 ge->nr_job_files++;
2308
2309 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2310 if (!client) {
2311 GError *error;
2312
2313 error = g_error_new(g_quark_from_string("fio"), 1,
2314 "Failed to add client %s", host);
2315 report_error(error);
2316 g_error_free(error);
2317 return 1;
2318 }
2319
2320 gfio_client_added(ge, client);
2321 file_add_recent(ge->ui, uri);
2322 return 0;
2323}
2324
Jens Axboea6790902012-03-13 15:16:11 +01002325static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002326{
Jens Axboea6790902012-03-13 15:16:11 +01002327 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002328 struct gui_entry *ge;
2329 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002330 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002331 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002332
2333 /*
2334 * Creates new tab if current tab is the main window, or the
2335 * current tab already has a client.
2336 */
2337 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002338 ge = get_ge_from_page(cur_page, &ge_is_new);
2339 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002340 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002341 ge_is_new = 1;
2342 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002343
2344 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002345
Jens Axboea6790902012-03-13 15:16:11 +01002346 if (get_connection_details(&host, &port, &type, &server_start)) {
2347 if (ge_is_new)
2348 gtk_widget_destroy(ge->vbox);
2349
2350 return 1;
2351 }
2352
2353 ret = do_file_open(ge, uri, host, type, port);
2354
2355 free(host);
2356
2357 if (!ret) {
2358 if (server_start)
2359 gfio_start_server();
2360 } else {
2361 if (ge_is_new)
2362 gtk_widget_destroy(ge->vbox);
2363 }
2364
2365 return ret;
2366}
2367
2368static void recent_open(GtkAction *action, gpointer data)
2369{
2370 struct gui *ui = (struct gui *) data;
2371 GtkRecentInfo *info;
2372 const gchar *uri;
2373
2374 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2375 uri = gtk_recent_info_get_uri(info);
2376
2377 do_file_open_with_tab(ui, uri);
2378}
2379
2380static void file_open(GtkWidget *w, gpointer data)
2381{
2382 struct gui *ui = data;
2383 GtkWidget *dialog;
2384 GSList *filenames, *fn_glist;
2385 GtkFileFilter *filter;
2386
Jens Axboe0420ba62012-02-29 11:16:52 +01002387 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002388 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002389 GTK_FILE_CHOOSER_ACTION_OPEN,
2390 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2391 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2392 NULL);
2393 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2394
2395 filter = gtk_file_filter_new();
2396 gtk_file_filter_add_pattern(filter, "*.fio");
2397 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002398 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002399 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002400 gtk_file_filter_set_name(filter, "Fio job file");
2401 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2402
2403 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2404 gtk_widget_destroy(dialog);
2405 return;
2406 }
2407
2408 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002409
2410 gtk_widget_destroy(dialog);
2411
Jens Axboe0420ba62012-02-29 11:16:52 +01002412 filenames = fn_glist;
2413 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002414 if (do_file_open_with_tab(ui, filenames->data))
2415 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002416 filenames = g_slist_next(filenames);
2417 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002418
Jens Axboe0420ba62012-02-29 11:16:52 +01002419 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002420}
2421
2422static void file_save(GtkWidget *w, gpointer data)
2423{
Jens Axboe63a130b2012-03-06 20:08:59 +01002424 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002425 GtkWidget *dialog;
2426
2427 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002428 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002429 GTK_FILE_CHOOSER_ACTION_SAVE,
2430 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2431 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2432 NULL);
2433
2434 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2435 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2436
2437 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2438 char *filename;
2439
2440 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2441 // save_job_file(filename);
2442 g_free(filename);
2443 }
2444 gtk_widget_destroy(dialog);
2445}
2446
Jens Axboe9b260bd2012-03-06 11:02:52 +01002447static void view_log_destroy(GtkWidget *w, gpointer data)
2448{
2449 struct gui *ui = (struct gui *) data;
2450
2451 gtk_widget_ref(ui->log_tree);
2452 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2453 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002454 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002455}
2456
2457static void view_log(GtkWidget *w, gpointer data)
2458{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002459 GtkWidget *win, *scroll, *vbox, *box;
2460 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002461
Jens Axboe4cbe7212012-03-06 13:36:17 +01002462 if (ui->log_view)
2463 return;
2464
2465 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002466 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002467 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002468
Jens Axboe4cbe7212012-03-06 13:36:17 +01002469 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002470
Jens Axboe4cbe7212012-03-06 13:36:17 +01002471 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2472
2473 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2474
2475 box = gtk_hbox_new(TRUE, 0);
2476 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2477 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2478 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2479
2480 vbox = gtk_vbox_new(TRUE, 5);
2481 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2482
2483 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002484 gtk_widget_show_all(win);
2485}
2486
Jens Axboe85dd01e2012-03-12 14:33:16 +01002487static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002488{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002489 struct gui *ui = (struct gui *) data;
2490 struct gui_entry *ge;
2491
2492 ge = get_ge_from_cur_tab(ui);
2493 if (ge)
2494 connect_clicked(w, ge);
2495}
2496
2497static void send_job_entry(GtkWidget *w, gpointer data)
2498{
2499 struct gui *ui = (struct gui *) data;
2500 struct gui_entry *ge;
2501
2502 ge = get_ge_from_cur_tab(ui);
2503 if (ge)
2504 send_clicked(w, ge);
2505
2506}
2507
2508static void edit_job_entry(GtkWidget *w, gpointer data)
2509{
2510}
2511
2512static void start_job_entry(GtkWidget *w, gpointer data)
2513{
2514 struct gui *ui = (struct gui *) data;
2515 struct gui_entry *ge;
2516
2517 ge = get_ge_from_cur_tab(ui);
2518 if (ge)
2519 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002520}
2521
Jens Axboe781ccba2012-03-15 09:44:42 +01002522static void view_results(GtkWidget *w, gpointer data)
2523{
2524 struct gui *ui = (struct gui *) data;
2525 struct gfio_client *gc;
2526 struct gui_entry *ge;
2527
2528 ge = get_ge_from_cur_tab(ui);
2529 if (!ge)
2530 return;
2531
2532 if (ge->results_window)
2533 return;
2534
2535 gc = ge->client;
2536 if (gc && gc->nr_results)
2537 gfio_display_end_results(gc);
2538}
2539
Jens Axboe8577f4f2012-03-09 19:28:27 +01002540static void __update_graph_limits(struct gfio_graphs *g)
2541{
2542 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2543 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2544}
2545
2546static void update_graph_limits(void)
2547{
2548 struct flist_head *entry;
2549 struct gui_entry *ge;
2550
2551 __update_graph_limits(&main_ui.graphs);
2552
2553 flist_for_each(entry, &main_ui.list) {
2554 ge = flist_entry(entry, struct gui_entry, list);
2555 __update_graph_limits(&ge->graphs);
2556 }
2557}
2558
Jens Axboe46974a72012-03-02 19:34:13 +01002559static void preferences(GtkWidget *w, gpointer data)
2560{
Jens Axboef3e84402012-03-07 13:14:32 +01002561 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002562 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002563 int i;
2564
2565 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002566 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002567 GTK_DIALOG_DESTROY_WITH_PARENT,
2568 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2569 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2570 NULL);
2571
Jens Axboe8577f4f2012-03-09 19:28:27 +01002572 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002573 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2574 vbox = gtk_vbox_new(FALSE, 6);
2575 gtk_container_add(GTK_CONTAINER(frame), vbox);
2576
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002577 hbox = gtk_hbox_new(FALSE, 5);
2578 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2579 entry = gtk_label_new("Font face to use for graph labels");
2580 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2581
Jens Axboef3e84402012-03-07 13:14:32 +01002582 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002583 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002584
Jens Axboe8577f4f2012-03-09 19:28:27 +01002585 box = gtk_vbox_new(FALSE, 6);
2586 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2587
2588 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002589 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002590 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2591 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2592
Jens Axboec05d9052012-03-11 13:05:35 +01002593 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002594
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002595 box = gtk_vbox_new(FALSE, 6);
2596 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2597
2598 hbox = gtk_hbox_new(FALSE, 5);
2599 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2600 entry = gtk_label_new("Client ETA request interval (msec)");
2601 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2602
2603 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002604 frame = gtk_frame_new("Debug logging");
2605 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2606 vbox = gtk_vbox_new(FALSE, 6);
2607 gtk_container_add(GTK_CONTAINER(frame), vbox);
2608
2609 box = gtk_hbox_new(FALSE, 6);
2610 gtk_container_add(GTK_CONTAINER(vbox), box);
2611
2612 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2613
2614 for (i = 0; i < FD_DEBUG_MAX; i++) {
2615 if (i == 7) {
2616 box = gtk_hbox_new(FALSE, 6);
2617 gtk_container_add(GTK_CONTAINER(vbox), box);
2618 }
2619
2620
2621 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2622 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2623 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2624 }
2625
Jens Axboe46974a72012-03-02 19:34:13 +01002626 gtk_widget_show_all(dialog);
2627
2628 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2629 gtk_widget_destroy(dialog);
2630 return;
2631 }
2632
2633 for (i = 0; i < FD_DEBUG_MAX; i++) {
2634 int set;
2635
2636 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2637 if (set)
2638 fio_debug |= (1UL << i);
2639 }
2640
Jens Axboef3e84402012-03-07 13:14:32 +01002641 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002642 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2643 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002644 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002645
Jens Axboe46974a72012-03-02 19:34:13 +01002646 gtk_widget_destroy(dialog);
2647}
2648
Jens Axboe0420ba62012-02-29 11:16:52 +01002649static void about_dialog(GtkWidget *w, gpointer data)
2650{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002651 const char *authors[] = {
2652 "Jens Axboe <axboe@kernel.dk>",
2653 "Stephen Carmeron <stephenmcameron@gmail.com>",
2654 NULL
2655 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002656 const char *license[] = {
2657 "Fio is free software; you can redistribute it and/or modify "
2658 "it under the terms of the GNU General Public License as published by "
2659 "the Free Software Foundation; either version 2 of the License, or "
2660 "(at your option) any later version.\n",
2661 "Fio is distributed in the hope that it will be useful, "
2662 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2663 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2664 "GNU General Public License for more details.\n",
2665 "You should have received a copy of the GNU General Public License "
2666 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2667 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2668 };
2669 char *license_trans;
2670
2671 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2672 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002673
Jens Axboe0420ba62012-02-29 11:16:52 +01002674 gtk_show_about_dialog(NULL,
2675 "program-name", "gfio",
2676 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002677 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002678 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2679 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002680 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002681 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002682 "logo-icon-name", "fio",
2683 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002684 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002685 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002686
Jens Axboe2f99deb2012-03-09 14:37:29 +01002687 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002688}
2689
2690static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002691 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002692 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002693 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002694 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002695 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002696 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002697 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2698 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2699 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002700 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01002701 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002702 { "ConnectJob", NULL, "Connect", "<Control>E", NULL, G_CALLBACK(connect_job_entry) },
2703 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2704 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2705 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002706 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2707 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002708};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002709static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002710
2711static const gchar *ui_string = " \
2712 <ui> \
2713 <menubar name=\"MainMenu\"> \
2714 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002715 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01002716 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002717 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002718 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002719 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002720 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002721 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2722 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002723 <placeholder name=\"FileRecentFiles\"/> \
2724 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002725 <menuitem name=\"Quit\" action=\"Quit\" /> \
2726 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002727 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002728 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002729 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002730 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2731 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002732 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002733 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002734 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002735 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01002736 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2737 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002738 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2739 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002740 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2741 <menuitem name=\"About\" action=\"About\" /> \
2742 </menu> \
2743 </menubar> \
2744 </ui> \
2745";
2746
Jens Axboe4cbe7212012-03-06 13:36:17 +01002747static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2748 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002749{
Jens Axboeca664f42012-03-14 19:49:40 +01002750 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002751 GError *error = 0;
2752
2753 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002754 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002755
2756 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2757 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2758
2759 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002760
Jens Axboe0420ba62012-02-29 11:16:52 +01002761 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2762}
2763
2764void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2765 GtkWidget *vbox, GtkUIManager *ui_manager)
2766{
2767 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2768}
2769
Jens Axboec80b74b2012-03-12 10:23:28 +01002770static void combo_entry_changed(GtkComboBox *box, gpointer data)
2771{
2772 struct gui_entry *ge = (struct gui_entry *) data;
2773 gint index;
2774
2775 index = gtk_combo_box_get_active(box);
2776
2777 multitext_set_entry(&ge->eta.iotype, index);
Jens Axboe99d633a2012-03-15 15:55:04 +01002778 multitext_set_entry(&ge->eta.bs, index);
Jens Axboec80b74b2012-03-12 10:23:28 +01002779 multitext_set_entry(&ge->eta.ioengine, index);
2780 multitext_set_entry(&ge->eta.iodepth, index);
2781}
2782
2783static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2784{
2785 struct gui_entry *ge = (struct gui_entry *) data;
2786
2787 multitext_free(&ge->eta.iotype);
Jens Axboe99d633a2012-03-15 15:55:04 +01002788 multitext_free(&ge->eta.bs);
Jens Axboec80b74b2012-03-12 10:23:28 +01002789 multitext_free(&ge->eta.ioengine);
2790 multitext_free(&ge->eta.iodepth);
2791}
2792
Jens Axboe2f99deb2012-03-09 14:37:29 +01002793static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002794{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002795 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002796 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01002797
Jens Axboe2f99deb2012-03-09 14:37:29 +01002798 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002799
Jens Axboe65476332012-03-13 10:37:04 +01002800 top_align = gtk_alignment_new(0, 0, 1, 0);
2801 top_vbox = gtk_vbox_new(FALSE, 3);
2802 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2803 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002804
Jens Axboe3e47bd22012-02-29 13:45:02 +01002805 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002806 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002807 probe_frame = gtk_vbox_new(FALSE, 3);
2808 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2809
2810 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002811 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2812 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2813 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2814 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2815 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002816
Jens Axboe3e47bd22012-02-29 13:45:02 +01002817 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002818 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2819
Jens Axboe3863d1a2012-03-09 17:39:05 +01002820 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002821 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2822 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2823 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
Jens Axboe99d633a2012-03-15 15:55:04 +01002824 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
Jens Axboec80b74b2012-03-12 10:23:28 +01002825 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2826 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002827 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2828 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2829
2830 probe_box = gtk_hbox_new(FALSE, 3);
2831 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2832 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2833 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2834 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2835 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2836
2837 /*
2838 * Only add this if we have a commit rate
2839 */
2840#if 0
2841 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002842 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002843
Jens Axboe2f99deb2012-03-09 14:37:29 +01002844 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2845 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2846
2847 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2848 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2849#endif
2850
2851 /*
2852 * Set up a drawing area and IOPS and bandwidth graphs
2853 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002854 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002855 gtk_widget_set_size_request(GTK_WIDGET(ge->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(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2858 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2859 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2860 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2861 G_CALLBACK(on_config_drawing_area), &ge->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),
Jens Axboe2f99deb2012-03-09 14:37:29 +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 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002867 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002868
2869 setup_graphs(&ge->graphs);
2870
2871 /*
2872 * Set up alignments for widgets at the bottom of ui,
2873 * align bottom left, expand horizontally but not vertically
2874 */
Jens Axboe65476332012-03-13 10:37:04 +01002875 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002876 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002877 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2878 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002879
2880 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2881
2882 /*
2883 * Set up thread status progress bar
2884 */
2885 ge->thread_status_pb = gtk_progress_bar_new();
2886 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2887 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2888 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2889
2890
2891 return main_vbox;
2892}
2893
2894static GtkWidget *new_main_page(struct gui *ui)
2895{
2896 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002897 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002898
2899 main_vbox = gtk_vbox_new(FALSE, 3);
2900
2901 /*
2902 * Set up alignments for widgets at the top of ui,
2903 * align top left, expand horizontally but not vertically
2904 */
Jens Axboe65476332012-03-13 10:37:04 +01002905 top_align = gtk_alignment_new(0, 0, 1, 0);
2906 top_vbox = gtk_vbox_new(FALSE, 0);
2907 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2908 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002909
2910 probe = gtk_frame_new("Run statistics");
2911 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2912 probe_frame = gtk_vbox_new(FALSE, 3);
2913 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002914
2915 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002916 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01002917 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01002918 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2919 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2920 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2921 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002922
2923 /*
2924 * Only add this if we have a commit rate
2925 */
2926#if 0
2927 probe_box = gtk_hbox_new(FALSE, 3);
2928 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2929
Jens Axboe3e47bd22012-02-29 13:45:02 +01002930 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2931 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2932
Jens Axboe3e47bd22012-02-29 13:45:02 +01002933 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2934 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002935#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002936
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002937 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002938 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002939 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002940 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002941 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002942 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002943 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2944 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2945 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2946 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2947 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002948 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2949 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002950 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002951 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002952 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002953 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002954 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002955
Jens Axboe2f99deb2012-03-09 14:37:29 +01002956 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002957
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002958 /*
2959 * Set up alignments for widgets at the bottom of ui,
2960 * align bottom left, expand horizontally but not vertically
2961 */
Jens Axboe65476332012-03-13 10:37:04 +01002962 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002963 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002964 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2965 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002966
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002967 /*
2968 * Set up thread status progress bar
2969 */
2970 ui->thread_status_pb = gtk_progress_bar_new();
2971 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002972 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002973 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2974
Jens Axboe2f99deb2012-03-09 14:37:29 +01002975 return main_vbox;
2976}
2977
2978static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2979 guint page, gpointer data)
2980
2981{
Jens Axboe02421e62012-03-12 12:05:50 +01002982 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002983 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01002984
Jens Axboe85dd01e2012-03-12 14:33:16 +01002985 if (!page) {
2986 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01002987 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002988 return TRUE;
2989 }
2990
2991 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01002992 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002993 if (ge)
2994 update_button_states(ui, ge);
2995
Jens Axboe2f99deb2012-03-09 14:37:29 +01002996 return TRUE;
2997}
2998
Jens Axboe38634cb2012-03-13 12:26:41 +01002999static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3000{
3001 time_t time_a = gtk_recent_info_get_visited(a);
3002 time_t time_b = gtk_recent_info_get_visited(b);
3003
3004 return time_b - time_a;
3005}
3006
3007static void add_recent_file_items(struct gui *ui)
3008{
3009 const gchar *gfio = g_get_application_name();
3010 GList *items, *item;
3011 int i = 0;
3012
3013 if (ui->recent_ui_id) {
3014 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3015 gtk_ui_manager_ensure_update(ui->uimanager);
3016 }
3017 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3018
3019 if (ui->actiongroup) {
3020 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3021 g_object_unref(ui->actiongroup);
3022 }
3023 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3024
3025 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3026
3027 items = gtk_recent_manager_get_items(ui->recentmanager);
3028 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3029
3030 for (item = items; item && item->data; item = g_list_next(item)) {
3031 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3032 gchar *action_name;
3033 const gchar *label;
3034 GtkAction *action;
3035
3036 if (!gtk_recent_info_has_application(info, gfio))
3037 continue;
3038
3039 /*
3040 * We only support local files for now
3041 */
3042 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3043 continue;
3044
3045 action_name = g_strdup_printf("RecentFile%u", i++);
3046 label = gtk_recent_info_get_display_name(info);
3047
3048 action = g_object_new(GTK_TYPE_ACTION,
3049 "name", action_name,
3050 "label", label, NULL);
3051
3052 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3053 gtk_recent_info_ref(info),
3054 (GDestroyNotify) gtk_recent_info_unref);
3055
3056
3057 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3058
3059 gtk_action_group_add_action(ui->actiongroup, action);
3060 g_object_unref(action);
3061
3062 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3063 "/MainMenu/FileMenu/FileRecentFiles",
3064 label, action_name,
3065 GTK_UI_MANAGER_MENUITEM, FALSE);
3066
3067 g_free(action_name);
3068
3069 if (i == 8)
3070 break;
3071 }
3072
3073 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3074 g_list_free(items);
3075}
3076
Jens Axboea6790902012-03-13 15:16:11 +01003077static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3078 gint x, gint y, GtkSelectionData *data,
3079 guint info, guint time)
3080{
3081 struct gui *ui = &main_ui;
3082 gchar **uris;
3083 GtkWidget *source;
3084 int i;
3085
3086 source = gtk_drag_get_source_widget(ctx);
3087 if (source && widget == gtk_widget_get_toplevel(source)) {
3088 gtk_drag_finish(ctx, FALSE, FALSE, time);
3089 return;
3090 }
3091
3092 uris = gtk_selection_data_get_uris(data);
3093 if (!uris) {
3094 gtk_drag_finish(ctx, FALSE, FALSE, time);
3095 return;
3096 }
3097
3098 i = 0;
3099 while (uris[i]) {
3100 if (do_file_open_with_tab(ui, uris[i]))
3101 break;
3102 i++;
3103 }
3104
3105 gtk_drag_finish(ctx, TRUE, FALSE, time);
3106 g_strfreev(uris);
3107}
3108
Jens Axboe2f99deb2012-03-09 14:37:29 +01003109static void init_ui(int *argc, char **argv[], struct gui *ui)
3110{
3111 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003112 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003113
3114 /* Magical g*thread incantation, you just need this thread stuff.
3115 * Without it, the update that happens in gfio_update_thread_status
3116 * doesn't really happen in a timely fashion, you need expose events
3117 */
3118 if (!g_thread_supported())
3119 g_thread_init(NULL);
3120 gdk_threads_init();
3121
3122 gtk_init(argc, argv);
3123 settings = gtk_settings_get_default();
3124 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3125 g_type_init();
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003126 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003127
3128 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003129 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003130 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3131
3132 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3133 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3134
3135 ui->vbox = gtk_vbox_new(FALSE, 0);
3136 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3137
Jens Axboe02421e62012-03-12 12:05:50 +01003138 ui->uimanager = gtk_ui_manager_new();
3139 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3140 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003141
Jens Axboe38634cb2012-03-13 12:26:41 +01003142 ui->recentmanager = gtk_recent_manager_get_default();
3143 add_recent_file_items(ui);
3144
Jens Axboe2f99deb2012-03-09 14:37:29 +01003145 ui->notebook = gtk_notebook_new();
3146 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003147 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003148 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003149 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3150
3151 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003152 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3153 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3154 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003155
3156 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3157
Jens Axboe9b260bd2012-03-06 11:02:52 +01003158 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003159
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003160 gtk_widget_show_all(ui->window);
3161}
3162
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003163int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003164{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003165 if (initialize_fio(envp))
3166 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003167 if (fio_init_options())
3168 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003169
Jens Axboe2f99deb2012-03-09 14:37:29 +01003170 memset(&main_ui, 0, sizeof(main_ui));
3171 INIT_FLIST_HEAD(&main_ui.list);
3172
3173 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003174
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003175 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003176 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003177 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003178 return 0;
3179}