blob: 675a87754777bad33f6ff8e02c2f6c4626bce319 [file] [log] [blame]
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001/*
2 * gfio - gui front end for fio - the flexible io tester
3 *
4 * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
Jens Axboec0187f32012-03-06 15:39:15 +01005 * Copyright (C) 2012 Jens Axboe <axboe@kernel.dk>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01006 *
7 * The license below covers all files distributed with fio unless otherwise
8 * noted in the file itself.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
Stephen M. Cameron8232e282012-02-24 08:17:31 +010024#include <locale.h>
Stephen M. Cameron60f6b332012-02-24 08:17:32 +010025#include <malloc.h>
Jens Axboe6b79c802012-03-08 10:51:36 +010026#include <string.h>
Stephen M. Cameron8232e282012-02-24 08:17:31 +010027
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +010028#include <glib.h>
Jens Axboe2fd3bb02012-03-07 08:07:39 +010029#include <cairo.h>
Stephen M. Cameronff1f3282012-02-24 08:17:30 +010030#include <gtk/gtk.h>
31
Stephen M. Cameron8232e282012-02-24 08:17:31 +010032#include "fio.h"
Jens Axboe53e0e852012-03-15 19:38:01 +010033#include "gfio.h"
34#include "ghelpers.h"
Jens Axboe9af4a242012-03-16 10:13:49 +010035#include "goptions.h"
Jens Axboe2fd3bb02012-03-07 08:07:39 +010036#include "graph.h"
Stephen M. Cameron8232e282012-02-24 08:17:31 +010037
Jens Axboe63a130b2012-03-06 20:08:59 +010038static int gfio_server_running;
Jens Axboef3e84402012-03-07 13:14:32 +010039static const char *gfio_graph_font;
Jens Axboe8577f4f2012-03-09 19:28:27 +010040static unsigned int gfio_graph_limit = 100;
Stephen M. Cameron814479d2012-03-15 07:58:14 +010041static GdkColor white;
Jens Axboe63a130b2012-03-06 20:08:59 +010042
Jens Axboe6b79c802012-03-08 10:51:36 +010043static void view_log(GtkWidget *w, gpointer data);
Jens Axboe3e47bd22012-02-29 13:45:02 +010044
Stephen M. Cameronf3074002012-02-24 08:17:30 +010045typedef void (*clickfunction)(GtkWidget *widget, gpointer data);
46
Jens Axboe3e47bd22012-02-29 13:45:02 +010047static void connect_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010048static void start_job_clicked(GtkWidget *widget, gpointer data);
Jens Axboeb9d2f302012-03-08 20:36:28 +010049static void send_clicked(GtkWidget *widget, gpointer data);
Stephen M. Cameronf3074002012-02-24 08:17:30 +010050
51static struct button_spec {
52 const char *buttontext;
53 clickfunction f;
Jens Axboe014f4022012-03-15 14:03:01 +010054 const char *tooltiptext[2];
55 const int start_sensitive;
Stephen M. Cameronf3074002012-02-24 08:17:30 +010056} buttonspeclist[] = {
Jens Axboe53e0e852012-03-15 19:38:01 +010057 {
58 .buttontext = "Connect",
59 .f = connect_clicked,
60 .tooltiptext = { "Disconnect from host", "Connect to host" },
61 .start_sensitive = 1,
62 },
63 {
64 .buttontext = "Send",
65 .f = send_clicked,
66 .tooltiptext = { "Send job description to host", NULL },
67 .start_sensitive = 0,
68 },
69 {
70 .buttontext = "Start Job",
71 .f = start_job_clicked,
72 .tooltiptext = { "Start the current job on the server", NULL },
73 .start_sensitive = 0,
74 },
Jens Axboee0681f32012-03-06 12:14:42 +010075};
76
Jens Axboe9988ca72012-03-09 15:14:06 +010077static void gfio_update_thread_status(struct gui_entry *ge, char *status_message, double perc);
78static void gfio_update_thread_status_all(char *status_message, double perc);
Jens Axboec7249262012-03-09 17:11:04 +010079void report_error(GError *error);
Jens Axboe9988ca72012-03-09 15:14:06 +010080
Jens Axboe2f99deb2012-03-09 14:37:29 +010081static struct graph *setup_iops_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010082{
Jens Axboe2f99deb2012-03-09 14:37:29 +010083 struct graph *g;
84
85 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +010086 graph_title(g, "IOPS (IOs/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +010087 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +010088 graph_add_label(g, "Read IOPS");
89 graph_add_label(g, "Write IOPS");
90 graph_set_color(g, "Read IOPS", 0.13, 0.54, 0.13);
91 graph_set_color(g, "Write IOPS", 1.0, 0.0, 0.0);
Jens Axboe8577f4f2012-03-09 19:28:27 +010092 line_graph_set_data_count_limit(g, gfio_graph_limit);
Jens Axboed8fbeef2012-03-14 10:25:44 +010093 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +010094 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +010095}
96
Jens Axboe2f99deb2012-03-09 14:37:29 +010097static struct graph *setup_bandwidth_graph(void)
Jens Axboe2fd3bb02012-03-07 08:07:39 +010098{
Jens Axboe2f99deb2012-03-09 14:37:29 +010099 struct graph *g;
100
101 g = graph_new(DRAWING_AREA_XDIM / 2.0, DRAWING_AREA_YDIM, gfio_graph_font);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100102 graph_title(g, "Bandwidth (bytes/sec)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100103 graph_x_title(g, "Time (secs)");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100104 graph_add_label(g, "Read Bandwidth");
105 graph_add_label(g, "Write Bandwidth");
106 graph_set_color(g, "Read Bandwidth", 0.13, 0.54, 0.13);
107 graph_set_color(g, "Write Bandwidth", 1.0, 0.0, 0.0);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100108 graph_set_base_offset(g, 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100109 line_graph_set_data_count_limit(g, 100);
Jens Axboed8fbeef2012-03-14 10:25:44 +0100110 graph_add_extra_space(g, 0.0, 0.0, 0.0, 0.0);
Jens Axboe2f99deb2012-03-09 14:37:29 +0100111 return g;
Jens Axboe2fd3bb02012-03-07 08:07:39 +0100112}
113
Jens Axboe2f99deb2012-03-09 14:37:29 +0100114static void setup_graphs(struct gfio_graphs *g)
Jens Axboe8663ea62012-03-02 14:04:30 +0100115{
Jens Axboe2f99deb2012-03-09 14:37:29 +0100116 g->iops_graph = setup_iops_graph();
117 g->bandwidth_graph = setup_bandwidth_graph();
118}
119
Jens Axboec80b74b2012-03-12 10:23:28 +0100120static void multitext_add_entry(struct multitext_widget *mt, const char *text)
121{
122 mt->text = realloc(mt->text, (mt->max_text + 1) * sizeof(char *));
123 mt->text[mt->max_text] = strdup(text);
124 mt->max_text++;
125}
126
127static void multitext_set_entry(struct multitext_widget *mt, unsigned int index)
128{
129 if (index >= mt->max_text)
130 return;
Jens Axboeda185432012-03-12 11:05:46 +0100131 if (!mt->text || !mt->text[index])
Jens Axboec80b74b2012-03-12 10:23:28 +0100132 return;
133
134 mt->cur_text = index;
135 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
136}
137
138static void multitext_update_entry(struct multitext_widget *mt,
139 unsigned int index, const char *text)
140{
Jens Axboeda185432012-03-12 11:05:46 +0100141 if (!mt->text)
142 return;
143
Jens Axboec80b74b2012-03-12 10:23:28 +0100144 if (mt->text[index])
145 free(mt->text[index]);
146
147 mt->text[index] = strdup(text);
148 if (mt->cur_text == index)
149 gtk_entry_set_text(GTK_ENTRY(mt->entry), mt->text[index]);
150}
151
152static void multitext_free(struct multitext_widget *mt)
153{
154 int i;
155
156 gtk_entry_set_text(GTK_ENTRY(mt->entry), "");
157
158 for (i = 0; i < mt->max_text; i++) {
159 if (mt->text[i])
160 free(mt->text[i]);
161 }
162
163 free(mt->text);
164 mt->cur_text = -1;
165 mt->max_text = 0;
166}
167
Jens Axboe2f99deb2012-03-09 14:37:29 +0100168static void clear_ge_ui_info(struct gui_entry *ge)
169{
170 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), "");
171 gtk_label_set_text(GTK_LABEL(ge->probe.os), "");
172 gtk_label_set_text(GTK_LABEL(ge->probe.arch), "");
173 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100174#if 0
175 /* should we empty it... */
Jens Axboe2f99deb2012-03-09 14:37:29 +0100176 gtk_entry_set_text(GTK_ENTRY(ge->eta.name), "");
Jens Axboe3863d1a2012-03-09 17:39:05 +0100177#endif
Jens Axboec80b74b2012-03-12 10:23:28 +0100178 multitext_update_entry(&ge->eta.iotype, 0, "");
Jens Axboe99d633a2012-03-15 15:55:04 +0100179 multitext_update_entry(&ge->eta.bs, 0, "");
Jens Axboec80b74b2012-03-12 10:23:28 +0100180 multitext_update_entry(&ge->eta.ioengine, 0, "");
181 multitext_update_entry(&ge->eta.iodepth, 0, "");
Jens Axboe2f99deb2012-03-09 14:37:29 +0100182 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), "");
183 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), "");
184 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), "");
185 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), "");
186 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), "");
187 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), "");
Jens Axboe8663ea62012-03-02 14:04:30 +0100188}
189
Jens Axboe16ce5ad2012-03-12 11:56:09 +0100190static void show_info_dialog(struct gui *ui, const char *title,
191 const char *message)
192{
193 GtkWidget *dialog, *content, *label;
194
195 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(ui->window),
196 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
197 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
198
199 content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
200 label = gtk_label_new(message);
201 gtk_container_add(GTK_CONTAINER(content), label);
202 gtk_widget_show_all(dialog);
203 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
204 gtk_dialog_run(GTK_DIALOG(dialog));
205 gtk_widget_destroy(dialog);
206}
207
Jens Axboe781ccba2012-03-15 09:44:42 +0100208static void set_menu_entry_text(struct gui *ui, const char *path,
209 const char *text)
210{
211 GtkWidget *w;
212
213 w = gtk_ui_manager_get_widget(ui->uimanager, path);
214 if (w)
215 gtk_menu_item_set_label(GTK_MENU_ITEM(w), text);
216 else
217 fprintf(stderr, "gfio: can't find path %s\n", path);
218}
219
220
221static void set_menu_entry_visible(struct gui *ui, const char *path, int show)
222{
223 GtkWidget *w;
224
225 w = gtk_ui_manager_get_widget(ui->uimanager, path);
226 if (w)
227 gtk_widget_set_sensitive(w, show);
228 else
229 fprintf(stderr, "gfio: can't find path %s\n", path);
230}
231
232static void set_job_menu_visible(struct gui *ui, int visible)
233{
234 set_menu_entry_visible(ui, "/MainMenu/JobMenu", visible);
235}
236
237static void set_view_results_visible(struct gui *ui, int visible)
238{
239 set_menu_entry_visible(ui, "/MainMenu/ViewMenu/Results", visible);
240}
241
Jens Axboe014f4022012-03-15 14:03:01 +0100242static const char *get_button_tooltip(struct button_spec *s, int sensitive)
243{
244 if (s->tooltiptext[sensitive])
245 return s->tooltiptext[sensitive];
246
247 return s->tooltiptext[0];
248}
249
250static GtkWidget *add_button(GtkWidget *buttonbox,
251 struct button_spec *buttonspec, gpointer data)
252{
253 GtkWidget *button = gtk_button_new_with_label(buttonspec->buttontext);
254 gboolean sens = buttonspec->start_sensitive;
255
256 g_signal_connect(button, "clicked", G_CALLBACK(buttonspec->f), data);
257 gtk_box_pack_start(GTK_BOX(buttonbox), button, FALSE, FALSE, 3);
258
259 sens = buttonspec->start_sensitive;
260 gtk_widget_set_tooltip_text(button, get_button_tooltip(buttonspec, sens));
261 gtk_widget_set_sensitive(button, sens);
262
263 return button;
264}
265
266static void add_buttons(struct gui_entry *ge, struct button_spec *buttonlist,
267 int nbuttons)
268{
269 int i;
270
271 for (i = 0; i < nbuttons; i++)
272 ge->button[i] = add_button(ge->buttonbox, &buttonlist[i], ge);
273}
274
Jens Axboe85dd01e2012-03-12 14:33:16 +0100275/*
276 * Update sensitivity of job buttons and job menu items, based on the
277 * state of the client.
278 */
279static void update_button_states(struct gui *ui, struct gui_entry *ge)
280{
281 unsigned int connect_state, send_state, start_state, edit_state;
282 const char *connect_str = NULL;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100283
284 switch (ge->state) {
285 default: {
286 char tmp[80];
287
288 sprintf(tmp, "Bad client state: %u\n", ge->state);
289 show_info_dialog(ui, "Error", tmp);
290 /* fall through to new state */
291 }
292
293 case GE_STATE_NEW:
294 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100295 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100296 connect_str = "Connect";
297 send_state = 0;
298 start_state = 0;
299 break;
300 case GE_STATE_CONNECTED:
301 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100302 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100303 connect_str = "Disconnect";
304 send_state = 1;
305 start_state = 0;
306 break;
307 case GE_STATE_JOB_SENT:
308 connect_state = 1;
Jens Axboe9af4a242012-03-16 10:13:49 +0100309 edit_state = 1;
Jens Axboe85dd01e2012-03-12 14:33:16 +0100310 connect_str = "Disconnect";
311 send_state = 0;
312 start_state = 1;
313 break;
314 case GE_STATE_JOB_STARTED:
315 connect_state = 1;
316 edit_state = 1;
317 connect_str = "Disconnect";
318 send_state = 0;
319 start_state = 1;
320 break;
321 case GE_STATE_JOB_RUNNING:
322 connect_state = 1;
323 edit_state = 0;
324 connect_str = "Disconnect";
325 send_state = 0;
326 start_state = 0;
327 break;
328 case GE_STATE_JOB_DONE:
329 connect_state = 1;
330 edit_state = 0;
331 connect_str = "Connect";
332 send_state = 0;
333 start_state = 0;
334 break;
335 }
336
Jens Axboe53e0e852012-03-15 19:38:01 +0100337 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_CONNECT], connect_state);
338 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_SEND], send_state);
339 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], start_state);
340 gtk_button_set_label(GTK_BUTTON(ge->button[GFIO_BUTTON_CONNECT]), connect_str);
341 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 +0100342
Jens Axboe781ccba2012-03-15 09:44:42 +0100343 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Connect", connect_state);
344 set_menu_entry_text(ui, "/MainMenu/JobMenu/Connect", connect_str);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100345
Jens Axboe781ccba2012-03-15 09:44:42 +0100346 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Edit job", edit_state);
347 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Send job", send_state);
348 set_menu_entry_visible(ui, "/MainMenu/JobMenu/Start job", start_state);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100349
Jens Axboe781ccba2012-03-15 09:44:42 +0100350 if (ge->client && ge->client->nr_results)
351 set_view_results_visible(ui, 1);
352 else
353 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +0100354}
355
356static void gfio_set_state(struct gui_entry *ge, unsigned int state)
357{
358 ge->state = state;
359 update_button_states(ge->ui, ge);
360}
361
Jens Axboea2697902012-03-05 16:43:49 +0100362#define ALIGN_LEFT 1
363#define ALIGN_RIGHT 2
364#define INVISIBLE 4
365#define UNSORTABLE 8
366
367GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title, unsigned int flags)
368{
369 GtkCellRenderer *renderer;
370 GtkTreeViewColumn *col;
371 double xalign = 0.0; /* left as default */
372 PangoAlignment align;
373 gboolean visible;
374
375 align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
376 (flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
377 PANGO_ALIGN_CENTER;
378 visible = !(flags & INVISIBLE);
379
380 renderer = gtk_cell_renderer_text_new();
381 col = gtk_tree_view_column_new();
382
383 gtk_tree_view_column_set_title(col, title);
384 if (!(flags & UNSORTABLE))
385 gtk_tree_view_column_set_sort_column_id(col, index);
386 gtk_tree_view_column_set_resizable(col, TRUE);
387 gtk_tree_view_column_pack_start(col, renderer, TRUE);
388 gtk_tree_view_column_add_attribute(col, renderer, "text", index);
389 gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
390 switch (align) {
391 case PANGO_ALIGN_LEFT:
392 xalign = 0.0;
393 break;
394 case PANGO_ALIGN_CENTER:
395 xalign = 0.5;
396 break;
397 case PANGO_ALIGN_RIGHT:
398 xalign = 1.0;
399 break;
400 }
401 gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
402 gtk_tree_view_column_set_visible(col, visible);
403 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
404 return col;
405}
406
Jens Axboe9b260bd2012-03-06 11:02:52 +0100407static void gfio_ui_setup_log(struct gui *ui)
408{
409 GtkTreeSelection *selection;
410 GtkListStore *model;
411 GtkWidget *tree_view;
412
413 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING);
414
415 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
416 gtk_widget_set_can_focus(tree_view, FALSE);
417
418 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
419 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
Jens Axboe661f7412012-03-06 13:55:45 +0100420 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
421 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100422
423 tree_view_column(tree_view, 0, "Time", ALIGN_RIGHT | UNSORTABLE);
424 tree_view_column(tree_view, 1, "Host", ALIGN_RIGHT | UNSORTABLE);
425 tree_view_column(tree_view, 2, "Level", ALIGN_RIGHT | UNSORTABLE);
Jens Axboef095d562012-03-06 13:49:12 +0100426 tree_view_column(tree_view, 3, "Text", ALIGN_LEFT | UNSORTABLE);
Jens Axboe9b260bd2012-03-06 11:02:52 +0100427
428 ui->log_model = model;
429 ui->log_tree = tree_view;
430}
431
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100432static struct graph *setup_clat_graph(char *title, unsigned int *ovals,
433 fio_fp64_t *plist,
434 unsigned int len,
435 double xdim, double ydim)
436{
437 struct graph *g;
438 int i;
439
440 g = graph_new(xdim, ydim, gfio_graph_font);
441 graph_title(g, title);
442 graph_x_title(g, "Percentile");
443
444 for (i = 0; i < len; i++) {
445 char fbuf[8];
446
447 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
448 graph_add_label(g, fbuf);
449 graph_add_data(g, fbuf, (double) ovals[i]);
450 }
451
452 return g;
453}
454
Jens Axboea2697902012-03-05 16:43:49 +0100455static GtkWidget *gfio_output_clat_percentiles(unsigned int *ovals,
456 fio_fp64_t *plist,
457 unsigned int len,
458 const char *base,
459 unsigned int scale)
460{
461 GType types[FIO_IO_U_LIST_MAX_LEN];
462 GtkWidget *tree_view;
463 GtkTreeSelection *selection;
464 GtkListStore *model;
465 GtkTreeIter iter;
466 int i;
467
468 for (i = 0; i < len; i++)
469 types[i] = G_TYPE_INT;
470
471 model = gtk_list_store_newv(len, types);
472
473 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
474 gtk_widget_set_can_focus(tree_view, FALSE);
475
Jens Axboe661f7412012-03-06 13:55:45 +0100476 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
477 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
478
Jens Axboea2697902012-03-05 16:43:49 +0100479 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
480 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
481
482 for (i = 0; i < len; i++) {
483 char fbuf[8];
484
485 sprintf(fbuf, "%2.2f%%", plist[i].u.f);
486 tree_view_column(tree_view, i, fbuf, ALIGN_RIGHT | UNSORTABLE);
487 }
488
489 gtk_list_store_append(model, &iter);
490
Jens Axboee0681f32012-03-06 12:14:42 +0100491 for (i = 0; i < len; i++) {
492 if (scale)
493 ovals[i] = (ovals[i] + 999) / 1000;
Jens Axboea2697902012-03-05 16:43:49 +0100494 gtk_list_store_set(model, &iter, i, ovals[i], -1);
Jens Axboee0681f32012-03-06 12:14:42 +0100495 }
Jens Axboea2697902012-03-05 16:43:49 +0100496
497 return tree_view;
498}
499
Jens Axboe09d574e2012-03-15 10:45:48 +0100500static int on_expose_lat_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100501{
502 struct graph *g = p;
503 cairo_t *cr;
504
505 cr = gdk_cairo_create(w->window);
506#if 0
507 if (graph_has_tooltips(g)) {
508 g_object_set(w, "has-tooltip", TRUE, NULL);
509 g_signal_connect(w, "query-tooltip", G_CALLBACK(clat_graph_tooltip), g);
510 }
511#endif
512 cairo_set_source_rgb(cr, 0, 0, 0);
513 bar_graph_draw(g, cr);
514 cairo_destroy(cr);
515
516 return FALSE;
517}
518
Jens Axboe09d574e2012-03-15 10:45:48 +0100519static gint on_config_lat_drawing_area(GtkWidget *w, GdkEventConfigure *event,
520 gpointer data)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100521{
522 struct graph *g = data;
523
524 graph_set_size(g, w->allocation.width, w->allocation.height);
525 graph_set_size(g, w->allocation.width, w->allocation.height);
526 graph_set_position(g, 0, 0);
527 return TRUE;
528}
529
530static void gfio_show_clat_percentiles(struct gfio_client *gc,
531 GtkWidget *vbox, struct thread_stat *ts,
Jens Axboea2697902012-03-05 16:43:49 +0100532 int ddir)
533{
534 unsigned int *io_u_plat = ts->io_u_plat[ddir];
535 unsigned long nr = ts->clat_stat[ddir].samples;
536 fio_fp64_t *plist = ts->percentile_list;
537 unsigned int *ovals, len, minv, maxv, scale_down;
538 const char *base;
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100539 GtkWidget *tree_view, *frame, *hbox, *drawing_area, *completion_vbox;
Jens Axboe09d574e2012-03-15 10:45:48 +0100540 struct gui_entry *ge = gc->ge;
Jens Axboea2697902012-03-05 16:43:49 +0100541 char tmp[64];
542
543 len = calc_clat_percentiles(io_u_plat, nr, plist, &ovals, &maxv, &minv);
544 if (!len)
545 goto out;
546
547 /*
548 * We default to usecs, but if the value range is such that we
549 * should scale down to msecs, do that.
550 */
551 if (minv > 2000 && maxv > 99999) {
552 scale_down = 1;
553 base = "msec";
554 } else {
555 scale_down = 0;
556 base = "usec";
557 }
558
Jens Axboea2697902012-03-05 16:43:49 +0100559 sprintf(tmp, "Completion percentiles (%s)", base);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100560 tree_view = gfio_output_clat_percentiles(ovals, plist, len, base, scale_down);
Jens Axboe09d574e2012-03-15 10:45:48 +0100561 ge->clat_graph = setup_clat_graph(tmp, ovals, plist, len, 700.0, 300.0);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100562
Jens Axboea2697902012-03-05 16:43:49 +0100563 frame = gtk_frame_new(tmp);
564 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
565
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100566 completion_vbox = gtk_vbox_new(FALSE, 3);
567 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
Jens Axboea2697902012-03-05 16:43:49 +0100568 hbox = gtk_hbox_new(FALSE, 3);
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100569 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
570 drawing_area = gtk_drawing_area_new();
571 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
572 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
573 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
Jens Axboe09d574e2012-03-15 10:45:48 +0100574 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->clat_graph);
575 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 +0100576
577 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
578out:
579 if (ovals)
580 free(ovals);
581}
582
Jens Axboe3650a3c2012-03-05 14:09:03 +0100583static void gfio_show_lat(GtkWidget *vbox, const char *name, unsigned long min,
584 unsigned long max, double mean, double dev)
585{
586 const char *base = "(usec)";
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100587 GtkWidget *hbox, *label, *frame;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100588 char *minp, *maxp;
589 char tmp[64];
590
591 if (!usec_to_msec(&min, &max, &mean, &dev))
592 base = "(msec)";
593
594 minp = num2str(min, 6, 1, 0);
595 maxp = num2str(max, 6, 1, 0);
596
Jens Axboe3650a3c2012-03-05 14:09:03 +0100597 sprintf(tmp, "%s %s", name, base);
598 frame = gtk_frame_new(tmp);
599 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
600
Jens Axboe3650a3c2012-03-05 14:09:03 +0100601 hbox = gtk_hbox_new(FALSE, 3);
Jens Axboe1c1e4a52012-03-05 14:37:38 +0100602 gtk_container_add(GTK_CONTAINER(frame), hbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100603
604 label = new_info_label_in_frame(hbox, "Minimum");
605 gtk_label_set_text(GTK_LABEL(label), minp);
606 label = new_info_label_in_frame(hbox, "Maximum");
607 gtk_label_set_text(GTK_LABEL(label), maxp);
608 label = new_info_label_in_frame(hbox, "Average");
609 sprintf(tmp, "%5.02f", mean);
610 gtk_label_set_text(GTK_LABEL(label), tmp);
611 label = new_info_label_in_frame(hbox, "Standard deviation");
612 sprintf(tmp, "%5.02f", dev);
613 gtk_label_set_text(GTK_LABEL(label), tmp);
614
615 free(minp);
616 free(maxp);
617
618}
619
Jens Axboeca850992012-03-05 20:04:43 +0100620#define GFIO_CLAT 1
621#define GFIO_SLAT 2
622#define GFIO_LAT 4
623
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100624static void gfio_show_ddir_status(struct gfio_client *gc, GtkWidget *mbox,
625 struct group_run_stats *rs,
Jens Axboe3650a3c2012-03-05 14:09:03 +0100626 struct thread_stat *ts, int ddir)
627{
628 const char *ddir_label[2] = { "Read", "Write" };
Jens Axboe0b761302012-03-05 20:44:11 +0100629 GtkWidget *frame, *label, *box, *vbox, *main_vbox;
Jens Axboee0681f32012-03-06 12:14:42 +0100630 unsigned long min[3], max[3], runt;
Jens Axboe3650a3c2012-03-05 14:09:03 +0100631 unsigned long long bw, iops;
Jens Axboeca850992012-03-05 20:04:43 +0100632 unsigned int flags = 0;
Jens Axboee0681f32012-03-06 12:14:42 +0100633 double mean[3], dev[3];
Jens Axboe3650a3c2012-03-05 14:09:03 +0100634 char *io_p, *bw_p, *iops_p;
635 int i2p;
636
637 if (!ts->runtime[ddir])
638 return;
639
640 i2p = is_power_of_2(rs->kb_base);
641 runt = ts->runtime[ddir];
642
643 bw = (1000 * ts->io_bytes[ddir]) / runt;
644 io_p = num2str(ts->io_bytes[ddir], 6, 1, i2p);
645 bw_p = num2str(bw, 6, 1, i2p);
646
647 iops = (1000 * (uint64_t)ts->total_io_u[ddir]) / runt;
648 iops_p = num2str(iops, 6, 1, 0);
649
650 box = gtk_hbox_new(FALSE, 3);
651 gtk_box_pack_start(GTK_BOX(mbox), box, TRUE, FALSE, 3);
652
653 frame = gtk_frame_new(ddir_label[ddir]);
654 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
655
Jens Axboe0b761302012-03-05 20:44:11 +0100656 main_vbox = gtk_vbox_new(FALSE, 3);
657 gtk_container_add(GTK_CONTAINER(frame), main_vbox);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100658
659 box = gtk_hbox_new(FALSE, 3);
Jens Axboe0b761302012-03-05 20:44:11 +0100660 gtk_box_pack_start(GTK_BOX(main_vbox), box, TRUE, FALSE, 3);
Jens Axboe3650a3c2012-03-05 14:09:03 +0100661
662 label = new_info_label_in_frame(box, "IO");
663 gtk_label_set_text(GTK_LABEL(label), io_p);
664 label = new_info_label_in_frame(box, "Bandwidth");
665 gtk_label_set_text(GTK_LABEL(label), bw_p);
666 label = new_info_label_in_frame(box, "IOPS");
667 gtk_label_set_text(GTK_LABEL(label), iops_p);
668 label = new_info_label_in_frame(box, "Runtime (msec)");
669 label_set_int_value(label, ts->runtime[ddir]);
670
Jens Axboee0681f32012-03-06 12:14:42 +0100671 if (calc_lat(&ts->bw_stat[ddir], &min[0], &max[0], &mean[0], &dev[0])) {
Jens Axboeca850992012-03-05 20:04:43 +0100672 double p_of_agg = 100.0;
673 const char *bw_str = "KB";
674 char tmp[32];
675
676 if (rs->agg[ddir]) {
Jens Axboee0681f32012-03-06 12:14:42 +0100677 p_of_agg = mean[0] * 100 / (double) rs->agg[ddir];
Jens Axboeca850992012-03-05 20:04:43 +0100678 if (p_of_agg > 100.0)
679 p_of_agg = 100.0;
680 }
681
Jens Axboee0681f32012-03-06 12:14:42 +0100682 if (mean[0] > 999999.9) {
683 min[0] /= 1000.0;
684 max[0] /= 1000.0;
685 mean[0] /= 1000.0;
686 dev[0] /= 1000.0;
Jens Axboeca850992012-03-05 20:04:43 +0100687 bw_str = "MB";
688 }
689
Jens Axboe0b761302012-03-05 20:44:11 +0100690 sprintf(tmp, "Bandwidth (%s)", bw_str);
691 frame = gtk_frame_new(tmp);
692 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
Jens Axboeca850992012-03-05 20:04:43 +0100693
Jens Axboe0b761302012-03-05 20:44:11 +0100694 box = gtk_hbox_new(FALSE, 3);
695 gtk_container_add(GTK_CONTAINER(frame), box);
Jens Axboeca850992012-03-05 20:04:43 +0100696
Jens Axboe0b761302012-03-05 20:44:11 +0100697 label = new_info_label_in_frame(box, "Minimum");
Jens Axboee0681f32012-03-06 12:14:42 +0100698 label_set_int_value(label, min[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100699 label = new_info_label_in_frame(box, "Maximum");
Jens Axboee0681f32012-03-06 12:14:42 +0100700 label_set_int_value(label, max[0]);
Jens Axboe0b761302012-03-05 20:44:11 +0100701 label = new_info_label_in_frame(box, "Percentage of jobs");
Jens Axboeca850992012-03-05 20:04:43 +0100702 sprintf(tmp, "%3.2f%%", p_of_agg);
703 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100704 label = new_info_label_in_frame(box, "Average");
Jens Axboee0681f32012-03-06 12:14:42 +0100705 sprintf(tmp, "%5.02f", mean[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100706 gtk_label_set_text(GTK_LABEL(label), tmp);
Jens Axboe0b761302012-03-05 20:44:11 +0100707 label = new_info_label_in_frame(box, "Standard deviation");
Jens Axboee0681f32012-03-06 12:14:42 +0100708 sprintf(tmp, "%5.02f", dev[0]);
Jens Axboeca850992012-03-05 20:04:43 +0100709 gtk_label_set_text(GTK_LABEL(label), tmp);
710 }
711
Jens Axboee0681f32012-03-06 12:14:42 +0100712 if (calc_lat(&ts->slat_stat[ddir], &min[0], &max[0], &mean[0], &dev[0]))
Jens Axboe2b089892012-03-06 08:09:17 +0100713 flags |= GFIO_SLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100714 if (calc_lat(&ts->clat_stat[ddir], &min[1], &max[1], &mean[1], &dev[1]))
Jens Axboe2b089892012-03-06 08:09:17 +0100715 flags |= GFIO_CLAT;
Jens Axboee0681f32012-03-06 12:14:42 +0100716 if (calc_lat(&ts->lat_stat[ddir], &min[2], &max[2], &mean[2], &dev[2]))
Jens Axboe2b089892012-03-06 08:09:17 +0100717 flags |= GFIO_LAT;
718
719 if (flags) {
720 frame = gtk_frame_new("Latency");
721 gtk_box_pack_start(GTK_BOX(main_vbox), frame, FALSE, FALSE, 5);
722
723 vbox = gtk_vbox_new(FALSE, 3);
724 gtk_container_add(GTK_CONTAINER(frame), vbox);
725
726 if (flags & GFIO_SLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100727 gfio_show_lat(vbox, "Submission latency", min[0], max[0], mean[0], dev[0]);
Jens Axboe2b089892012-03-06 08:09:17 +0100728 if (flags & GFIO_CLAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100729 gfio_show_lat(vbox, "Completion latency", min[1], max[1], mean[1], dev[1]);
Jens Axboe2b089892012-03-06 08:09:17 +0100730 if (flags & GFIO_LAT)
Jens Axboee0681f32012-03-06 12:14:42 +0100731 gfio_show_lat(vbox, "Total latency", min[2], max[2], mean[2], dev[2]);
Jens Axboe2b089892012-03-06 08:09:17 +0100732 }
733
734 if (ts->clat_percentiles)
Stephen M. Cameronc57f2542012-03-15 08:10:40 +0100735 gfio_show_clat_percentiles(gc, main_vbox, ts, ddir);
Jens Axboe2b089892012-03-06 08:09:17 +0100736
Jens Axboe3650a3c2012-03-05 14:09:03 +0100737 free(io_p);
738 free(bw_p);
739 free(iops_p);
740}
741
Jens Axboe09d574e2012-03-15 10:45:48 +0100742static struct graph *setup_lat_bucket_graph(const char *title, double *lat,
743 const char **labels,
744 unsigned int len,
745 double xdim, double ydim)
746{
747 struct graph *g;
748 int i;
749
750 g = graph_new(xdim, ydim, gfio_graph_font);
751 graph_title(g, title);
752 graph_x_title(g, "Buckets");
753
754 for (i = 0; i < len; i++) {
755 graph_add_label(g, labels[i]);
756 graph_add_data(g, labels[i], lat[i]);
757 }
758
759 return g;
760}
761
762static GtkWidget *gfio_output_lat_buckets(double *lat, const char **labels,
763 int num)
Jens Axboee5bd1342012-03-05 21:38:12 +0100764{
765 GtkWidget *tree_view;
766 GtkTreeSelection *selection;
767 GtkListStore *model;
768 GtkTreeIter iter;
769 GType *types;
Jens Axboe09d574e2012-03-15 10:45:48 +0100770 int i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100771
772 types = malloc(num * sizeof(GType));
773
774 for (i = 0; i < num; i++)
775 types[i] = G_TYPE_STRING;
776
777 model = gtk_list_store_newv(num, types);
778 free(types);
779 types = NULL;
780
781 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
782 gtk_widget_set_can_focus(tree_view, FALSE);
783
Jens Axboe661f7412012-03-06 13:55:45 +0100784 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
785 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
786
Jens Axboee5bd1342012-03-05 21:38:12 +0100787 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
788 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
789
790 for (i = 0; i < num; i++)
791 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
792
793 gtk_list_store_append(model, &iter);
794
795 for (i = 0; i < num; i++) {
796 char fbuf[32];
797
798 if (lat[i] <= 0.0)
799 sprintf(fbuf, "0.00");
800 else
801 sprintf(fbuf, "%3.2f%%", lat[i]);
802
803 gtk_list_store_set(model, &iter, i, fbuf, -1);
804 }
805
806 return tree_view;
807}
808
Jens Axboe09d574e2012-03-15 10:45:48 +0100809static void gfio_show_latency_buckets(struct gfio_client *gc, GtkWidget *vbox,
810 struct thread_stat *ts)
Jens Axboee5bd1342012-03-05 21:38:12 +0100811{
Jens Axboe09d574e2012-03-15 10:45:48 +0100812 double io_u_lat[FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR];
813 const char *ranges[] = { "2u", "4u", "10u", "20u", "50u", "100u",
814 "250u", "500u", "750u", "1m", "2m",
815 "4m", "10m", "20m", "50m", "100m",
816 "250m", "500m", "750m", "1s", "2s", ">= 2s" };
817 int start, end, i;
818 const int total = FIO_IO_U_LAT_U_NR + FIO_IO_U_LAT_M_NR;
819 GtkWidget *frame, *tree_view, *hbox, *completion_vbox, *drawing_area;
820 struct gui_entry *ge = gc->ge;
Jens Axboee5bd1342012-03-05 21:38:12 +0100821
Jens Axboe09d574e2012-03-15 10:45:48 +0100822 stat_calc_lat_u(ts, io_u_lat);
823 stat_calc_lat_m(ts, &io_u_lat[FIO_IO_U_LAT_U_NR]);
Jens Axboee5bd1342012-03-05 21:38:12 +0100824
Jens Axboe09d574e2012-03-15 10:45:48 +0100825 /*
826 * Found out which first bucket has entries, and which last bucket
827 */
828 start = end = -1U;
829 for (i = 0; i < total; i++) {
830 if (io_u_lat[i] == 0.00)
831 continue;
Jens Axboee5bd1342012-03-05 21:38:12 +0100832
Jens Axboe09d574e2012-03-15 10:45:48 +0100833 if (start == -1U)
834 start = i;
835 end = i;
Jens Axboee5bd1342012-03-05 21:38:12 +0100836 }
837
Jens Axboe09d574e2012-03-15 10:45:48 +0100838 /*
839 * No entries...
840 */
841 if (start == -1U)
842 return;
843
844 tree_view = gfio_output_lat_buckets(&io_u_lat[start], &ranges[start], end - start + 1);
845 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 +0100846
Jens Axboe09d574e2012-03-15 10:45:48 +0100847 frame = gtk_frame_new("Latency buckets");
848 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
849
850 completion_vbox = gtk_vbox_new(FALSE, 3);
851 gtk_container_add(GTK_CONTAINER(frame), completion_vbox);
852 hbox = gtk_hbox_new(FALSE, 3);
853 gtk_container_add(GTK_CONTAINER(completion_vbox), hbox);
854
855 drawing_area = gtk_drawing_area_new();
856 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), 700, 300);
857 gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &white);
858 gtk_container_add(GTK_CONTAINER(completion_vbox), drawing_area);
859 g_signal_connect(G_OBJECT(drawing_area), "expose_event", G_CALLBACK(on_expose_lat_drawing_area), ge->lat_bucket_graph);
860 g_signal_connect(G_OBJECT(drawing_area), "configure_event", G_CALLBACK(on_config_lat_drawing_area), ge->lat_bucket_graph);
861
862 gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
Jens Axboee5bd1342012-03-05 21:38:12 +0100863}
864
Jens Axboe2e331012012-03-05 22:07:54 +0100865static void gfio_show_cpu_usage(GtkWidget *vbox, struct thread_stat *ts)
866{
867 GtkWidget *box, *frame, *entry;
868 double usr_cpu, sys_cpu;
869 unsigned long runtime;
870 char tmp[32];
871
872 runtime = ts->total_run_time;
873 if (runtime) {
874 double runt = (double) runtime;
875
876 usr_cpu = (double) ts->usr_time * 100 / runt;
877 sys_cpu = (double) ts->sys_time * 100 / runt;
878 } else {
879 usr_cpu = 0;
880 sys_cpu = 0;
881 }
882
883 frame = gtk_frame_new("OS resources");
884 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
885
886 box = gtk_hbox_new(FALSE, 3);
887 gtk_container_add(GTK_CONTAINER(frame), box);
888
889 entry = new_info_entry_in_frame(box, "User CPU");
890 sprintf(tmp, "%3.2f%%", usr_cpu);
891 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
892 entry = new_info_entry_in_frame(box, "System CPU");
893 sprintf(tmp, "%3.2f%%", sys_cpu);
894 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
895 entry = new_info_entry_in_frame(box, "Context switches");
896 entry_set_int_value(entry, ts->ctx);
897 entry = new_info_entry_in_frame(box, "Major faults");
898 entry_set_int_value(entry, ts->majf);
899 entry = new_info_entry_in_frame(box, "Minor faults");
900 entry_set_int_value(entry, ts->minf);
901}
Jens Axboe19998db2012-03-06 09:17:59 +0100902static void gfio_add_sc_depths_tree(GtkListStore *model,
903 struct thread_stat *ts, unsigned int len,
904 int submit)
905{
906 double io_u_dist[FIO_IO_U_MAP_NR];
907 GtkTreeIter iter;
908 /* Bits 0, and 3-8 */
909 const int add_mask = 0x1f9;
910 int i, j;
911
912 if (submit)
913 stat_calc_dist(ts->io_u_submit, ts->total_submit, io_u_dist);
914 else
915 stat_calc_dist(ts->io_u_complete, ts->total_complete, io_u_dist);
916
917 gtk_list_store_append(model, &iter);
918
919 gtk_list_store_set(model, &iter, 0, submit ? "Submit" : "Complete", -1);
920
921 for (i = 1, j = 0; i < len; i++) {
922 char fbuf[32];
923
924 if (!(add_mask & (1UL << (i - 1))))
925 sprintf(fbuf, "0.0%%");
926 else {
927 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
928 j++;
929 }
930
931 gtk_list_store_set(model, &iter, i, fbuf, -1);
932 }
933
934}
935
936static void gfio_add_total_depths_tree(GtkListStore *model,
937 struct thread_stat *ts, unsigned int len)
938{
939 double io_u_dist[FIO_IO_U_MAP_NR];
940 GtkTreeIter iter;
941 /* Bits 1-6, and 8 */
942 const int add_mask = 0x17e;
943 int i, j;
944
945 stat_calc_dist(ts->io_u_map, ts_total_io_u(ts), io_u_dist);
946
947 gtk_list_store_append(model, &iter);
948
949 gtk_list_store_set(model, &iter, 0, "Total", -1);
950
951 for (i = 1, j = 0; i < len; i++) {
952 char fbuf[32];
953
954 if (!(add_mask & (1UL << (i - 1))))
955 sprintf(fbuf, "0.0%%");
956 else {
957 sprintf(fbuf, "%3.1f%%", io_u_dist[j]);
958 j++;
959 }
960
961 gtk_list_store_set(model, &iter, i, fbuf, -1);
962 }
963
964}
Jens Axboe2e331012012-03-05 22:07:54 +0100965
966static void gfio_show_io_depths(GtkWidget *vbox, struct thread_stat *ts)
967{
Jens Axboe2e331012012-03-05 22:07:54 +0100968 GtkWidget *frame, *box, *tree_view;
969 GtkTreeSelection *selection;
970 GtkListStore *model;
Jens Axboe2e331012012-03-05 22:07:54 +0100971 GType types[FIO_IO_U_MAP_NR + 1];
972 int i;
Jens Axboe19998db2012-03-06 09:17:59 +0100973#define NR_LABELS 10
974 const char *labels[NR_LABELS] = { "Depth", "0", "1", "2", "4", "8", "16", "32", "64", ">= 64" };
Jens Axboe2e331012012-03-05 22:07:54 +0100975
976 frame = gtk_frame_new("IO depths");
977 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
978
979 box = gtk_hbox_new(FALSE, 3);
980 gtk_container_add(GTK_CONTAINER(frame), box);
981
Jens Axboe19998db2012-03-06 09:17:59 +0100982 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100983 types[i] = G_TYPE_STRING;
984
Jens Axboe19998db2012-03-06 09:17:59 +0100985 model = gtk_list_store_newv(NR_LABELS, types);
Jens Axboe2e331012012-03-05 22:07:54 +0100986
987 tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
988 gtk_widget_set_can_focus(tree_view, FALSE);
989
Jens Axboe661f7412012-03-06 13:55:45 +0100990 g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
991 "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, NULL);
992
Jens Axboe2e331012012-03-05 22:07:54 +0100993 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
994 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
995
Jens Axboe19998db2012-03-06 09:17:59 +0100996 for (i = 0; i < NR_LABELS; i++)
Jens Axboe2e331012012-03-05 22:07:54 +0100997 tree_view_column(tree_view, i, labels[i], ALIGN_RIGHT | UNSORTABLE);
998
Jens Axboe19998db2012-03-06 09:17:59 +0100999 gfio_add_total_depths_tree(model, ts, NR_LABELS);
1000 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 1);
1001 gfio_add_sc_depths_tree(model, ts, NR_LABELS, 0);
Jens Axboe2e331012012-03-05 22:07:54 +01001002
1003 gtk_box_pack_start(GTK_BOX(box), tree_view, TRUE, FALSE, 3);
1004}
1005
Jens Axboef9d40b42012-03-06 09:52:49 +01001006static gboolean results_window_delete(GtkWidget *w, gpointer data)
1007{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001008 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboef9d40b42012-03-06 09:52:49 +01001009
1010 gtk_widget_destroy(w);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001011 ge->results_window = NULL;
1012 ge->results_notebook = NULL;
Jens Axboef9d40b42012-03-06 09:52:49 +01001013 return TRUE;
1014}
1015
Jens Axboe17b97212012-03-14 20:17:57 +01001016static void results_close(GtkWidget *w, gpointer *data)
1017{
1018 struct gui_entry *ge = (struct gui_entry *) data;
1019
1020 gtk_widget_destroy(ge->results_window);
1021}
1022
1023static GtkActionEntry results_menu_items[] = {
1024 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
1025 { "GraphMenuAction", GTK_STOCK_FILE, "Graph", NULL, NULL, NULL},
1026 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(results_close) },
1027};
1028static gint results_nmenu_items = sizeof(results_menu_items) / sizeof(results_menu_items[0]);
1029
1030static const gchar *results_ui_string = " \
1031 <ui> \
1032 <menubar name=\"MainMenu\"> \
1033 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
1034 <menuitem name=\"Close\" action=\"CloseFile\" /> \
1035 </menu> \
1036 <menu name=\"GraphMenu\" action=\"GraphMenuAction\"> \
1037 </menu>\
1038 </menubar> \
1039 </ui> \
1040";
1041
1042static GtkWidget *get_results_menubar(GtkWidget *window, struct gui_entry *ge)
1043{
1044 GtkActionGroup *action_group;
1045 GtkWidget *widget;
1046 GError *error = 0;
1047
1048 ge->results_uimanager = gtk_ui_manager_new();
1049
1050 action_group = gtk_action_group_new("ResultsMenu");
1051 gtk_action_group_add_actions(action_group, results_menu_items, results_nmenu_items, ge);
1052
1053 gtk_ui_manager_insert_action_group(ge->results_uimanager, action_group, 0);
1054 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ge->results_uimanager), results_ui_string, -1, &error);
1055
1056 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ge->results_uimanager));
1057
1058 widget = gtk_ui_manager_get_widget(ge->results_uimanager, "/MainMenu");
1059 return widget;
1060}
1061
Jens Axboe2f99deb2012-03-09 14:37:29 +01001062static GtkWidget *get_results_window(struct gui_entry *ge)
Jens Axboef9d40b42012-03-06 09:52:49 +01001063{
Jens Axboe17b97212012-03-14 20:17:57 +01001064 GtkWidget *win, *notebook, *vbox;
Jens Axboef9d40b42012-03-06 09:52:49 +01001065
Jens Axboe2f99deb2012-03-09 14:37:29 +01001066 if (ge->results_window)
1067 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001068
1069 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1070 gtk_window_set_title(GTK_WINDOW(win), "Results");
Jens Axboeb01329d2012-03-07 20:31:28 +01001071 gtk_window_set_default_size(GTK_WINDOW(win), 1024, 768);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001072 g_signal_connect(win, "delete-event", G_CALLBACK(results_window_delete), ge);
1073 g_signal_connect(win, "destroy", G_CALLBACK(results_window_delete), ge);
Jens Axboef9d40b42012-03-06 09:52:49 +01001074
Jens Axboe17b97212012-03-14 20:17:57 +01001075 vbox = gtk_vbox_new(FALSE, 0);
1076 gtk_container_add(GTK_CONTAINER(win), vbox);
1077
1078 ge->results_menu = get_results_menubar(win, ge);
1079 gtk_box_pack_start(GTK_BOX(vbox), ge->results_menu, FALSE, FALSE, 0);
1080
Jens Axboef9d40b42012-03-06 09:52:49 +01001081 notebook = gtk_notebook_new();
Jens Axboe0aa928c2012-03-09 17:24:07 +01001082 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), 1);
1083 gtk_notebook_popup_enable(GTK_NOTEBOOK(notebook));
Jens Axboe17b97212012-03-14 20:17:57 +01001084 gtk_container_add(GTK_CONTAINER(vbox), notebook);
Jens Axboef9d40b42012-03-06 09:52:49 +01001085
Jens Axboe2f99deb2012-03-09 14:37:29 +01001086 ge->results_window = win;
1087 ge->results_notebook = notebook;
1088 return ge->results_notebook;
Jens Axboef9d40b42012-03-06 09:52:49 +01001089}
1090
Jens Axboe7da23b42012-03-15 13:45:02 +01001091static void disk_util_destroy(GtkWidget *w, gpointer data)
Jens Axboe3650a3c2012-03-05 14:09:03 +01001092{
Jens Axboe7da23b42012-03-15 13:45:02 +01001093 struct gui_entry *ge = (struct gui_entry *) data;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001094
Jens Axboe7da23b42012-03-15 13:45:02 +01001095 ge->disk_util_vbox = NULL;
1096 gtk_widget_destroy(w);
Jens Axboe781ccba2012-03-15 09:44:42 +01001097}
Jens Axboe3650a3c2012-03-05 14:09:03 +01001098
Jens Axboef0602d72012-03-15 15:38:02 +01001099static GtkWidget *get_scrolled_window(gint border_width)
1100{
1101 GtkWidget *scroll;
1102
1103 scroll = gtk_scrolled_window_new(NULL, NULL);
1104 gtk_container_set_border_width(GTK_CONTAINER(scroll), border_width);
1105 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1106
1107 return scroll;
1108}
1109
1110static GtkWidget *gfio_disk_util_get_vbox(struct gui_entry *ge)
1111{
1112 GtkWidget *vbox, *box, *scroll, *res_notebook;
1113
1114 if (ge->disk_util_vbox)
1115 return ge->disk_util_vbox;
1116
1117 scroll = get_scrolled_window(5);
1118 vbox = gtk_vbox_new(FALSE, 3);
1119 box = gtk_hbox_new(FALSE, 0);
1120 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1121
1122 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1123 res_notebook = get_results_window(ge);
1124
1125 gtk_notebook_append_page(GTK_NOTEBOOK(res_notebook), scroll, gtk_label_new("Disk utilization"));
1126 ge->disk_util_vbox = box;
1127 g_signal_connect(vbox, "destroy", G_CALLBACK(disk_util_destroy), ge);
1128
1129 return ge->disk_util_vbox;
1130}
1131
Jens Axboe7da23b42012-03-15 13:45:02 +01001132static int __gfio_disk_util_show(GtkWidget *res_notebook,
1133 struct gfio_client *gc, struct cmd_du_pdu *p)
Jens Axboe781ccba2012-03-15 09:44:42 +01001134{
Jens Axboef0602d72012-03-15 15:38:02 +01001135 GtkWidget *box, *frame, *entry, *vbox, *util_vbox;
Jens Axboe7da23b42012-03-15 13:45:02 +01001136 struct gui_entry *ge = gc->ge;
Jens Axboe604cfe32012-03-07 19:51:36 +01001137 double util;
1138 char tmp[16];
Jens Axboee0681f32012-03-06 12:14:42 +01001139
Jens Axboef0602d72012-03-15 15:38:02 +01001140 util_vbox = gfio_disk_util_get_vbox(ge);
Jens Axboee0681f32012-03-06 12:14:42 +01001141
1142 vbox = gtk_vbox_new(FALSE, 3);
Jens Axboef0602d72012-03-15 15:38:02 +01001143 gtk_container_add(GTK_CONTAINER(util_vbox), vbox);
Jens Axboee0681f32012-03-06 12:14:42 +01001144
1145 frame = gtk_frame_new((char *) p->dus.name);
1146 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 2);
1147
1148 box = gtk_vbox_new(FALSE, 3);
1149 gtk_container_add(GTK_CONTAINER(frame), box);
1150
1151 frame = gtk_frame_new("Read");
1152 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1153 vbox = gtk_hbox_new(TRUE, 3);
1154 gtk_container_add(GTK_CONTAINER(frame), vbox);
1155 entry = new_info_entry_in_frame(vbox, "IOs");
1156 entry_set_int_value(entry, p->dus.ios[0]);
1157 entry = new_info_entry_in_frame(vbox, "Merges");
1158 entry_set_int_value(entry, p->dus.merges[0]);
1159 entry = new_info_entry_in_frame(vbox, "Sectors");
1160 entry_set_int_value(entry, p->dus.sectors[0]);
1161 entry = new_info_entry_in_frame(vbox, "Ticks");
1162 entry_set_int_value(entry, p->dus.ticks[0]);
1163
1164 frame = gtk_frame_new("Write");
1165 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1166 vbox = gtk_hbox_new(TRUE, 3);
1167 gtk_container_add(GTK_CONTAINER(frame), vbox);
1168 entry = new_info_entry_in_frame(vbox, "IOs");
1169 entry_set_int_value(entry, p->dus.ios[1]);
1170 entry = new_info_entry_in_frame(vbox, "Merges");
1171 entry_set_int_value(entry, p->dus.merges[1]);
1172 entry = new_info_entry_in_frame(vbox, "Sectors");
1173 entry_set_int_value(entry, p->dus.sectors[1]);
1174 entry = new_info_entry_in_frame(vbox, "Ticks");
1175 entry_set_int_value(entry, p->dus.ticks[1]);
1176
1177 frame = gtk_frame_new("Shared");
1178 gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 2);
1179 vbox = gtk_hbox_new(TRUE, 3);
1180 gtk_container_add(GTK_CONTAINER(frame), vbox);
1181 entry = new_info_entry_in_frame(vbox, "IO ticks");
1182 entry_set_int_value(entry, p->dus.io_ticks);
1183 entry = new_info_entry_in_frame(vbox, "Time in queue");
1184 entry_set_int_value(entry, p->dus.time_in_queue);
1185
Jens Axboe604cfe32012-03-07 19:51:36 +01001186 util = 0.0;
1187 if (p->dus.msec)
1188 util = (double) 100 * p->dus.io_ticks / (double) p->dus.msec;
1189 if (util > 100.0)
1190 util = 100.0;
1191
1192 sprintf(tmp, "%3.2f%%", util);
1193 entry = new_info_entry_in_frame(vbox, "Disk utilization");
1194 gtk_entry_set_text(GTK_ENTRY(entry), tmp);
1195
Jens Axboe7da23b42012-03-15 13:45:02 +01001196 gtk_widget_show_all(ge->results_window);
1197 return 0;
1198}
1199
1200static int gfio_disk_util_show(struct gfio_client *gc)
1201{
1202 struct gui_entry *ge = gc->ge;
1203 GtkWidget *res_notebook;
1204 int i;
1205
1206 if (!gc->nr_du)
1207 return 1;
1208
1209 res_notebook = get_results_window(ge);
1210
1211 for (i = 0; i < gc->nr_du; i++) {
1212 struct cmd_du_pdu *p = &gc->du[i];
1213
1214 __gfio_disk_util_show(res_notebook, gc, p);
1215 }
1216
1217 gtk_widget_show_all(ge->results_window);
1218 return 0;
1219}
1220
1221static void gfio_add_end_results(struct gfio_client *gc, struct thread_stat *ts,
1222 struct group_run_stats *rs)
1223{
1224 unsigned int nr = gc->nr_results;
1225
1226 gc->results = realloc(gc->results, (nr + 1) * sizeof(struct end_results));
1227 memcpy(&gc->results[nr].ts, ts, sizeof(*ts));
1228 memcpy(&gc->results[nr].gs, rs, sizeof(*rs));
1229 gc->nr_results++;
1230}
1231
1232static void __gfio_display_end_results(GtkWidget *win, struct gfio_client *gc,
1233 struct thread_stat *ts,
1234 struct group_run_stats *rs)
1235{
1236 GtkWidget *box, *vbox, *entry, *scroll;
1237
1238 scroll = gtk_scrolled_window_new(NULL, NULL);
1239 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
1240 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1241
1242 vbox = gtk_vbox_new(FALSE, 3);
1243
1244 box = gtk_hbox_new(FALSE, 0);
1245 gtk_box_pack_start(GTK_BOX(vbox), box, TRUE, FALSE, 5);
1246
1247 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), vbox);
1248
1249 gtk_notebook_append_page(GTK_NOTEBOOK(win), scroll, gtk_label_new(ts->name));
1250
1251 entry = new_info_entry_in_frame(box, "Name");
1252 gtk_entry_set_text(GTK_ENTRY(entry), ts->name);
1253 if (strlen(ts->description)) {
1254 entry = new_info_entry_in_frame(box, "Description");
1255 gtk_entry_set_text(GTK_ENTRY(entry), ts->description);
1256 }
1257 entry = new_info_entry_in_frame(box, "Group ID");
1258 entry_set_int_value(entry, ts->groupid);
1259 entry = new_info_entry_in_frame(box, "Jobs");
1260 entry_set_int_value(entry, ts->members);
1261 gc->err_entry = entry = new_info_entry_in_frame(box, "Error");
1262 entry_set_int_value(entry, ts->error);
1263 entry = new_info_entry_in_frame(box, "PID");
1264 entry_set_int_value(entry, ts->pid);
1265
1266 if (ts->io_bytes[DDIR_READ])
1267 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_READ);
1268 if (ts->io_bytes[DDIR_WRITE])
1269 gfio_show_ddir_status(gc, vbox, rs, ts, DDIR_WRITE);
1270
1271 gfio_show_latency_buckets(gc, vbox, ts);
1272 gfio_show_cpu_usage(vbox, ts);
1273 gfio_show_io_depths(vbox, ts);
1274}
1275
1276static void gfio_display_end_results(struct gfio_client *gc)
1277{
1278 struct gui_entry *ge = gc->ge;
1279 GtkWidget *res_notebook;
1280 int i;
1281
1282 res_notebook = get_results_window(ge);
1283
1284 for (i = 0; i < gc->nr_results; i++) {
1285 struct end_results *e = &gc->results[i];
1286
1287 __gfio_display_end_results(res_notebook, gc, &e->ts, &e->gs);
1288 }
1289
1290 if (gfio_disk_util_show(gc))
1291 gtk_widget_show_all(ge->results_window);
1292}
1293
1294static void gfio_display_ts(struct fio_client *client, struct thread_stat *ts,
1295 struct group_run_stats *rs)
1296{
1297 struct gfio_client *gc = client->client_data;
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001298 struct gui_entry *ge = gc->ge;
Jens Axboe7da23b42012-03-15 13:45:02 +01001299
1300 gfio_add_end_results(gc, ts, rs);
1301
1302 gdk_threads_enter();
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001303 if (ge->results_window)
1304 __gfio_display_end_results(ge->results_notebook, gc, ts, rs);
1305 else
1306 gfio_display_end_results(gc);
Jens Axboe7da23b42012-03-15 13:45:02 +01001307 gdk_threads_leave();
1308}
1309
1310static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
1311{
1312 struct cmd_text_pdu *p = (struct cmd_text_pdu *) cmd->payload;
1313 struct gui *ui = &main_ui;
1314 GtkTreeIter iter;
1315 struct tm *tm;
1316 time_t sec;
1317 char tmp[64], timebuf[80];
1318
1319 sec = p->log_sec;
1320 tm = localtime(&sec);
1321 strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", tm);
1322 sprintf(timebuf, "%s.%03ld", tmp, p->log_usec / 1000);
1323
1324 gdk_threads_enter();
1325
1326 gtk_list_store_append(ui->log_model, &iter);
1327 gtk_list_store_set(ui->log_model, &iter, 0, timebuf, -1);
1328 gtk_list_store_set(ui->log_model, &iter, 1, client->hostname, -1);
1329 gtk_list_store_set(ui->log_model, &iter, 2, p->level, -1);
1330 gtk_list_store_set(ui->log_model, &iter, 3, p->buf, -1);
1331
1332 if (p->level == FIO_LOG_ERR)
1333 view_log(NULL, (gpointer) ui);
1334
1335 gdk_threads_leave();
1336}
1337
1338static void gfio_disk_util_op(struct fio_client *client, struct fio_net_cmd *cmd)
1339{
1340 struct cmd_du_pdu *p = (struct cmd_du_pdu *) cmd->payload;
1341 struct gfio_client *gc = client->client_data;
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001342 struct gui_entry *ge = gc->ge;
Jens Axboe7da23b42012-03-15 13:45:02 +01001343 unsigned int nr = gc->nr_du;
1344
1345 gc->du = realloc(gc->du, (nr + 1) * sizeof(struct cmd_du_pdu));
1346 memcpy(&gc->du[nr], p, sizeof(*p));
1347 gc->nr_du++;
1348
1349 gdk_threads_enter();
Jens Axboe0ed83bc2012-03-15 14:36:39 +01001350 if (ge->results_window)
1351 __gfio_disk_util_show(ge->results_notebook, gc, p);
1352 else
1353 gfio_disk_util_show(gc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001354 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001355}
1356
Jens Axboe3650a3c2012-03-05 14:09:03 +01001357extern int sum_stat_clients;
1358extern struct thread_stat client_ts;
1359extern struct group_run_stats client_gs;
1360
1361static int sum_stat_nr;
1362
Jens Axboe89e5fad2012-03-05 09:21:12 +01001363static void gfio_thread_status_op(struct fio_client *client,
1364 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001365{
Jens Axboe3650a3c2012-03-05 14:09:03 +01001366 struct cmd_ts_pdu *p = (struct cmd_ts_pdu *) cmd->payload;
1367
1368 gfio_display_ts(client, &p->ts, &p->rs);
1369
1370 if (sum_stat_clients == 1)
1371 return;
1372
1373 sum_thread_stats(&client_ts, &p->ts, sum_stat_nr);
1374 sum_group_stats(&client_gs, &p->rs);
1375
1376 client_ts.members++;
Jens Axboe2f122b12012-03-15 13:10:19 +01001377 client_ts.thread_number = p->ts.thread_number;
Jens Axboe3650a3c2012-03-05 14:09:03 +01001378 client_ts.groupid = p->ts.groupid;
1379
1380 if (++sum_stat_nr == sum_stat_clients) {
1381 strcpy(client_ts.name, "All clients");
1382 gfio_display_ts(client, &client_ts, &client_gs);
1383 }
Stephen M. Camerona1820202012-02-24 08:17:31 +01001384}
1385
Jens Axboe89e5fad2012-03-05 09:21:12 +01001386static void gfio_group_stats_op(struct fio_client *client,
1387 struct fio_net_cmd *cmd)
Stephen M. Camerona1820202012-02-24 08:17:31 +01001388{
Jens Axboe98ceabd2012-03-09 08:53:28 +01001389 /* We're ignoring group stats for now */
Stephen M. Camerona1820202012-02-24 08:17:31 +01001390}
1391
Jens Axboe2f99deb2012-03-09 14:37:29 +01001392static gint on_config_drawing_area(GtkWidget *w, GdkEventConfigure *event,
1393 gpointer data)
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001394{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001395 struct gfio_graphs *g = data;
1396
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001397 graph_set_size(g->iops_graph, w->allocation.width / 2.0, w->allocation.height);
1398 graph_set_position(g->iops_graph, w->allocation.width / 2.0, 0.0);
1399 graph_set_size(g->bandwidth_graph, w->allocation.width / 2.0, w->allocation.height);
1400 graph_set_position(g->bandwidth_graph, 0, 0);
Stephen M. Cameron3ea48b82012-03-07 19:40:58 +01001401 return TRUE;
1402}
1403
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001404static void draw_graph(struct graph *g, cairo_t *cr)
1405{
1406 line_graph_draw(g, cr);
1407 cairo_stroke(cr);
1408}
1409
Jens Axboe93e2db22012-03-13 09:45:22 +01001410static gboolean graph_tooltip(GtkWidget *w, gint x, gint y,
1411 gboolean keyboard_mode, GtkTooltip *tooltip,
1412 gpointer data)
1413{
1414 struct gfio_graphs *g = data;
1415 const char *text = NULL;
1416
1417 if (graph_contains_xy(g->iops_graph, x, y))
1418 text = graph_find_tooltip(g->iops_graph, x, y);
1419 else if (graph_contains_xy(g->bandwidth_graph, x, y))
1420 text = graph_find_tooltip(g->bandwidth_graph, x, y);
1421
1422 if (text) {
1423 gtk_tooltip_set_text(tooltip, text);
1424 return TRUE;
1425 }
1426
1427 return FALSE;
1428}
1429
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001430static int on_expose_drawing_area(GtkWidget *w, GdkEvent *event, gpointer p)
1431{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001432 struct gfio_graphs *g = p;
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001433 cairo_t *cr;
1434
1435 cr = gdk_cairo_create(w->window);
Jens Axboe93e2db22012-03-13 09:45:22 +01001436
1437 if (graph_has_tooltips(g->iops_graph) ||
1438 graph_has_tooltips(g->bandwidth_graph)) {
1439 g_object_set(w, "has-tooltip", TRUE, NULL);
1440 g_signal_connect(w, "query-tooltip", G_CALLBACK(graph_tooltip), g);
1441 }
1442
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001443 cairo_set_source_rgb(cr, 0, 0, 0);
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01001444 draw_graph(g->iops_graph, cr);
1445 draw_graph(g->bandwidth_graph, cr);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01001446 cairo_destroy(cr);
1447
1448 return FALSE;
1449}
1450
Jens Axboe2f99deb2012-03-09 14:37:29 +01001451/*
1452 * Client specific ETA
1453 */
1454static void gfio_update_client_eta(struct fio_client *client, struct jobs_eta *je)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001455{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001456 struct gfio_client *gc = client->client_data;
1457 struct gui_entry *ge = gc->ge;
Jens Axboe3e47bd22012-02-29 13:45:02 +01001458 static int eta_good;
1459 char eta_str[128];
1460 char output[256];
1461 char tmp[32];
1462 double perc = 0.0;
1463 int i2p = 0;
1464
Jens Axboe0050e5f2012-03-06 09:23:27 +01001465 gdk_threads_enter();
1466
Jens Axboe3e47bd22012-02-29 13:45:02 +01001467 eta_str[0] = '\0';
1468 output[0] = '\0';
1469
1470 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1471 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1472 eta_to_str(eta_str, je->eta_sec);
1473 }
1474
1475 sprintf(tmp, "%u", je->nr_running);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001476 gtk_entry_set_text(GTK_ENTRY(ge->eta.jobs), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001477 sprintf(tmp, "%u", je->files_open);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001478 gtk_entry_set_text(GTK_ENTRY(ge->eta.files), tmp);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001479
1480#if 0
1481 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1482 if (je->m_rate || je->t_rate) {
1483 char *tr, *mr;
1484
1485 mr = num2str(je->m_rate, 4, 0, i2p);
1486 tr = num2str(je->t_rate, 4, 0, i2p);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001487 gtk_entry_set_text(GTK_ENTRY(ge->eta);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001488 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1489 free(tr);
1490 free(mr);
1491 } else if (je->m_iops || je->t_iops)
1492 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
Jens Axboeebbd89c2012-03-02 11:21:13 +01001493
Jens Axboe2f99deb2012-03-09 14:37:29 +01001494 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_bw), "---");
1495 gtk_entry_set_text(GTK_ENTRY(ge->eta.cr_iops), "---");
1496 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_bw), "---");
1497 gtk_entry_set_text(GTK_ENTRY(ge->eta.cw_iops), "---");
Jens Axboe3e47bd22012-02-29 13:45:02 +01001498#endif
1499
1500 if (je->eta_sec != INT_MAX && je->nr_running) {
1501 char *iops_str[2];
1502 char *rate_str[2];
1503
1504 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1505 strcpy(output, "-.-% done");
1506 else {
1507 eta_good = 1;
1508 perc *= 100.0;
1509 sprintf(output, "%3.1f%% done", perc);
1510 }
1511
1512 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1513 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1514
1515 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1516 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1517
Jens Axboe2f99deb2012-03-09 14:37:29 +01001518 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_bw), rate_str[0]);
1519 gtk_entry_set_text(GTK_ENTRY(ge->eta.read_iops), iops_str[0]);
1520 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_bw), rate_str[1]);
1521 gtk_entry_set_text(GTK_ENTRY(ge->eta.write_iops), iops_str[1]);
Jens Axboe3e47bd22012-02-29 13:45:02 +01001522
Jens Axboe93e2db22012-03-13 09:45:22 +01001523 graph_add_xy_data(ge->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1524 graph_add_xy_data(ge->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1525 graph_add_xy_data(ge->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1526 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 +01001527
1528 free(rate_str[0]);
1529 free(rate_str[1]);
1530 free(iops_str[0]);
1531 free(iops_str[1]);
1532 }
1533
1534 if (eta_str[0]) {
1535 char *dst = output + strlen(output);
1536
1537 sprintf(dst, " - %s", eta_str);
1538 }
1539
Jens Axboe9988ca72012-03-09 15:14:06 +01001540 gfio_update_thread_status(ge, output, perc);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001541 gdk_threads_leave();
1542}
1543
1544/*
1545 * Update ETA in main window for all clients
1546 */
1547static void gfio_update_all_eta(struct jobs_eta *je)
1548{
1549 struct gui *ui = &main_ui;
1550 static int eta_good;
1551 char eta_str[128];
1552 char output[256];
1553 double perc = 0.0;
1554 int i2p = 0;
1555
1556 gdk_threads_enter();
1557
1558 eta_str[0] = '\0';
1559 output[0] = '\0';
1560
1561 if (je->eta_sec != INT_MAX && je->elapsed_sec) {
1562 perc = (double) je->elapsed_sec / (double) (je->elapsed_sec + je->eta_sec);
1563 eta_to_str(eta_str, je->eta_sec);
1564 }
1565
1566#if 0
1567 if (je->m_rate[0] || je->m_rate[1] || je->t_rate[0] || je->t_rate[1]) {
1568 if (je->m_rate || je->t_rate) {
1569 char *tr, *mr;
1570
1571 mr = num2str(je->m_rate, 4, 0, i2p);
1572 tr = num2str(je->t_rate, 4, 0, i2p);
1573 gtk_entry_set_text(GTK_ENTRY(ui->eta);
1574 p += sprintf(p, ", CR=%s/%s KB/s", tr, mr);
1575 free(tr);
1576 free(mr);
1577 } else if (je->m_iops || je->t_iops)
1578 p += sprintf(p, ", CR=%d/%d IOPS", je->t_iops, je->m_iops);
1579
1580 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_bw), "---");
1581 gtk_entry_set_text(GTK_ENTRY(ui->eta.cr_iops), "---");
1582 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_bw), "---");
1583 gtk_entry_set_text(GTK_ENTRY(ui->eta.cw_iops), "---");
1584#endif
1585
Jens Axboe3863d1a2012-03-09 17:39:05 +01001586 entry_set_int_value(ui->eta.jobs, je->nr_running);
1587
Jens Axboe2f99deb2012-03-09 14:37:29 +01001588 if (je->eta_sec != INT_MAX && je->nr_running) {
1589 char *iops_str[2];
1590 char *rate_str[2];
1591
1592 if ((!je->eta_sec && !eta_good) || je->nr_ramp == je->nr_running)
1593 strcpy(output, "-.-% done");
1594 else {
1595 eta_good = 1;
1596 perc *= 100.0;
1597 sprintf(output, "%3.1f%% done", perc);
1598 }
1599
1600 rate_str[0] = num2str(je->rate[0], 5, 10, i2p);
1601 rate_str[1] = num2str(je->rate[1], 5, 10, i2p);
1602
1603 iops_str[0] = num2str(je->iops[0], 4, 1, 0);
1604 iops_str[1] = num2str(je->iops[1], 4, 1, 0);
1605
1606 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_bw), rate_str[0]);
1607 gtk_entry_set_text(GTK_ENTRY(ui->eta.read_iops), iops_str[0]);
1608 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_bw), rate_str[1]);
1609 gtk_entry_set_text(GTK_ENTRY(ui->eta.write_iops), iops_str[1]);
1610
Jens Axboe93e2db22012-03-13 09:45:22 +01001611 graph_add_xy_data(ui->graphs.iops_graph, "Read IOPS", je->elapsed_sec, je->iops[0], iops_str[0]);
1612 graph_add_xy_data(ui->graphs.iops_graph, "Write IOPS", je->elapsed_sec, je->iops[1], iops_str[1]);
1613 graph_add_xy_data(ui->graphs.bandwidth_graph, "Read Bandwidth", je->elapsed_sec, je->rate[0], rate_str[0]);
1614 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 +01001615
Jens Axboe3e47bd22012-02-29 13:45:02 +01001616 free(rate_str[0]);
1617 free(rate_str[1]);
1618 free(iops_str[0]);
1619 free(iops_str[1]);
1620 }
1621
1622 if (eta_str[0]) {
1623 char *dst = output + strlen(output);
1624
1625 sprintf(dst, " - %s", eta_str);
1626 }
1627
Jens Axboe9988ca72012-03-09 15:14:06 +01001628 gfio_update_thread_status_all(output, perc);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001629 gdk_threads_leave();
Jens Axboe3e47bd22012-02-29 13:45:02 +01001630}
1631
Stephen M. Camerona1820202012-02-24 08:17:31 +01001632static void gfio_probe_op(struct fio_client *client, struct fio_net_cmd *cmd)
1633{
Jens Axboe843ad232012-02-29 11:44:53 +01001634 struct cmd_probe_pdu *probe = (struct cmd_probe_pdu *) cmd->payload;
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001635 struct gfio_client *gc = client->client_data;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001636 struct gui_entry *ge = gc->ge;
Jens Axboe843ad232012-02-29 11:44:53 +01001637 const char *os, *arch;
1638 char buf[64];
1639
1640 os = fio_get_os_string(probe->os);
1641 if (!os)
1642 os = "unknown";
1643
1644 arch = fio_get_arch_string(probe->arch);
1645 if (!arch)
1646 os = "unknown";
1647
1648 if (!client->name)
1649 client->name = strdup((char *) probe->hostname);
1650
Jens Axboe0050e5f2012-03-06 09:23:27 +01001651 gdk_threads_enter();
1652
Jens Axboe2f99deb2012-03-09 14:37:29 +01001653 gtk_label_set_text(GTK_LABEL(ge->probe.hostname), (char *) probe->hostname);
1654 gtk_label_set_text(GTK_LABEL(ge->probe.os), os);
1655 gtk_label_set_text(GTK_LABEL(ge->probe.arch), arch);
Jens Axboe843ad232012-02-29 11:44:53 +01001656 sprintf(buf, "%u.%u.%u", probe->fio_major, probe->fio_minor, probe->fio_patch);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001657 gtk_label_set_text(GTK_LABEL(ge->probe.fio_ver), buf);
Jens Axboe88f6e7a2012-03-06 12:55:29 +01001658
Jens Axboe85dd01e2012-03-12 14:33:16 +01001659 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001660
1661 gdk_threads_leave();
Stephen M. Camerona1820202012-02-24 08:17:31 +01001662}
1663
Jens Axboe9988ca72012-03-09 15:14:06 +01001664static void gfio_update_thread_status(struct gui_entry *ge,
1665 char *status_message, double perc)
1666{
1667 static char message[100];
1668 const char *m = message;
1669
1670 strncpy(message, status_message, sizeof(message) - 1);
1671 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), m);
1672 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), perc / 100.0);
1673 gtk_widget_queue_draw(main_ui.window);
1674}
1675
1676static void gfio_update_thread_status_all(char *status_message, double perc)
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001677{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001678 struct gui *ui = &main_ui;
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001679 static char message[100];
1680 const char *m = message;
1681
1682 strncpy(message, status_message, sizeof(message) - 1);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001683 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), m);
1684 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), perc / 100.0);
1685 gtk_widget_queue_draw(ui->window);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01001686}
1687
Jens Axboe35c0ba72012-03-14 10:56:40 +01001688static void gfio_quit_op(struct fio_client *client, struct fio_net_cmd *cmd)
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001689{
Jens Axboee0681f32012-03-06 12:14:42 +01001690 struct gfio_client *gc = client->client_data;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001691
Jens Axboe0050e5f2012-03-06 09:23:27 +01001692 gdk_threads_enter();
Jens Axboe85dd01e2012-03-12 14:33:16 +01001693 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001694 gdk_threads_leave();
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001695}
1696
Jens Axboe807f9972012-03-02 10:25:24 +01001697static void gfio_add_job_op(struct fio_client *client, struct fio_net_cmd *cmd)
1698{
1699 struct cmd_add_job_pdu *p = (struct cmd_add_job_pdu *) cmd->payload;
Jens Axboee0681f32012-03-06 12:14:42 +01001700 struct gfio_client *gc = client->client_data;
Jens Axboedcaeb602012-03-08 19:45:37 +01001701 struct thread_options *o = &gc->o;
Jens Axboe2f99deb2012-03-09 14:37:29 +01001702 struct gui_entry *ge = gc->ge;
Jens Axboe99d633a2012-03-15 15:55:04 +01001703 char *c1, *c2, *c3, *c4;
1704 char tmp[80];
Jens Axboe807f9972012-03-02 10:25:24 +01001705
Jens Axboe2f122b12012-03-15 13:10:19 +01001706 p->thread_number = le32_to_cpu(p->thread_number);
1707 p->groupid = le32_to_cpu(p->groupid);
Jens Axboedcaeb602012-03-08 19:45:37 +01001708 convert_thread_options_to_cpu(o, &p->top);
Jens Axboe807f9972012-03-02 10:25:24 +01001709
Jens Axboe0050e5f2012-03-06 09:23:27 +01001710 gdk_threads_enter();
1711
Jens Axboe2f99deb2012-03-09 14:37:29 +01001712 gtk_label_set_text(GTK_LABEL(ge->page_label), (gchar *) o->name);
1713
Jens Axboe3863d1a2012-03-09 17:39:05 +01001714 gtk_combo_box_append_text(GTK_COMBO_BOX(ge->eta.names), (gchar *) o->name);
1715 gtk_combo_box_set_active(GTK_COMBO_BOX(ge->eta.names), 0);
1716
Jens Axboeddbafc12012-03-15 18:57:03 +01001717 sprintf(tmp, "%s %s", o->odirect ? "direct" : "buffered", ddir_str(o->td_ddir));
1718 multitext_add_entry(&ge->eta.iotype, tmp);
Jens Axboe99d633a2012-03-15 15:55:04 +01001719
1720 c1 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1721 c2 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1722 c3 = fio_uint_to_kmg(o->min_bs[DDIR_READ]);
1723 c4 = fio_uint_to_kmg(o->max_bs[DDIR_WRITE]);
1724 sprintf(tmp, "%s-%s/%s-%s", c1, c2, c3, c4);
1725 free(c1);
1726 free(c2);
1727 free(c3);
1728 free(c4);
1729 multitext_add_entry(&ge->eta.bs, tmp);
1730
Jens Axboec80b74b2012-03-12 10:23:28 +01001731 multitext_add_entry(&ge->eta.ioengine, (const char *) o->ioengine);
Jens Axboe807f9972012-03-02 10:25:24 +01001732
Jens Axboedcaeb602012-03-08 19:45:37 +01001733 sprintf(tmp, "%u", o->iodepth);
Jens Axboec80b74b2012-03-12 10:23:28 +01001734 multitext_add_entry(&ge->eta.iodepth, tmp);
1735
1736 multitext_set_entry(&ge->eta.iotype, 0);
Jens Axboe99d633a2012-03-15 15:55:04 +01001737 multitext_set_entry(&ge->eta.bs, 0);
Jens Axboec80b74b2012-03-12 10:23:28 +01001738 multitext_set_entry(&ge->eta.ioengine, 0);
1739 multitext_set_entry(&ge->eta.iodepth, 0);
Jens Axboe0050e5f2012-03-06 09:23:27 +01001740
Jens Axboe85dd01e2012-03-12 14:33:16 +01001741 gfio_set_state(ge, GE_STATE_JOB_SENT);
1742
Jens Axboe0050e5f2012-03-06 09:23:27 +01001743 gdk_threads_leave();
Jens Axboe807f9972012-03-02 10:25:24 +01001744}
1745
Jens Axboeed727a42012-03-02 12:14:40 +01001746static void gfio_client_timed_out(struct fio_client *client)
1747{
Jens Axboee0681f32012-03-06 12:14:42 +01001748 struct gfio_client *gc = client->client_data;
Jens Axboeed727a42012-03-02 12:14:40 +01001749 char buf[256];
1750
1751 gdk_threads_enter();
1752
Jens Axboe85dd01e2012-03-12 14:33:16 +01001753 gfio_set_state(gc->ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001754 clear_ge_ui_info(gc->ge);
Jens Axboeed727a42012-03-02 12:14:40 +01001755
1756 sprintf(buf, "Client %s: timeout talking to server.\n", client->hostname);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01001757 show_info_dialog(gc->ge->ui, "Network timeout", buf);
Jens Axboeed727a42012-03-02 12:14:40 +01001758
1759 gdk_threads_leave();
1760}
1761
Jens Axboe6b79c802012-03-08 10:51:36 +01001762static void gfio_client_stop(struct fio_client *client, struct fio_net_cmd *cmd)
1763{
1764 struct gfio_client *gc = client->client_data;
1765
1766 gdk_threads_enter();
1767
Jens Axboe85dd01e2012-03-12 14:33:16 +01001768 gfio_set_state(gc->ge, GE_STATE_JOB_DONE);
Jens Axboe6b79c802012-03-08 10:51:36 +01001769
1770 if (gc->err_entry)
1771 entry_set_int_value(gc->err_entry, client->error);
1772
1773 gdk_threads_leave();
1774}
1775
Jens Axboe85dd01e2012-03-12 14:33:16 +01001776static void gfio_client_start(struct fio_client *client, struct fio_net_cmd *cmd)
1777{
1778 struct gfio_client *gc = client->client_data;
1779
1780 gdk_threads_enter();
1781 gfio_set_state(gc->ge, GE_STATE_JOB_STARTED);
1782 gdk_threads_leave();
1783}
1784
1785static void gfio_client_job_start(struct fio_client *client, struct fio_net_cmd *cmd)
1786{
1787 struct gfio_client *gc = client->client_data;
1788
1789 gdk_threads_enter();
1790 gfio_set_state(gc->ge, GE_STATE_JOB_RUNNING);
1791 gdk_threads_leave();
1792}
1793
Jens Axboe1b427252012-03-14 15:03:03 +01001794static void gfio_client_iolog(struct fio_client *client, struct cmd_iolog_pdu *pdu)
1795{
Jens Axboe284b1e62012-03-14 21:55:07 +01001796 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 +01001797 free(pdu);
1798}
1799
Stephen M. Camerona1820202012-02-24 08:17:31 +01001800struct client_ops gfio_client_ops = {
Jens Axboe35c0ba72012-03-14 10:56:40 +01001801 .text = gfio_text_op,
Jens Axboe0420ba62012-02-29 11:16:52 +01001802 .disk_util = gfio_disk_util_op,
1803 .thread_status = gfio_thread_status_op,
1804 .group_stats = gfio_group_stats_op,
Jens Axboe2f99deb2012-03-09 14:37:29 +01001805 .jobs_eta = gfio_update_client_eta,
1806 .eta = gfio_update_all_eta,
Jens Axboe0420ba62012-02-29 11:16:52 +01001807 .probe = gfio_probe_op,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001808 .quit = gfio_quit_op,
Jens Axboe807f9972012-03-02 10:25:24 +01001809 .add_job = gfio_add_job_op,
Jens Axboeed727a42012-03-02 12:14:40 +01001810 .timed_out = gfio_client_timed_out,
Jens Axboe6b79c802012-03-08 10:51:36 +01001811 .stop = gfio_client_stop,
Jens Axboe85dd01e2012-03-12 14:33:16 +01001812 .start = gfio_client_start,
1813 .job_start = gfio_client_job_start,
Jens Axboe1b427252012-03-14 15:03:03 +01001814 .iolog = gfio_client_iolog,
Jens Axboe6433ee02012-03-09 20:10:51 +01001815 .eta_msec = FIO_CLIENT_DEF_ETA_MSEC,
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001816 .stay_connected = 1,
Jens Axboe46bcd492012-03-14 11:31:21 +01001817 .client_type = FIO_CLIENT_TYPE_GUI,
Stephen M. Camerona1820202012-02-24 08:17:31 +01001818};
1819
Jens Axboe0fd18982012-03-14 10:34:48 +01001820/*
1821 * FIXME: need more handling here
1822 */
1823static void ge_destroy(struct gui_entry *ge)
1824{
1825 struct gfio_client *gc = ge->client;
1826
1827 if (gc && gc->client) {
1828 if (ge->state >= GE_STATE_CONNECTED)
1829 fio_client_terminate(gc->client);
1830
1831 fio_put_client(gc->client);
1832 }
1833
1834 flist_del(&ge->list);
1835 free(ge);
1836}
1837
1838static void ge_widget_destroy(GtkWidget *w, gpointer data)
1839{
Jens Axboe0fd18982012-03-14 10:34:48 +01001840}
1841
1842static void gfio_quit(struct gui *ui)
1843{
1844 struct gui_entry *ge;
1845
1846 while (!flist_empty(&ui->list)) {
1847 ge = flist_entry(ui->list.next, struct gui_entry, list);
1848 ge_destroy(ge);
1849 }
1850
1851 gtk_main_quit();
1852}
1853
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001854static void quit_clicked(__attribute__((unused)) GtkWidget *widget,
1855 __attribute__((unused)) gpointer data)
1856{
Jens Axboe0fd18982012-03-14 10:34:48 +01001857 gfio_quit(data);
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01001858}
1859
Stephen M. Cameron25927252012-02-24 08:17:31 +01001860static void *job_thread(void *arg)
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001861{
Jens Axboea9eccde2012-03-09 14:59:42 +01001862 struct gui *ui = arg;
1863
1864 ui->handler_running = 1;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001865 fio_handle_clients(&gfio_client_ops);
Jens Axboea9eccde2012-03-09 14:59:42 +01001866 ui->handler_running = 0;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001867 return NULL;
1868}
1869
Jens Axboe2f99deb2012-03-09 14:37:29 +01001870static int send_job_files(struct gui_entry *ge)
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001871{
Jens Axboe9988ca72012-03-09 15:14:06 +01001872 struct gfio_client *gc = ge->client;
Jens Axboe441013b2012-03-01 08:01:52 +01001873 int i, ret = 0;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001874
Jens Axboe2f99deb2012-03-09 14:37:29 +01001875 for (i = 0; i < ge->nr_job_files; i++) {
Jens Axboe9988ca72012-03-09 15:14:06 +01001876 ret = fio_client_send_ini(gc->client, ge->job_files[i]);
Jens Axboec7249262012-03-09 17:11:04 +01001877 if (ret < 0) {
1878 GError *error;
1879
1880 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to send file %s: %s\n", ge->job_files[i], strerror(-ret));
1881 report_error(error);
1882 g_error_free(error);
1883 break;
1884 } else if (ret)
Jens Axboe441013b2012-03-01 08:01:52 +01001885 break;
1886
Jens Axboe2f99deb2012-03-09 14:37:29 +01001887 free(ge->job_files[i]);
1888 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001889 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01001890 while (i < ge->nr_job_files) {
1891 free(ge->job_files[i]);
1892 ge->job_files[i] = NULL;
Jens Axboe441013b2012-03-01 08:01:52 +01001893 i++;
Jens Axboe0420ba62012-02-29 11:16:52 +01001894 }
1895
Jens Axboe2c77d832012-03-13 19:02:04 +01001896 free(ge->job_files);
1897 ge->job_files = NULL;
Jens Axboe3af45202012-03-13 09:59:53 +01001898 ge->nr_job_files = 0;
Jens Axboe441013b2012-03-01 08:01:52 +01001899 return ret;
Stephen M. Cameron60f6b332012-02-24 08:17:32 +01001900}
1901
Jens Axboe63a130b2012-03-06 20:08:59 +01001902static void *server_thread(void *arg)
1903{
1904 is_backend = 1;
1905 gfio_server_running = 1;
1906 fio_start_server(NULL);
1907 gfio_server_running = 0;
1908 return NULL;
1909}
1910
Jens Axboe2f99deb2012-03-09 14:37:29 +01001911static void gfio_start_server(void)
Jens Axboe63a130b2012-03-06 20:08:59 +01001912{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001913 struct gui *ui = &main_ui;
1914
Jens Axboe63a130b2012-03-06 20:08:59 +01001915 if (!gfio_server_running) {
1916 gfio_server_running = 1;
1917 pthread_create(&ui->server_t, NULL, server_thread, NULL);
Jens Axboee34f6ad2012-03-06 20:47:15 +01001918 pthread_detach(ui->server_t);
Jens Axboe63a130b2012-03-06 20:08:59 +01001919 }
1920}
1921
Stephen M. Cameron25927252012-02-24 08:17:31 +01001922static void start_job_clicked(__attribute__((unused)) GtkWidget *widget,
1923 gpointer data)
1924{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001925 struct gui_entry *ge = data;
1926 struct gfio_client *gc = ge->client;
Stephen M. Cameron25927252012-02-24 08:17:31 +01001927
Jens Axboe78cb2fe2012-03-12 23:05:29 +01001928 if (gc)
1929 fio_start_client(gc->client);
Stephen M. Cameronf3074002012-02-24 08:17:30 +01001930}
1931
Jens Axboedf06f222012-03-02 13:32:04 +01001932static void file_open(GtkWidget *w, gpointer data);
1933
1934static void connect_clicked(GtkWidget *widget, gpointer data)
Jens Axboe3e47bd22012-02-29 13:45:02 +01001935{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001936 struct gui_entry *ge = data;
1937 struct gfio_client *gc = ge->client;
Jens Axboe3ec62ec2012-03-01 12:01:29 +01001938
Jens Axboe85dd01e2012-03-12 14:33:16 +01001939 if (ge->state == GE_STATE_NEW) {
Jens Axboec7249262012-03-09 17:11:04 +01001940 int ret;
1941
Jens Axboe2f99deb2012-03-09 14:37:29 +01001942 if (!ge->nr_job_files)
Jens Axboecf4b0442012-03-12 15:09:42 +01001943 file_open(widget, ge->ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001944 if (!ge->nr_job_files)
1945 return;
1946
Jens Axboed3b70f32012-03-15 14:05:01 +01001947 gc = ge->client;
1948
Jens Axboe2f99deb2012-03-09 14:37:29 +01001949 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No jobs running");
1950 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
Jens Axboec7249262012-03-09 17:11:04 +01001951 ret = fio_client_connect(gc->client);
1952 if (!ret) {
Jens Axboea9eccde2012-03-09 14:59:42 +01001953 if (!ge->ui->handler_running)
1954 pthread_create(&ge->ui->t, NULL, job_thread, ge->ui);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001955 gfio_set_state(ge, GE_STATE_CONNECTED);
Jens Axboec7249262012-03-09 17:11:04 +01001956 } else {
1957 GError *error;
1958
1959 error = g_error_new(g_quark_from_string("fio"), 1, "Failed to connect to %s: %s\n", ge->client->client->hostname, strerror(-ret));
1960 report_error(error);
1961 g_error_free(error);
Jens Axboe69406b92012-03-06 14:00:42 +01001962 }
Jens Axboedf06f222012-03-02 13:32:04 +01001963 } else {
Jens Axboe2f99deb2012-03-09 14:37:29 +01001964 fio_client_terminate(gc->client);
Jens Axboe85dd01e2012-03-12 14:33:16 +01001965 gfio_set_state(ge, GE_STATE_NEW);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001966 clear_ge_ui_info(ge);
Jens Axboedf06f222012-03-02 13:32:04 +01001967 }
Jens Axboe3e47bd22012-02-29 13:45:02 +01001968}
1969
Jens Axboeb9d2f302012-03-08 20:36:28 +01001970static void send_clicked(GtkWidget *widget, gpointer data)
1971{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001972 struct gui_entry *ge = data;
Jens Axboeb9d2f302012-03-08 20:36:28 +01001973
Jens Axboe2f99deb2012-03-09 14:37:29 +01001974 if (send_job_files(ge)) {
Jens Axboec7249262012-03-09 17:11:04 +01001975 GError *error;
1976
1977 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);
1978 report_error(error);
1979 g_error_free(error);
1980
Jens Axboe53e0e852012-03-15 19:38:01 +01001981 gtk_widget_set_sensitive(ge->button[GFIO_BUTTON_START], 1);
Jens Axboeb9d2f302012-03-08 20:36:28 +01001982 }
Jens Axboeb9d2f302012-03-08 20:36:28 +01001983}
1984
Jens Axboe0420ba62012-02-29 11:16:52 +01001985static void on_info_bar_response(GtkWidget *widget, gint response,
1986 gpointer data)
1987{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001988 struct gui *ui = &main_ui;
1989
Jens Axboe0420ba62012-02-29 11:16:52 +01001990 if (response == GTK_RESPONSE_OK) {
1991 gtk_widget_destroy(widget);
Jens Axboe2f99deb2012-03-09 14:37:29 +01001992 ui->error_info_bar = NULL;
Jens Axboe0420ba62012-02-29 11:16:52 +01001993 }
1994}
1995
Jens Axboedf06f222012-03-02 13:32:04 +01001996void report_error(GError *error)
Jens Axboe0420ba62012-02-29 11:16:52 +01001997{
Jens Axboe2f99deb2012-03-09 14:37:29 +01001998 struct gui *ui = &main_ui;
1999
2000 if (ui->error_info_bar == NULL) {
2001 ui->error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
Jens Axboe0420ba62012-02-29 11:16:52 +01002002 GTK_RESPONSE_OK,
2003 NULL);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002004 g_signal_connect(ui->error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
2005 gtk_info_bar_set_message_type(GTK_INFO_BAR(ui->error_info_bar),
Jens Axboe0420ba62012-02-29 11:16:52 +01002006 GTK_MESSAGE_ERROR);
2007
Jens Axboe2f99deb2012-03-09 14:37:29 +01002008 ui->error_label = gtk_label_new(error->message);
2009 GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(ui->error_info_bar));
2010 gtk_container_add(GTK_CONTAINER(container), ui->error_label);
Jens Axboe0420ba62012-02-29 11:16:52 +01002011
Jens Axboe2f99deb2012-03-09 14:37:29 +01002012 gtk_box_pack_start(GTK_BOX(ui->vbox), ui->error_info_bar, FALSE, FALSE, 0);
2013 gtk_widget_show_all(ui->vbox);
Jens Axboe0420ba62012-02-29 11:16:52 +01002014 } else {
2015 char buffer[256];
2016 snprintf(buffer, sizeof(buffer), "Failed to open file.");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002017 gtk_label_set(GTK_LABEL(ui->error_label), buffer);
Jens Axboe0420ba62012-02-29 11:16:52 +01002018 }
2019}
2020
Jens Axboe62bc9372012-03-07 11:45:07 +01002021struct connection_widgets
2022{
2023 GtkWidget *hentry;
2024 GtkWidget *combo;
2025 GtkWidget *button;
2026};
2027
2028static void hostname_cb(GtkEntry *entry, gpointer data)
2029{
2030 struct connection_widgets *cw = data;
2031 int uses_net = 0, is_localhost = 0;
2032 const gchar *text;
2033 gchar *ctext;
2034
2035 /*
2036 * Check whether to display the 'auto start backend' box
2037 * or not. Show it if we are a localhost and using network,
2038 * or using a socket.
2039 */
2040 ctext = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw->combo));
2041 if (!ctext || !strncmp(ctext, "IPv4", 4) || !strncmp(ctext, "IPv6", 4))
2042 uses_net = 1;
2043 g_free(ctext);
2044
2045 if (uses_net) {
2046 text = gtk_entry_get_text(GTK_ENTRY(cw->hentry));
2047 if (!strcmp(text, "127.0.0.1") || !strcmp(text, "localhost") ||
2048 !strcmp(text, "::1") || !strcmp(text, "ip6-localhost") ||
2049 !strcmp(text, "ip6-loopback"))
2050 is_localhost = 1;
2051 }
2052
2053 if (!uses_net || is_localhost) {
2054 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 1);
2055 gtk_widget_set_sensitive(cw->button, 1);
2056 } else {
2057 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw->button), 0);
2058 gtk_widget_set_sensitive(cw->button, 0);
2059 }
2060}
2061
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002062static int get_connection_details(char **host, int *port, int *type,
2063 int *server_start)
Jens Axboea7a42ce2012-03-02 13:12:04 +01002064{
Jens Axboe62bc9372012-03-07 11:45:07 +01002065 GtkWidget *dialog, *box, *vbox, *hbox, *frame, *pentry;
2066 struct connection_widgets cw;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002067 char *typeentry;
2068
2069 dialog = gtk_dialog_new_with_buttons("Connection details",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002070 GTK_WINDOW(main_ui.window),
Jens Axboea7a42ce2012-03-02 13:12:04 +01002071 GTK_DIALOG_DESTROY_WITH_PARENT,
2072 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2073 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
2074
2075 frame = gtk_frame_new("Hostname / socket name");
Jens Axboef1299092012-03-07 20:00:02 +01002076 /* gtk_dialog_get_content_area() is 2.14 and newer */
2077 vbox = GTK_DIALOG(dialog)->vbox;
Jens Axboea7a42ce2012-03-02 13:12:04 +01002078 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2079
2080 box = gtk_vbox_new(FALSE, 6);
2081 gtk_container_add(GTK_CONTAINER(frame), box);
2082
2083 hbox = gtk_hbox_new(TRUE, 10);
2084 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
Jens Axboe62bc9372012-03-07 11:45:07 +01002085 cw.hentry = gtk_entry_new();
2086 gtk_entry_set_text(GTK_ENTRY(cw.hentry), "localhost");
2087 gtk_box_pack_start(GTK_BOX(hbox), cw.hentry, TRUE, TRUE, 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002088
2089 frame = gtk_frame_new("Port");
2090 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2091 box = gtk_vbox_new(FALSE, 10);
2092 gtk_container_add(GTK_CONTAINER(frame), box);
2093
2094 hbox = gtk_hbox_new(TRUE, 4);
2095 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2096 pentry = create_spinbutton(hbox, 1, 65535, FIO_NET_PORT);
2097
2098 frame = gtk_frame_new("Type");
2099 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2100 box = gtk_vbox_new(FALSE, 10);
2101 gtk_container_add(GTK_CONTAINER(frame), box);
2102
2103 hbox = gtk_hbox_new(TRUE, 4);
2104 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2105
Jens Axboe62bc9372012-03-07 11:45:07 +01002106 cw.combo = gtk_combo_box_new_text();
2107 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv4");
2108 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "IPv6");
2109 gtk_combo_box_append_text(GTK_COMBO_BOX(cw.combo), "local socket");
2110 gtk_combo_box_set_active(GTK_COMBO_BOX(cw.combo), 0);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002111
Jens Axboe62bc9372012-03-07 11:45:07 +01002112 gtk_container_add(GTK_CONTAINER(hbox), cw.combo);
Jens Axboea7a42ce2012-03-02 13:12:04 +01002113
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002114 frame = gtk_frame_new("Options");
2115 gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
2116 box = gtk_vbox_new(FALSE, 10);
2117 gtk_container_add(GTK_CONTAINER(frame), box);
2118
2119 hbox = gtk_hbox_new(TRUE, 4);
2120 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
2121
Jens Axboe62bc9372012-03-07 11:45:07 +01002122 cw.button = gtk_check_button_new_with_label("Auto-spawn fio backend");
2123 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cw.button), 1);
2124 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.");
2125 gtk_box_pack_start(GTK_BOX(hbox), cw.button, FALSE, FALSE, 6);
2126
2127 /*
2128 * Connect edit signal, so we can show/not-show the auto start button
2129 */
2130 g_signal_connect(GTK_OBJECT(cw.hentry), "changed", G_CALLBACK(hostname_cb), &cw);
2131 g_signal_connect(GTK_OBJECT(cw.combo), "changed", G_CALLBACK(hostname_cb), &cw);
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002132
Jens Axboea7a42ce2012-03-02 13:12:04 +01002133 gtk_widget_show_all(dialog);
2134
2135 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2136 gtk_widget_destroy(dialog);
2137 return 1;
2138 }
2139
Jens Axboe62bc9372012-03-07 11:45:07 +01002140 *host = strdup(gtk_entry_get_text(GTK_ENTRY(cw.hentry)));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002141 *port = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(pentry));
2142
Jens Axboe62bc9372012-03-07 11:45:07 +01002143 typeentry = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cw.combo));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002144 if (!typeentry || !strncmp(typeentry, "IPv4", 4))
2145 *type = Fio_client_ipv4;
2146 else if (!strncmp(typeentry, "IPv6", 4))
2147 *type = Fio_client_ipv6;
2148 else
2149 *type = Fio_client_socket;
2150 g_free(typeentry);
2151
Jens Axboe62bc9372012-03-07 11:45:07 +01002152 *server_start = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw.button));
Jens Axboeb9f3c7e2012-03-02 14:27:17 +01002153
Jens Axboea7a42ce2012-03-02 13:12:04 +01002154 gtk_widget_destroy(dialog);
2155 return 0;
2156}
2157
Jens Axboe2f99deb2012-03-09 14:37:29 +01002158static void gfio_client_added(struct gui_entry *ge, struct fio_client *client)
Jens Axboee0681f32012-03-06 12:14:42 +01002159{
2160 struct gfio_client *gc;
2161
2162 gc = malloc(sizeof(*gc));
2163 memset(gc, 0, sizeof(*gc));
Jens Axboe2f99deb2012-03-09 14:37:29 +01002164 gc->ge = ge;
Jens Axboe343cb4a2012-03-09 17:16:51 +01002165 gc->client = fio_get_client(client);
Jens Axboeb9d2f302012-03-08 20:36:28 +01002166
Jens Axboe2f99deb2012-03-09 14:37:29 +01002167 ge->client = gc;
Jens Axboee0681f32012-03-06 12:14:42 +01002168
2169 client->client_data = gc;
2170}
2171
Jens Axboe2f99deb2012-03-09 14:37:29 +01002172static GtkWidget *new_client_page(struct gui_entry *ge);
2173
2174static struct gui_entry *alloc_new_gui_entry(struct gui *ui)
2175{
2176 struct gui_entry *ge;
2177
2178 ge = malloc(sizeof(*ge));
2179 memset(ge, 0, sizeof(*ge));
Jens Axboe85dd01e2012-03-12 14:33:16 +01002180 ge->state = GE_STATE_NEW;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002181 INIT_FLIST_HEAD(&ge->list);
2182 flist_add_tail(&ge->list, &ui->list);
2183 ge->ui = ui;
2184 return ge;
2185}
2186
Jens Axboe2f99deb2012-03-09 14:37:29 +01002187static struct gui_entry *get_new_ge_with_tab(const char *name)
2188{
2189 struct gui_entry *ge;
2190
2191 ge = alloc_new_gui_entry(&main_ui);
2192
2193 ge->vbox = new_client_page(ge);
Jens Axboe0fd18982012-03-14 10:34:48 +01002194 g_signal_connect(ge->vbox, "destroy", G_CALLBACK(ge_widget_destroy), ge);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002195
2196 ge->page_label = gtk_label_new(name);
2197 ge->page_num = gtk_notebook_append_page(GTK_NOTEBOOK(main_ui.notebook), ge->vbox, ge->page_label);
2198
2199 gtk_widget_show_all(main_ui.window);
2200 return ge;
2201}
2202
2203static void file_new(GtkWidget *w, gpointer data)
2204{
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002205 struct gui *ui = (struct gui *) data;
2206 struct gui_entry *ge;
2207
2208 ge = get_new_ge_with_tab("Untitled");
2209 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002210}
2211
2212/*
2213 * Return the 'ge' corresponding to the tab. If the active tab is the
2214 * main tab, open a new tab.
2215 */
Jens Axboe38634cb2012-03-13 12:26:41 +01002216static struct gui_entry *get_ge_from_page(gint cur_page, int *created)
Jens Axboe2f99deb2012-03-09 14:37:29 +01002217{
2218 struct flist_head *entry;
2219 struct gui_entry *ge;
2220
Jens Axboe38634cb2012-03-13 12:26:41 +01002221 if (!cur_page) {
2222 if (created)
2223 *created = 1;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002224 return get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002225 }
2226
2227 if (created)
2228 *created = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002229
2230 flist_for_each(entry, &main_ui.list) {
2231 ge = flist_entry(entry, struct gui_entry, list);
2232 if (ge->page_num == cur_page)
2233 return ge;
2234 }
2235
2236 return NULL;
2237}
2238
Jens Axboe85dd01e2012-03-12 14:33:16 +01002239static struct gui_entry *get_ge_from_cur_tab(struct gui *ui)
2240{
2241 gint cur_page;
2242
2243 /*
2244 * Main tab is tab 0, so any current page other than 0 holds
2245 * a ge entry.
2246 */
2247 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
2248 if (cur_page)
Jens Axboe38634cb2012-03-13 12:26:41 +01002249 return get_ge_from_page(cur_page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002250
2251 return NULL;
2252}
2253
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002254static void file_close(GtkWidget *w, gpointer data)
2255{
2256 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002257 struct gui_entry *ge;
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002258
2259 /*
2260 * Can't close the main tab
2261 */
Jens Axboe85dd01e2012-03-12 14:33:16 +01002262 ge = get_ge_from_cur_tab(ui);
2263 if (ge) {
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002264 gtk_widget_destroy(ge->vbox);
2265 return;
2266 }
2267
Jens Axboef5c67262012-03-13 08:20:41 +01002268 if (!flist_empty(&ui->list)) {
2269 show_info_dialog(ui, "Error", "The main page view cannot be closed\n");
2270 return;
2271 }
2272
Jens Axboe0fd18982012-03-14 10:34:48 +01002273 gfio_quit(ui);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002274}
2275
Jens Axboe38634cb2012-03-13 12:26:41 +01002276static void file_add_recent(struct gui *ui, const gchar *uri)
2277{
Jens Axboea217ba72012-03-13 20:29:39 +01002278 GtkRecentData grd;
2279
2280 memset(&grd, 0, sizeof(grd));
2281 grd.display_name = strdup("gfio");
2282 grd.description = strdup("Fio job file");
2283 grd.mime_type = strdup(GFIO_MIME);
2284 grd.app_name = strdup(g_get_application_name());
2285 grd.app_exec = strdup("gfio %f/%u");
2286
2287 gtk_recent_manager_add_full(ui->recentmanager, uri, &grd);
Jens Axboe38634cb2012-03-13 12:26:41 +01002288}
2289
2290static gchar *get_filename_from_uri(const gchar *uri)
2291{
2292 if (strncmp(uri, "file://", 7))
2293 return strdup(uri);
2294
2295 return strdup(uri + 7);
2296}
2297
2298static int do_file_open(struct gui_entry *ge, const gchar *uri, char *host,
2299 int type, int port)
2300{
2301 struct fio_client *client;
2302 gchar *filename;
2303
2304 filename = get_filename_from_uri(uri);
2305
2306 ge->job_files = realloc(ge->job_files, (ge->nr_job_files + 1) * sizeof(char *));
2307 ge->job_files[ge->nr_job_files] = strdup(filename);
2308 ge->nr_job_files++;
2309
2310 client = fio_client_add_explicit(&gfio_client_ops, host, type, port);
2311 if (!client) {
2312 GError *error;
2313
2314 error = g_error_new(g_quark_from_string("fio"), 1,
2315 "Failed to add client %s", host);
2316 report_error(error);
2317 g_error_free(error);
2318 return 1;
2319 }
2320
2321 gfio_client_added(ge, client);
2322 file_add_recent(ge->ui, uri);
2323 return 0;
2324}
2325
Jens Axboea6790902012-03-13 15:16:11 +01002326static int do_file_open_with_tab(struct gui *ui, const gchar *uri)
Jens Axboe0420ba62012-02-29 11:16:52 +01002327{
Jens Axboea6790902012-03-13 15:16:11 +01002328 int port, type, server_start;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002329 struct gui_entry *ge;
2330 gint cur_page;
Jens Axboe38634cb2012-03-13 12:26:41 +01002331 char *host;
Jens Axboea6790902012-03-13 15:16:11 +01002332 int ret, ge_is_new = 0;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002333
2334 /*
2335 * Creates new tab if current tab is the main window, or the
2336 * current tab already has a client.
2337 */
2338 cur_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(ui->notebook));
Jens Axboe38634cb2012-03-13 12:26:41 +01002339 ge = get_ge_from_page(cur_page, &ge_is_new);
2340 if (ge->client) {
Jens Axboe2f99deb2012-03-09 14:37:29 +01002341 ge = get_new_ge_with_tab("Untitled");
Jens Axboe38634cb2012-03-13 12:26:41 +01002342 ge_is_new = 1;
2343 }
Jens Axboe2f99deb2012-03-09 14:37:29 +01002344
2345 gtk_notebook_set_current_page(GTK_NOTEBOOK(ui->notebook), ge->page_num);
Jens Axboe0420ba62012-02-29 11:16:52 +01002346
Jens Axboea6790902012-03-13 15:16:11 +01002347 if (get_connection_details(&host, &port, &type, &server_start)) {
2348 if (ge_is_new)
2349 gtk_widget_destroy(ge->vbox);
2350
2351 return 1;
2352 }
2353
2354 ret = do_file_open(ge, uri, host, type, port);
2355
2356 free(host);
2357
2358 if (!ret) {
2359 if (server_start)
2360 gfio_start_server();
2361 } else {
2362 if (ge_is_new)
2363 gtk_widget_destroy(ge->vbox);
2364 }
2365
2366 return ret;
2367}
2368
2369static void recent_open(GtkAction *action, gpointer data)
2370{
2371 struct gui *ui = (struct gui *) data;
2372 GtkRecentInfo *info;
2373 const gchar *uri;
2374
2375 info = g_object_get_data(G_OBJECT(action), "gtk-recent-info");
2376 uri = gtk_recent_info_get_uri(info);
2377
2378 do_file_open_with_tab(ui, uri);
2379}
2380
2381static void file_open(GtkWidget *w, gpointer data)
2382{
2383 struct gui *ui = data;
2384 GtkWidget *dialog;
2385 GSList *filenames, *fn_glist;
2386 GtkFileFilter *filter;
2387
Jens Axboe0420ba62012-02-29 11:16:52 +01002388 dialog = gtk_file_chooser_dialog_new("Open File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002389 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002390 GTK_FILE_CHOOSER_ACTION_OPEN,
2391 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2392 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2393 NULL);
2394 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
2395
2396 filter = gtk_file_filter_new();
2397 gtk_file_filter_add_pattern(filter, "*.fio");
2398 gtk_file_filter_add_pattern(filter, "*.job");
Jens Axboe2d262992012-03-07 08:19:30 +01002399 gtk_file_filter_add_pattern(filter, "*.ini");
Jens Axboe38634cb2012-03-13 12:26:41 +01002400 gtk_file_filter_add_mime_type(filter, GFIO_MIME);
Jens Axboe0420ba62012-02-29 11:16:52 +01002401 gtk_file_filter_set_name(filter, "Fio job file");
2402 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
2403
2404 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2405 gtk_widget_destroy(dialog);
2406 return;
2407 }
2408
2409 fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
Jens Axboea7a42ce2012-03-02 13:12:04 +01002410
2411 gtk_widget_destroy(dialog);
2412
Jens Axboe0420ba62012-02-29 11:16:52 +01002413 filenames = fn_glist;
2414 while (filenames != NULL) {
Jens Axboea6790902012-03-13 15:16:11 +01002415 if (do_file_open_with_tab(ui, filenames->data))
2416 break;
Jens Axboe0420ba62012-02-29 11:16:52 +01002417 filenames = g_slist_next(filenames);
2418 }
Jens Axboe63a130b2012-03-06 20:08:59 +01002419
Jens Axboe0420ba62012-02-29 11:16:52 +01002420 g_slist_free(fn_glist);
Jens Axboe0420ba62012-02-29 11:16:52 +01002421}
2422
2423static void file_save(GtkWidget *w, gpointer data)
2424{
Jens Axboe63a130b2012-03-06 20:08:59 +01002425 struct gui *ui = data;
Jens Axboe0420ba62012-02-29 11:16:52 +01002426 GtkWidget *dialog;
2427
2428 dialog = gtk_file_chooser_dialog_new("Save File",
Jens Axboe63a130b2012-03-06 20:08:59 +01002429 GTK_WINDOW(ui->window),
Jens Axboe0420ba62012-02-29 11:16:52 +01002430 GTK_FILE_CHOOSER_ACTION_SAVE,
2431 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2432 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2433 NULL);
2434
2435 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
2436 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
2437
2438 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
2439 char *filename;
2440
2441 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
2442 // save_job_file(filename);
2443 g_free(filename);
2444 }
2445 gtk_widget_destroy(dialog);
2446}
2447
Jens Axboe9b260bd2012-03-06 11:02:52 +01002448static void view_log_destroy(GtkWidget *w, gpointer data)
2449{
2450 struct gui *ui = (struct gui *) data;
2451
2452 gtk_widget_ref(ui->log_tree);
2453 gtk_container_remove(GTK_CONTAINER(w), ui->log_tree);
2454 gtk_widget_destroy(w);
Jens Axboe4cbe7212012-03-06 13:36:17 +01002455 ui->log_view = NULL;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002456}
2457
2458static void view_log(GtkWidget *w, gpointer data)
2459{
Jens Axboe4cbe7212012-03-06 13:36:17 +01002460 GtkWidget *win, *scroll, *vbox, *box;
2461 struct gui *ui = (struct gui *) data;
Jens Axboe9b260bd2012-03-06 11:02:52 +01002462
Jens Axboe4cbe7212012-03-06 13:36:17 +01002463 if (ui->log_view)
2464 return;
2465
2466 ui->log_view = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002467 gtk_window_set_title(GTK_WINDOW(win), "Log");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002468 gtk_window_set_default_size(GTK_WINDOW(win), 700, 500);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002469
Jens Axboe4cbe7212012-03-06 13:36:17 +01002470 scroll = gtk_scrolled_window_new(NULL, NULL);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002471
Jens Axboe4cbe7212012-03-06 13:36:17 +01002472 gtk_container_set_border_width(GTK_CONTAINER(scroll), 5);
2473
2474 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2475
2476 box = gtk_hbox_new(TRUE, 0);
2477 gtk_box_pack_start_defaults(GTK_BOX(box), ui->log_tree);
2478 g_signal_connect(box, "destroy", G_CALLBACK(view_log_destroy), ui);
2479 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box);
2480
2481 vbox = gtk_vbox_new(TRUE, 5);
2482 gtk_box_pack_start_defaults(GTK_BOX(vbox), scroll);
2483
2484 gtk_container_add(GTK_CONTAINER(win), vbox);
Jens Axboe9b260bd2012-03-06 11:02:52 +01002485 gtk_widget_show_all(win);
2486}
2487
Jens Axboe85dd01e2012-03-12 14:33:16 +01002488static void connect_job_entry(GtkWidget *w, gpointer data)
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002489{
Jens Axboe85dd01e2012-03-12 14:33:16 +01002490 struct gui *ui = (struct gui *) data;
2491 struct gui_entry *ge;
2492
2493 ge = get_ge_from_cur_tab(ui);
2494 if (ge)
2495 connect_clicked(w, ge);
2496}
2497
2498static void send_job_entry(GtkWidget *w, gpointer data)
2499{
2500 struct gui *ui = (struct gui *) data;
2501 struct gui_entry *ge;
2502
2503 ge = get_ge_from_cur_tab(ui);
2504 if (ge)
2505 send_clicked(w, ge);
2506
2507}
2508
2509static void edit_job_entry(GtkWidget *w, gpointer data)
2510{
Jens Axboe9af4a242012-03-16 10:13:49 +01002511 struct gui *ui = (struct gui *) data;
2512
2513 gopt_get_options_window(ui->window);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002514}
2515
2516static void start_job_entry(GtkWidget *w, gpointer data)
2517{
2518 struct gui *ui = (struct gui *) data;
2519 struct gui_entry *ge;
2520
2521 ge = get_ge_from_cur_tab(ui);
2522 if (ge)
2523 start_job_clicked(w, ge);
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002524}
2525
Jens Axboe781ccba2012-03-15 09:44:42 +01002526static void view_results(GtkWidget *w, gpointer data)
2527{
2528 struct gui *ui = (struct gui *) data;
2529 struct gfio_client *gc;
2530 struct gui_entry *ge;
2531
2532 ge = get_ge_from_cur_tab(ui);
2533 if (!ge)
2534 return;
2535
2536 if (ge->results_window)
2537 return;
2538
2539 gc = ge->client;
2540 if (gc && gc->nr_results)
2541 gfio_display_end_results(gc);
2542}
2543
Jens Axboe8577f4f2012-03-09 19:28:27 +01002544static void __update_graph_limits(struct gfio_graphs *g)
2545{
2546 line_graph_set_data_count_limit(g->iops_graph, gfio_graph_limit);
2547 line_graph_set_data_count_limit(g->bandwidth_graph, gfio_graph_limit);
2548}
2549
2550static void update_graph_limits(void)
2551{
2552 struct flist_head *entry;
2553 struct gui_entry *ge;
2554
2555 __update_graph_limits(&main_ui.graphs);
2556
2557 flist_for_each(entry, &main_ui.list) {
2558 ge = flist_entry(entry, struct gui_entry, list);
2559 __update_graph_limits(&ge->graphs);
2560 }
2561}
2562
Jens Axboe46974a72012-03-02 19:34:13 +01002563static void preferences(GtkWidget *w, gpointer data)
2564{
Jens Axboef3e84402012-03-07 13:14:32 +01002565 GtkWidget *dialog, *frame, *box, **buttons, *vbox, *font;
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002566 GtkWidget *hbox, *spin, *entry, *spin_int;
Jens Axboe46974a72012-03-02 19:34:13 +01002567 int i;
2568
2569 dialog = gtk_dialog_new_with_buttons("Preferences",
Jens Axboe2f99deb2012-03-09 14:37:29 +01002570 GTK_WINDOW(main_ui.window),
Jens Axboe46974a72012-03-02 19:34:13 +01002571 GTK_DIALOG_DESTROY_WITH_PARENT,
2572 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2573 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2574 NULL);
2575
Jens Axboe8577f4f2012-03-09 19:28:27 +01002576 frame = gtk_frame_new("Graphing");
Jens Axboef3e84402012-03-07 13:14:32 +01002577 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2578 vbox = gtk_vbox_new(FALSE, 6);
2579 gtk_container_add(GTK_CONTAINER(frame), vbox);
2580
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002581 hbox = gtk_hbox_new(FALSE, 5);
2582 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2583 entry = gtk_label_new("Font face to use for graph labels");
2584 gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
2585
Jens Axboef3e84402012-03-07 13:14:32 +01002586 font = gtk_font_button_new();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002587 gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 5);
Jens Axboef3e84402012-03-07 13:14:32 +01002588
Jens Axboe8577f4f2012-03-09 19:28:27 +01002589 box = gtk_vbox_new(FALSE, 6);
2590 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2591
2592 hbox = gtk_hbox_new(FALSE, 5);
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002593 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002594 entry = gtk_label_new("Maximum number of data points in graph (seconds)");
2595 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2596
Jens Axboec05d9052012-03-11 13:05:35 +01002597 spin = create_spinbutton(hbox, 10, 1000000, gfio_graph_limit);
Jens Axboe8577f4f2012-03-09 19:28:27 +01002598
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002599 box = gtk_vbox_new(FALSE, 6);
2600 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 5);
2601
2602 hbox = gtk_hbox_new(FALSE, 5);
2603 gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 5);
2604 entry = gtk_label_new("Client ETA request interval (msec)");
2605 gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
2606
2607 spin_int = create_spinbutton(hbox, 100, 100000, gfio_client_ops.eta_msec);
Jens Axboea31d9fa2012-03-09 20:23:05 +01002608 frame = gtk_frame_new("Debug logging");
2609 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
2610 vbox = gtk_vbox_new(FALSE, 6);
2611 gtk_container_add(GTK_CONTAINER(frame), vbox);
2612
2613 box = gtk_hbox_new(FALSE, 6);
2614 gtk_container_add(GTK_CONTAINER(vbox), box);
2615
2616 buttons = malloc(sizeof(GtkWidget *) * FD_DEBUG_MAX);
2617
2618 for (i = 0; i < FD_DEBUG_MAX; i++) {
2619 if (i == 7) {
2620 box = gtk_hbox_new(FALSE, 6);
2621 gtk_container_add(GTK_CONTAINER(vbox), box);
2622 }
2623
2624
2625 buttons[i] = gtk_check_button_new_with_label(debug_levels[i].name);
2626 gtk_widget_set_tooltip_text(buttons[i], debug_levels[i].help);
2627 gtk_box_pack_start(GTK_BOX(box), buttons[i], FALSE, FALSE, 6);
2628 }
2629
Jens Axboe46974a72012-03-02 19:34:13 +01002630 gtk_widget_show_all(dialog);
2631
2632 if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_ACCEPT) {
2633 gtk_widget_destroy(dialog);
2634 return;
2635 }
2636
2637 for (i = 0; i < FD_DEBUG_MAX; i++) {
2638 int set;
2639
2640 set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(buttons[i]));
2641 if (set)
2642 fio_debug |= (1UL << i);
2643 }
2644
Jens Axboef3e84402012-03-07 13:14:32 +01002645 gfio_graph_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002646 gfio_graph_limit = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
2647 update_graph_limits();
Jens Axboe1cf6bca2012-03-09 20:20:17 +01002648 gfio_client_ops.eta_msec = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_int));
Jens Axboe8577f4f2012-03-09 19:28:27 +01002649
Jens Axboe46974a72012-03-02 19:34:13 +01002650 gtk_widget_destroy(dialog);
2651}
2652
Jens Axboe0420ba62012-02-29 11:16:52 +01002653static void about_dialog(GtkWidget *w, gpointer data)
2654{
Jens Axboe81e4ea62012-03-07 14:18:28 +01002655 const char *authors[] = {
2656 "Jens Axboe <axboe@kernel.dk>",
2657 "Stephen Carmeron <stephenmcameron@gmail.com>",
2658 NULL
2659 };
Jens Axboe84a72ed2012-03-07 14:24:57 +01002660 const char *license[] = {
2661 "Fio is free software; you can redistribute it and/or modify "
2662 "it under the terms of the GNU General Public License as published by "
2663 "the Free Software Foundation; either version 2 of the License, or "
2664 "(at your option) any later version.\n",
2665 "Fio is distributed in the hope that it will be useful, "
2666 "but WITHOUT ANY WARRANTY; without even the implied warranty of "
2667 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
2668 "GNU General Public License for more details.\n",
2669 "You should have received a copy of the GNU General Public License "
2670 "along with Fio; if not, write to the Free Software Foundation, Inc., "
2671 "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
2672 };
2673 char *license_trans;
2674
2675 license_trans = g_strconcat(license[0], "\n", license[1], "\n",
2676 license[2], "\n", NULL);
Jens Axboe81e4ea62012-03-07 14:18:28 +01002677
Jens Axboe0420ba62012-02-29 11:16:52 +01002678 gtk_show_about_dialog(NULL,
2679 "program-name", "gfio",
2680 "comments", "Gtk2 UI for fio",
Jens Axboe84a72ed2012-03-07 14:24:57 +01002681 "license", license_trans,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002682 "website", "http://git.kernel.dk/?p=fio.git;a=summary",
2683 "authors", authors,
Jens Axboe0420ba62012-02-29 11:16:52 +01002684 "version", fio_version_string,
Jens Axboe81e4ea62012-03-07 14:18:28 +01002685 "copyright", "© 2012 Jens Axboe <axboe@kernel.dk>",
Jens Axboe0420ba62012-02-29 11:16:52 +01002686 "logo-icon-name", "fio",
2687 /* Must be last: */
Jens Axboe81e4ea62012-03-07 14:18:28 +01002688 "wrap-license", TRUE,
Jens Axboe0420ba62012-02-29 11:16:52 +01002689 NULL);
Jens Axboe84a72ed2012-03-07 14:24:57 +01002690
Jens Axboe2f99deb2012-03-09 14:37:29 +01002691 g_free(license_trans);
Jens Axboe0420ba62012-02-29 11:16:52 +01002692}
2693
2694static GtkActionEntry menu_items[] = {
Jens Axboe46974a72012-03-02 19:34:13 +01002695 { "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
Jens Axboe9b260bd2012-03-06 11:02:52 +01002696 { "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002697 { "JobMenuAction", GTK_STOCK_FILE, "Job", NULL, NULL, NULL},
Jens Axboe46974a72012-03-02 19:34:13 +01002698 { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
Jens Axboe2f99deb2012-03-09 14:37:29 +01002699 { "NewFile", GTK_STOCK_NEW, "New", "<Control>N", NULL, G_CALLBACK(file_new) },
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002700 { "CloseFile", GTK_STOCK_CLOSE, "Close", "<Control>W", NULL, G_CALLBACK(file_close) },
Jens Axboe46974a72012-03-02 19:34:13 +01002701 { "OpenFile", GTK_STOCK_OPEN, NULL, "<Control>O", NULL, G_CALLBACK(file_open) },
2702 { "SaveFile", GTK_STOCK_SAVE, NULL, "<Control>S", NULL, G_CALLBACK(file_save) },
2703 { "Preferences", GTK_STOCK_PREFERENCES, NULL, "<Control>p", NULL, G_CALLBACK(preferences) },
Jens Axboe9b260bd2012-03-06 11:02:52 +01002704 { "ViewLog", NULL, "Log", "<Control>l", NULL, G_CALLBACK(view_log) },
Jens Axboe781ccba2012-03-15 09:44:42 +01002705 { "ViewResults", NULL, "Results", "<Control>R", NULL, G_CALLBACK(view_results) },
Jens Axboebc271d82012-03-15 21:57:40 +01002706 { "ConnectJob", NULL, "Connect", "<Control>D", NULL, G_CALLBACK(connect_job_entry) },
Jens Axboe85dd01e2012-03-12 14:33:16 +01002707 { "EditJob", NULL, "Edit job", "<Control>E", NULL, G_CALLBACK(edit_job_entry) },
2708 { "SendJob", NULL, "Send job", "<Control>X", NULL, G_CALLBACK(send_job_entry) },
2709 { "StartJob", NULL, "Start job", "<Control>L", NULL, G_CALLBACK(start_job_entry) },
Jens Axboe46974a72012-03-02 19:34:13 +01002710 { "Quit", GTK_STOCK_QUIT, NULL, "<Control>Q", NULL, G_CALLBACK(quit_clicked) },
2711 { "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
Jens Axboe0420ba62012-02-29 11:16:52 +01002712};
Jens Axboe3e47bd22012-02-29 13:45:02 +01002713static gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
Jens Axboe0420ba62012-02-29 11:16:52 +01002714
2715static const gchar *ui_string = " \
2716 <ui> \
2717 <menubar name=\"MainMenu\"> \
2718 <menu name=\"FileMenu\" action=\"FileMenuAction\"> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002719 <menuitem name=\"New\" action=\"NewFile\" /> \
Jens Axboebf641382012-03-15 13:46:16 +01002720 <menuitem name=\"Open\" action=\"OpenFile\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002721 <menuitem name=\"Close\" action=\"CloseFile\" /> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002722 <separator name=\"Separator1\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002723 <menuitem name=\"Save\" action=\"SaveFile\" /> \
Jens Axboe46974a72012-03-02 19:34:13 +01002724 <separator name=\"Separator2\"/> \
Jens Axboe2f99deb2012-03-09 14:37:29 +01002725 <menuitem name=\"Preferences\" action=\"Preferences\" /> \
2726 <separator name=\"Separator3\"/> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002727 <placeholder name=\"FileRecentFiles\"/> \
2728 <separator name=\"Separator4\"/> \
Jens Axboe0420ba62012-02-29 11:16:52 +01002729 <menuitem name=\"Quit\" action=\"Quit\" /> \
2730 </menu> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002731 <menu name=\"JobMenu\" action=\"JobMenuAction\"> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002732 <menuitem name=\"Connect\" action=\"ConnectJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002733 <separator name=\"Separator5\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002734 <menuitem name=\"Edit job\" action=\"EditJob\" /> \
2735 <menuitem name=\"Send job\" action=\"SendJob\" /> \
Jens Axboe261f21d2012-03-12 14:58:22 +01002736 <separator name=\"Separator6\"/> \
Jens Axboe85dd01e2012-03-12 14:33:16 +01002737 <menuitem name=\"Start job\" action=\"StartJob\" /> \
Jens Axboe16ce5ad2012-03-12 11:56:09 +01002738 </menu>\
Jens Axboe9b260bd2012-03-06 11:02:52 +01002739 <menu name=\"ViewMenu\" action=\"ViewMenuAction\"> \
Jens Axboe781ccba2012-03-15 09:44:42 +01002740 <menuitem name=\"Results\" action=\"ViewResults\" /> \
2741 <separator name=\"Separator7\"/> \
Jens Axboe9b260bd2012-03-06 11:02:52 +01002742 <menuitem name=\"Log\" action=\"ViewLog\" /> \
2743 </menu>\
Jens Axboe0420ba62012-02-29 11:16:52 +01002744 <menu name=\"Help\" action=\"HelpMenuAction\"> \
2745 <menuitem name=\"About\" action=\"About\" /> \
2746 </menu> \
2747 </menubar> \
2748 </ui> \
2749";
2750
Jens Axboe4cbe7212012-03-06 13:36:17 +01002751static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager,
2752 struct gui *ui)
Jens Axboe0420ba62012-02-29 11:16:52 +01002753{
Jens Axboeca664f42012-03-14 19:49:40 +01002754 GtkActionGroup *action_group;
Jens Axboe0420ba62012-02-29 11:16:52 +01002755 GError *error = 0;
2756
2757 action_group = gtk_action_group_new("Menu");
Jens Axboe4cbe7212012-03-06 13:36:17 +01002758 gtk_action_group_add_actions(action_group, menu_items, nmenu_items, ui);
Jens Axboe0420ba62012-02-29 11:16:52 +01002759
2760 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
2761 gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
2762
2763 gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
Jens Axboe02421e62012-03-12 12:05:50 +01002764
Jens Axboe0420ba62012-02-29 11:16:52 +01002765 return gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
2766}
2767
2768void gfio_ui_setup(GtkSettings *settings, GtkWidget *menubar,
2769 GtkWidget *vbox, GtkUIManager *ui_manager)
2770{
2771 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
2772}
2773
Jens Axboec80b74b2012-03-12 10:23:28 +01002774static void combo_entry_changed(GtkComboBox *box, gpointer data)
2775{
2776 struct gui_entry *ge = (struct gui_entry *) data;
2777 gint index;
2778
2779 index = gtk_combo_box_get_active(box);
2780
2781 multitext_set_entry(&ge->eta.iotype, index);
Jens Axboe99d633a2012-03-15 15:55:04 +01002782 multitext_set_entry(&ge->eta.bs, index);
Jens Axboec80b74b2012-03-12 10:23:28 +01002783 multitext_set_entry(&ge->eta.ioengine, index);
2784 multitext_set_entry(&ge->eta.iodepth, index);
2785}
2786
2787static void combo_entry_destroy(GtkWidget *widget, gpointer data)
2788{
2789 struct gui_entry *ge = (struct gui_entry *) data;
2790
2791 multitext_free(&ge->eta.iotype);
Jens Axboe99d633a2012-03-15 15:55:04 +01002792 multitext_free(&ge->eta.bs);
Jens Axboec80b74b2012-03-12 10:23:28 +01002793 multitext_free(&ge->eta.ioengine);
2794 multitext_free(&ge->eta.iodepth);
2795}
2796
Jens Axboe2f99deb2012-03-09 14:37:29 +01002797static GtkWidget *new_client_page(struct gui_entry *ge)
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01002798{
Jens Axboe2f99deb2012-03-09 14:37:29 +01002799 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002800 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe0420ba62012-02-29 11:16:52 +01002801
Jens Axboe2f99deb2012-03-09 14:37:29 +01002802 main_vbox = gtk_vbox_new(FALSE, 3);
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002803
Jens Axboe65476332012-03-13 10:37:04 +01002804 top_align = gtk_alignment_new(0, 0, 1, 0);
2805 top_vbox = gtk_vbox_new(FALSE, 3);
2806 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2807 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002808
Jens Axboe3e47bd22012-02-29 13:45:02 +01002809 probe = gtk_frame_new("Job");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002810 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
Jens Axboe843ad232012-02-29 11:44:53 +01002811 probe_frame = gtk_vbox_new(FALSE, 3);
2812 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
2813
2814 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002815 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2816 ge->probe.hostname = new_info_label_in_frame(probe_box, "Host");
2817 ge->probe.os = new_info_label_in_frame(probe_box, "OS");
2818 ge->probe.arch = new_info_label_in_frame(probe_box, "Architecture");
2819 ge->probe.fio_ver = new_info_label_in_frame(probe_box, "Fio version");
Jens Axboe843ad232012-02-29 11:44:53 +01002820
Jens Axboe3e47bd22012-02-29 13:45:02 +01002821 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002822 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2823
Jens Axboe3863d1a2012-03-09 17:39:05 +01002824 ge->eta.names = new_combo_entry_in_frame(probe_box, "Jobs");
Jens Axboec80b74b2012-03-12 10:23:28 +01002825 g_signal_connect(ge->eta.names, "changed", G_CALLBACK(combo_entry_changed), ge);
2826 g_signal_connect(ge->eta.names, "destroy", G_CALLBACK(combo_entry_destroy), ge);
2827 ge->eta.iotype.entry = new_info_entry_in_frame(probe_box, "IO");
Jens Axboe99d633a2012-03-15 15:55:04 +01002828 ge->eta.bs.entry = new_info_entry_in_frame(probe_box, "Blocksize (Read/Write)");
Jens Axboec80b74b2012-03-12 10:23:28 +01002829 ge->eta.ioengine.entry = new_info_entry_in_frame(probe_box, "IO Engine");
2830 ge->eta.iodepth.entry = new_info_entry_in_frame(probe_box, "IO Depth");
Jens Axboe2f99deb2012-03-09 14:37:29 +01002831 ge->eta.jobs = new_info_entry_in_frame(probe_box, "Jobs");
2832 ge->eta.files = new_info_entry_in_frame(probe_box, "Open files");
2833
2834 probe_box = gtk_hbox_new(FALSE, 3);
2835 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
2836 ge->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2837 ge->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2838 ge->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2839 ge->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
2840
2841 /*
2842 * Only add this if we have a commit rate
2843 */
2844#if 0
2845 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002846 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
Jens Axboe807f9972012-03-02 10:25:24 +01002847
Jens Axboe2f99deb2012-03-09 14:37:29 +01002848 ge->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2849 ge->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2850
2851 ge->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2852 ge->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2853#endif
2854
2855 /*
2856 * Set up a drawing area and IOPS and bandwidth graphs
2857 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002858 ge->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002859 gtk_widget_set_size_request(GTK_WIDGET(ge->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002860 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002861 gtk_widget_modify_bg(ge->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2862 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "expose_event",
2863 G_CALLBACK(on_expose_drawing_area), &ge->graphs);
2864 g_signal_connect(G_OBJECT(ge->graphs.drawing_area), "configure_event",
2865 G_CALLBACK(on_config_drawing_area), &ge->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002866 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2867 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002868 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002869 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002870 ge->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002871 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window, TRUE, TRUE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002872
2873 setup_graphs(&ge->graphs);
2874
2875 /*
2876 * Set up alignments for widgets at the bottom of ui,
2877 * align bottom left, expand horizontally but not vertically
2878 */
Jens Axboe65476332012-03-13 10:37:04 +01002879 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002880 ge->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002881 gtk_container_add(GTK_CONTAINER(bottom_align), ge->buttonbox);
2882 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002883
2884 add_buttons(ge, buttonspeclist, ARRAYSIZE(buttonspeclist));
2885
2886 /*
2887 * Set up thread status progress bar
2888 */
2889 ge->thread_status_pb = gtk_progress_bar_new();
2890 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ge->thread_status_pb), 0.0);
2891 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ge->thread_status_pb), "No connections");
2892 gtk_container_add(GTK_CONTAINER(ge->buttonbox), ge->thread_status_pb);
2893
2894
2895 return main_vbox;
2896}
2897
2898static GtkWidget *new_main_page(struct gui *ui)
2899{
2900 GtkWidget *main_vbox, *probe, *probe_frame, *probe_box;
Jens Axboe65476332012-03-13 10:37:04 +01002901 GtkWidget *scrolled_window, *bottom_align, *top_align, *top_vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01002902
2903 main_vbox = gtk_vbox_new(FALSE, 3);
2904
2905 /*
2906 * Set up alignments for widgets at the top of ui,
2907 * align top left, expand horizontally but not vertically
2908 */
Jens Axboe65476332012-03-13 10:37:04 +01002909 top_align = gtk_alignment_new(0, 0, 1, 0);
2910 top_vbox = gtk_vbox_new(FALSE, 0);
2911 gtk_container_add(GTK_CONTAINER(top_align), top_vbox);
2912 gtk_box_pack_start(GTK_BOX(main_vbox), top_align, FALSE, FALSE, 0);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002913
2914 probe = gtk_frame_new("Run statistics");
2915 gtk_box_pack_start(GTK_BOX(main_vbox), probe, FALSE, FALSE, 3);
2916 probe_frame = gtk_vbox_new(FALSE, 3);
2917 gtk_container_add(GTK_CONTAINER(probe), probe_frame);
Jens Axboe3e47bd22012-02-29 13:45:02 +01002918
2919 probe_box = gtk_hbox_new(FALSE, 3);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002920 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, FALSE, FALSE, 3);
Jens Axboe3863d1a2012-03-09 17:39:05 +01002921 ui->eta.jobs = new_info_entry_in_frame(probe_box, "Running");
Jens Axboeca850992012-03-05 20:04:43 +01002922 ui->eta.read_bw = new_info_entry_in_frame(probe_box, "Read BW");
2923 ui->eta.read_iops = new_info_entry_in_frame(probe_box, "IOPS");
2924 ui->eta.write_bw = new_info_entry_in_frame(probe_box, "Write BW");
2925 ui->eta.write_iops = new_info_entry_in_frame(probe_box, "IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002926
2927 /*
2928 * Only add this if we have a commit rate
2929 */
2930#if 0
2931 probe_box = gtk_hbox_new(FALSE, 3);
2932 gtk_box_pack_start(GTK_BOX(probe_frame), probe_box, TRUE, FALSE, 3);
2933
Jens Axboe3e47bd22012-02-29 13:45:02 +01002934 ui->eta.cr_bw = new_info_label_in_frame(probe_box, "Commit BW");
2935 ui->eta.cr_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
2936
Jens Axboe3e47bd22012-02-29 13:45:02 +01002937 ui->eta.cw_bw = new_info_label_in_frame(probe_box, "Commit BW");
2938 ui->eta.cw_iops = new_info_label_in_frame(probe_box, "Commit IOPS");
Jens Axboe807f9972012-03-02 10:25:24 +01002939#endif
Jens Axboe3e47bd22012-02-29 13:45:02 +01002940
Stephen M. Cameron45032dd2012-02-24 08:17:31 +01002941 /*
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002942 * Set up a drawing area and IOPS and bandwidth graphs
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002943 */
Jens Axboe2f99deb2012-03-09 14:37:29 +01002944 ui->graphs.drawing_area = gtk_drawing_area_new();
Jens Axboe2f99deb2012-03-09 14:37:29 +01002945 gtk_widget_set_size_request(GTK_WIDGET(ui->graphs.drawing_area),
Stephen M. Cameron57f9d282012-03-11 11:36:51 +01002946 DRAWING_AREA_XDIM, DRAWING_AREA_YDIM);
Jens Axboe2f99deb2012-03-09 14:37:29 +01002947 gtk_widget_modify_bg(ui->graphs.drawing_area, GTK_STATE_NORMAL, &white);
2948 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "expose_event",
2949 G_CALLBACK(on_expose_drawing_area), &ui->graphs);
2950 g_signal_connect(G_OBJECT(ui->graphs.drawing_area), "configure_event",
2951 G_CALLBACK(on_config_drawing_area), &ui->graphs);
Jens Axboe65476332012-03-13 10:37:04 +01002952 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
2953 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002954 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
Jens Axboe65476332012-03-13 10:37:04 +01002955 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
Jens Axboe2f99deb2012-03-09 14:37:29 +01002956 ui->graphs.drawing_area);
Jens Axboe65476332012-03-13 10:37:04 +01002957 gtk_box_pack_start(GTK_BOX(main_vbox), scrolled_window,
Stephen M. Camerone1645342012-02-24 08:17:32 +01002958 TRUE, TRUE, 0);
Stephen M. Cameron736f2df2012-02-24 08:17:32 +01002959
Jens Axboe2f99deb2012-03-09 14:37:29 +01002960 setup_graphs(&ui->graphs);
Jens Axboe2fd3bb02012-03-07 08:07:39 +01002961
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002962 /*
2963 * Set up alignments for widgets at the bottom of ui,
2964 * align bottom left, expand horizontally but not vertically
2965 */
Jens Axboe65476332012-03-13 10:37:04 +01002966 bottom_align = gtk_alignment_new(0, 1, 1, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002967 ui->buttonbox = gtk_hbox_new(FALSE, 0);
Jens Axboe65476332012-03-13 10:37:04 +01002968 gtk_container_add(GTK_CONTAINER(bottom_align), ui->buttonbox);
2969 gtk_box_pack_start(GTK_BOX(main_vbox), bottom_align, FALSE, FALSE, 0);
Stephen M. Cameronc36f98d2012-02-24 08:17:32 +01002970
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002971 /*
2972 * Set up thread status progress bar
2973 */
2974 ui->thread_status_pb = gtk_progress_bar_new();
2975 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ui->thread_status_pb), 0.0);
Jens Axboe8663ea62012-03-02 14:04:30 +01002976 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(ui->thread_status_pb), "No connections");
Jens Axboe3ec62ec2012-03-01 12:01:29 +01002977 gtk_container_add(GTK_CONTAINER(ui->buttonbox), ui->thread_status_pb);
2978
Jens Axboe2f99deb2012-03-09 14:37:29 +01002979 return main_vbox;
2980}
2981
2982static gboolean notebook_switch_page(GtkNotebook *notebook, GtkWidget *widget,
2983 guint page, gpointer data)
2984
2985{
Jens Axboe02421e62012-03-12 12:05:50 +01002986 struct gui *ui = (struct gui *) data;
Jens Axboe85dd01e2012-03-12 14:33:16 +01002987 struct gui_entry *ge;
Jens Axboe02421e62012-03-12 12:05:50 +01002988
Jens Axboe85dd01e2012-03-12 14:33:16 +01002989 if (!page) {
2990 set_job_menu_visible(ui, 0);
Jens Axboe781ccba2012-03-15 09:44:42 +01002991 set_view_results_visible(ui, 0);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002992 return TRUE;
2993 }
2994
2995 set_job_menu_visible(ui, 1);
Jens Axboe38634cb2012-03-13 12:26:41 +01002996 ge = get_ge_from_page(page, NULL);
Jens Axboe85dd01e2012-03-12 14:33:16 +01002997 if (ge)
2998 update_button_states(ui, ge);
2999
Jens Axboe2f99deb2012-03-09 14:37:29 +01003000 return TRUE;
3001}
3002
Jens Axboe38634cb2012-03-13 12:26:41 +01003003static gint compare_recent_items(GtkRecentInfo *a, GtkRecentInfo *b)
3004{
3005 time_t time_a = gtk_recent_info_get_visited(a);
3006 time_t time_b = gtk_recent_info_get_visited(b);
3007
3008 return time_b - time_a;
3009}
3010
3011static void add_recent_file_items(struct gui *ui)
3012{
3013 const gchar *gfio = g_get_application_name();
3014 GList *items, *item;
3015 int i = 0;
3016
3017 if (ui->recent_ui_id) {
3018 gtk_ui_manager_remove_ui(ui->uimanager, ui->recent_ui_id);
3019 gtk_ui_manager_ensure_update(ui->uimanager);
3020 }
3021 ui->recent_ui_id = gtk_ui_manager_new_merge_id(ui->uimanager);
3022
3023 if (ui->actiongroup) {
3024 gtk_ui_manager_remove_action_group(ui->uimanager, ui->actiongroup);
3025 g_object_unref(ui->actiongroup);
3026 }
3027 ui->actiongroup = gtk_action_group_new("RecentFileActions");
3028
3029 gtk_ui_manager_insert_action_group(ui->uimanager, ui->actiongroup, -1);
3030
3031 items = gtk_recent_manager_get_items(ui->recentmanager);
3032 items = g_list_sort(items, (GCompareFunc) compare_recent_items);
3033
3034 for (item = items; item && item->data; item = g_list_next(item)) {
3035 GtkRecentInfo *info = (GtkRecentInfo *) item->data;
3036 gchar *action_name;
3037 const gchar *label;
3038 GtkAction *action;
3039
3040 if (!gtk_recent_info_has_application(info, gfio))
3041 continue;
3042
3043 /*
3044 * We only support local files for now
3045 */
3046 if (!gtk_recent_info_is_local(info) || !gtk_recent_info_exists(info))
3047 continue;
3048
3049 action_name = g_strdup_printf("RecentFile%u", i++);
3050 label = gtk_recent_info_get_display_name(info);
3051
3052 action = g_object_new(GTK_TYPE_ACTION,
3053 "name", action_name,
3054 "label", label, NULL);
3055
3056 g_object_set_data_full(G_OBJECT(action), "gtk-recent-info",
3057 gtk_recent_info_ref(info),
3058 (GDestroyNotify) gtk_recent_info_unref);
3059
3060
3061 g_signal_connect(action, "activate", G_CALLBACK(recent_open), ui);
3062
3063 gtk_action_group_add_action(ui->actiongroup, action);
3064 g_object_unref(action);
3065
3066 gtk_ui_manager_add_ui(ui->uimanager, ui->recent_ui_id,
3067 "/MainMenu/FileMenu/FileRecentFiles",
3068 label, action_name,
3069 GTK_UI_MANAGER_MENUITEM, FALSE);
3070
3071 g_free(action_name);
3072
3073 if (i == 8)
3074 break;
3075 }
3076
3077 g_list_foreach(items, (GFunc) gtk_recent_info_unref, NULL);
3078 g_list_free(items);
3079}
3080
Jens Axboea6790902012-03-13 15:16:11 +01003081static void drag_and_drop_received(GtkWidget *widget, GdkDragContext *ctx,
3082 gint x, gint y, GtkSelectionData *data,
3083 guint info, guint time)
3084{
3085 struct gui *ui = &main_ui;
3086 gchar **uris;
3087 GtkWidget *source;
3088 int i;
3089
3090 source = gtk_drag_get_source_widget(ctx);
3091 if (source && widget == gtk_widget_get_toplevel(source)) {
3092 gtk_drag_finish(ctx, FALSE, FALSE, time);
3093 return;
3094 }
3095
3096 uris = gtk_selection_data_get_uris(data);
3097 if (!uris) {
3098 gtk_drag_finish(ctx, FALSE, FALSE, time);
3099 return;
3100 }
3101
3102 i = 0;
3103 while (uris[i]) {
3104 if (do_file_open_with_tab(ui, uris[i]))
3105 break;
3106 i++;
3107 }
3108
3109 gtk_drag_finish(ctx, TRUE, FALSE, time);
3110 g_strfreev(uris);
3111}
3112
Jens Axboe2f99deb2012-03-09 14:37:29 +01003113static void init_ui(int *argc, char **argv[], struct gui *ui)
3114{
3115 GtkSettings *settings;
Jens Axboe02421e62012-03-12 12:05:50 +01003116 GtkWidget *vbox;
Jens Axboe2f99deb2012-03-09 14:37:29 +01003117
3118 /* Magical g*thread incantation, you just need this thread stuff.
3119 * Without it, the update that happens in gfio_update_thread_status
3120 * doesn't really happen in a timely fashion, you need expose events
3121 */
3122 if (!g_thread_supported())
3123 g_thread_init(NULL);
3124 gdk_threads_init();
3125
3126 gtk_init(argc, argv);
3127 settings = gtk_settings_get_default();
3128 gtk_settings_set_long_property(settings, "gtk_tooltip_timeout", 10, "gfio setting");
3129 g_type_init();
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003130 gdk_color_parse("white", &white);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003131
3132 ui->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
Stephen M. Cameron814479d2012-03-15 07:58:14 +01003133 gtk_window_set_title(GTK_WINDOW(ui->window), "fio");
Jens Axboe2f99deb2012-03-09 14:37:29 +01003134 gtk_window_set_default_size(GTK_WINDOW(ui->window), 1024, 768);
3135
3136 g_signal_connect(ui->window, "delete-event", G_CALLBACK(quit_clicked), NULL);
3137 g_signal_connect(ui->window, "destroy", G_CALLBACK(quit_clicked), NULL);
3138
3139 ui->vbox = gtk_vbox_new(FALSE, 0);
3140 gtk_container_add(GTK_CONTAINER(ui->window), ui->vbox);
3141
Jens Axboe02421e62012-03-12 12:05:50 +01003142 ui->uimanager = gtk_ui_manager_new();
3143 ui->menu = get_menubar_menu(ui->window, ui->uimanager, ui);
3144 gfio_ui_setup(settings, ui->menu, ui->vbox, ui->uimanager);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003145
Jens Axboe38634cb2012-03-13 12:26:41 +01003146 ui->recentmanager = gtk_recent_manager_get_default();
3147 add_recent_file_items(ui);
3148
Jens Axboe2f99deb2012-03-09 14:37:29 +01003149 ui->notebook = gtk_notebook_new();
3150 g_signal_connect(ui->notebook, "switch-page", G_CALLBACK(notebook_switch_page), ui);
Jens Axboeb870c312012-03-09 17:22:01 +01003151 gtk_notebook_set_scrollable(GTK_NOTEBOOK(ui->notebook), 1);
Jens Axboe0aa928c2012-03-09 17:24:07 +01003152 gtk_notebook_popup_enable(GTK_NOTEBOOK(ui->notebook));
Jens Axboe2f99deb2012-03-09 14:37:29 +01003153 gtk_container_add(GTK_CONTAINER(ui->vbox), ui->notebook);
3154
3155 vbox = new_main_page(ui);
Jens Axboea6790902012-03-13 15:16:11 +01003156 gtk_drag_dest_set(GTK_WIDGET(ui->window), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY);
3157 gtk_drag_dest_add_uri_targets(GTK_WIDGET(ui->window));
3158 g_signal_connect(ui->window, "drag-data-received", G_CALLBACK(drag_and_drop_received), ui);
Jens Axboe2f99deb2012-03-09 14:37:29 +01003159
3160 gtk_notebook_append_page(GTK_NOTEBOOK(ui->notebook), vbox, gtk_label_new("Main"));
3161
Jens Axboe9b260bd2012-03-06 11:02:52 +01003162 gfio_ui_setup_log(ui);
Jens Axboe3ec62ec2012-03-01 12:01:29 +01003163
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003164 gtk_widget_show_all(ui->window);
3165}
3166
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003167int main(int argc, char *argv[], char *envp[])
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003168{
Stephen M. Cameron8232e282012-02-24 08:17:31 +01003169 if (initialize_fio(envp))
3170 return 1;
Jens Axboe0420ba62012-02-29 11:16:52 +01003171 if (fio_init_options())
3172 return 1;
Stephen M. Camerona1820202012-02-24 08:17:31 +01003173
Jens Axboe2f99deb2012-03-09 14:37:29 +01003174 memset(&main_ui, 0, sizeof(main_ui));
3175 INIT_FLIST_HEAD(&main_ui.list);
3176
3177 init_ui(&argc, &argv, &main_ui);
Stephen M. Cameron5b7573a2012-02-24 08:17:31 +01003178
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003179 gdk_threads_enter();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003180 gtk_main();
Stephen M. Cameron2839f0c2012-02-24 08:17:31 +01003181 gdk_threads_leave();
Stephen M. Cameronff1f3282012-02-24 08:17:30 +01003182 return 0;
3183}